mirror of
https://github.com/chartdb/chartdb.git
synced 2025-11-06 06:53:34 +00:00
Compare commits
94 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
084a1d505c | ||
|
|
91e713c30a | ||
|
|
acf6d4b365 | ||
|
|
9e8979d062 | ||
|
|
9ed27cf30c | ||
|
|
2c4b344efb | ||
|
|
ccb29e0a57 | ||
|
|
7d811de097 | ||
|
|
62dec48572 | ||
|
|
49328d8fbd | ||
|
|
459698b5d0 | ||
|
|
7ad0e7712d | ||
|
|
34475add32 | ||
|
|
38fedcec0c | ||
|
|
498655e7b7 | ||
|
|
bcd8aa9378 | ||
|
|
b15bc945ac | ||
|
|
c3c646bf7c | ||
|
|
57b3b8777f | ||
|
|
bb033091b1 | ||
|
|
c9ac8929c5 | ||
|
|
c567c0a5f3 | ||
|
|
2dc1a6fc75 | ||
|
|
98f6edd5c8 | ||
|
|
47a7a73a13 | ||
|
|
d71b46e8b5 | ||
|
|
e4c4a3b354 | ||
|
|
1b8d51b73c | ||
|
|
93d72a896b | ||
|
|
9991077978 | ||
|
|
bc82f9d6a8 | ||
|
|
26dc299cd2 | ||
|
|
d6ba4a4074 | ||
|
|
d09379e8be | ||
|
|
bdc41c0b74 | ||
|
|
d3dbf41894 | ||
|
|
e6783a89cc | ||
|
|
af3638da7a | ||
|
|
8954d893bb | ||
|
|
1a6688e85e | ||
|
|
5e81c1848a | ||
|
|
2bd9ca25b2 | ||
|
|
b016a70691 | ||
|
|
a0fb1ed08b | ||
|
|
ffddcdcc98 | ||
|
|
fe9ef275b8 | ||
|
|
df89f0b6b9 | ||
|
|
534d2858af | ||
|
|
2a64deebb8 | ||
|
|
e5e1d59327 | ||
|
|
aa290615ca | ||
|
|
ec6e46fe81 | ||
|
|
ac128d67de | ||
|
|
07937a2f51 | ||
|
|
d8e0bc7db8 | ||
|
|
1ce265781b | ||
|
|
60c5675cbf | ||
|
|
66b086378c | ||
|
|
abd2a6ccbe | ||
|
|
459c5f1ce3 | ||
|
|
44be48ff3a | ||
|
|
ad8e34483f | ||
|
|
215d57979d | ||
|
|
ec3719ebce | ||
|
|
0a5874a69b | ||
|
|
7e0fdd1595 | ||
|
|
2531a7023f | ||
|
|
73daf0df21 | ||
|
|
c77c983989 | ||
|
|
0aaa451479 | ||
|
|
b697e26170 | ||
|
|
04d91c67b1 | ||
|
|
d0dee84970 | ||
|
|
b4ccfcdcde | ||
|
|
1759b0b9f2 | ||
|
|
ab4845c772 | ||
|
|
0545b41140 | ||
|
|
4520f8b1f7 | ||
|
|
712bdf5b95 | ||
|
|
d7c9536272 | ||
|
|
815a52f192 | ||
|
|
f1a4298362 | ||
|
|
b8f2141bd2 | ||
|
|
eaebe34768 | ||
|
|
0d623a86b1 | ||
|
|
19fd94c6bd | ||
|
|
0da3caeeac | ||
|
|
cb2ba66233 | ||
|
|
8a2267281b | ||
|
|
41ba251377 | ||
|
|
e9c5442d9d | ||
|
|
4f1d3295c0 | ||
|
|
5936500ca0 | ||
|
|
43fc1d7fc2 |
2
.github/workflows/cla.yaml
vendored
2
.github/workflows/cla.yaml
vendored
@@ -7,7 +7,7 @@ on:
|
|||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
actions: write
|
actions: write
|
||||||
contents: write # this can be 'read' if the signatures are in remote repository
|
contents: read
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
statuses: write
|
statuses: write
|
||||||
|
|
||||||
|
|||||||
117
CHANGELOG.md
117
CHANGELOG.md
@@ -1,5 +1,122 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [1.17.0](https://github.com/chartdb/chartdb/compare/v1.16.0...v1.17.0) (2025-10-27)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* create relationships on canvas modal ([#946](https://github.com/chartdb/chartdb/issues/946)) ([34475ad](https://github.com/chartdb/chartdb/commit/34475add32f11323589ef092ccf2a8e9152ff272))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add auto-increment field detection in smart-query import ([#935](https://github.com/chartdb/chartdb/issues/935)) ([57b3b87](https://github.com/chartdb/chartdb/commit/57b3b8777fd0a445abf0ba6603faab612d469d5c))
|
||||||
|
* add open table in editor from canvas edit ([#952](https://github.com/chartdb/chartdb/issues/952)) ([7d811de](https://github.com/chartdb/chartdb/commit/7d811de097eb11e51012772fa6bf586fd0b16c62))
|
||||||
|
* add rels export dbml ([#937](https://github.com/chartdb/chartdb/issues/937)) ([c3c646b](https://github.com/chartdb/chartdb/commit/c3c646bf7cbb1328f4b2eb85c9a7e929f0fcd3b9))
|
||||||
|
* add support for arrays ([#949](https://github.com/chartdb/chartdb/issues/949)) ([49328d8](https://github.com/chartdb/chartdb/commit/49328d8fbd7786f6c0c04cd5605d43a24cbf10ea))
|
||||||
|
* add support for parsing default values in DBML ([#948](https://github.com/chartdb/chartdb/issues/948)) ([459698b](https://github.com/chartdb/chartdb/commit/459698b5d0a1ff23a3719c2e55e4ab2e2384c4fe))
|
||||||
|
* add timestampz and int as datatypes to postgres ([#940](https://github.com/chartdb/chartdb/issues/940)) ([b15bc94](https://github.com/chartdb/chartdb/commit/b15bc945acb96d7cb3832b3b1b607dfcaef9e5ca))
|
||||||
|
* auto-enter edit mode when creating new tables from canvas ([#943](https://github.com/chartdb/chartdb/issues/943)) ([bcd8aa9](https://github.com/chartdb/chartdb/commit/bcd8aa9378aa563f40a2b6802cc503be4c882356))
|
||||||
|
* dbml diff fields types preview ([#934](https://github.com/chartdb/chartdb/issues/934)) ([bb03309](https://github.com/chartdb/chartdb/commit/bb033091b1f64b888822be1423a80f16f5314f6b))
|
||||||
|
* exit table edit on area click ([#945](https://github.com/chartdb/chartdb/issues/945)) ([38fedce](https://github.com/chartdb/chartdb/commit/38fedcec0c10ea2b3f0b7fc92ca1f5ac9e540389))
|
||||||
|
* import array fields ([#961](https://github.com/chartdb/chartdb/issues/961)) ([91e713c](https://github.com/chartdb/chartdb/commit/91e713c30a44f1ba7a767ca7816079610136fcb8))
|
||||||
|
* manipulate schema directly from the canvas ([#947](https://github.com/chartdb/chartdb/issues/947)) ([7ad0e77](https://github.com/chartdb/chartdb/commit/7ad0e7712de975a23b2a337dc0a4a7fb4b122bd1))
|
||||||
|
* preserve multi-word types in DBML export/import ([#956](https://github.com/chartdb/chartdb/issues/956)) ([9ed27cf](https://github.com/chartdb/chartdb/commit/9ed27cf30cca1312713e80e525138f0c27154936))
|
||||||
|
* prevent text input glitch when editing table field names ([#944](https://github.com/chartdb/chartdb/issues/944)) ([498655e](https://github.com/chartdb/chartdb/commit/498655e7b77e57eaf641ba86263ce1ef60b93e16))
|
||||||
|
* resolve canvas filter tree state issues ([#953](https://github.com/chartdb/chartdb/issues/953)) ([ccb29e0](https://github.com/chartdb/chartdb/commit/ccb29e0a574dfa4cfdf0ebf242a4c4aaa48cc37b))
|
||||||
|
* resolve dbml increment & nullable attributes issue ([#954](https://github.com/chartdb/chartdb/issues/954)) ([2c4b344](https://github.com/chartdb/chartdb/commit/2c4b344efb24041e7f607fc6124e109b69aaa457))
|
||||||
|
* show SQL Script option conditionally for databases without DDL support ([#960](https://github.com/chartdb/chartdb/issues/960)) ([acf6d4b](https://github.com/chartdb/chartdb/commit/acf6d4b3654d8868b8a8ebf717c608d9749b71da))
|
||||||
|
* use flag for custom types ([#951](https://github.com/chartdb/chartdb/issues/951)) ([62dec48](https://github.com/chartdb/chartdb/commit/62dec4857211b705a8039691da1772263ea986fe))
|
||||||
|
|
||||||
|
## [1.16.0](https://github.com/chartdb/chartdb/compare/v1.15.1...v1.16.0) (2025-09-24)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add area context menu and UI improvements ([#918](https://github.com/chartdb/chartdb/issues/918)) ([d09379e](https://github.com/chartdb/chartdb/commit/d09379e8be0fa3c83ca77ff62ae815fe4db9869b))
|
||||||
|
* add quick table mode on canvas ([#915](https://github.com/chartdb/chartdb/issues/915)) ([8954d89](https://github.com/chartdb/chartdb/commit/8954d893bbfee45bb311380115fb14ebbf3a3133))
|
||||||
|
* add zoom navigation buttons to canvas filter for tables and areas ([#903](https://github.com/chartdb/chartdb/issues/903)) ([a0fb1ed](https://github.com/chartdb/chartdb/commit/a0fb1ed08ba18b66354fa3498d610097a83d4afc))
|
||||||
|
* **import-db:** add DBML syntax to import database dialog ([#768](https://github.com/chartdb/chartdb/issues/768)) ([af3638d](https://github.com/chartdb/chartdb/commit/af3638da7a9b70f281ceaddbc2f712a713d90cda))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add areas width and height + table width to diff check ([#931](https://github.com/chartdb/chartdb/issues/931)) ([98f6edd](https://github.com/chartdb/chartdb/commit/98f6edd5c8a8e9130e892b2d841744e0cf63a7bf))
|
||||||
|
* add diff x,y ([#928](https://github.com/chartdb/chartdb/issues/928)) ([e4c4a3b](https://github.com/chartdb/chartdb/commit/e4c4a3b35484d9ece955a5aec577603dde73d634))
|
||||||
|
* add support for ALTER TABLE ADD COLUMN in PostgreSQL importer ([#892](https://github.com/chartdb/chartdb/issues/892)) ([ec6e46f](https://github.com/chartdb/chartdb/commit/ec6e46fe81ea1806c179c50a4c5779d8596008aa))
|
||||||
|
* add tests for diff ([#930](https://github.com/chartdb/chartdb/issues/930)) ([47a7a73](https://github.com/chartdb/chartdb/commit/47a7a73a137b87dfa6e67aff5f939cf64ccf4601))
|
||||||
|
* dbml edit mode glitch ([#925](https://github.com/chartdb/chartdb/issues/925)) ([93d72a8](https://github.com/chartdb/chartdb/commit/93d72a896bab9aa79d8ea2f876126887e432214c))
|
||||||
|
* dbml export default time bug ([#922](https://github.com/chartdb/chartdb/issues/922)) ([bc82f9d](https://github.com/chartdb/chartdb/commit/bc82f9d6a8fe4de2f7e0fc465e0a20c5dbf8f41d))
|
||||||
|
* dbml export renaming fields bug ([#921](https://github.com/chartdb/chartdb/issues/921)) ([26dc299](https://github.com/chartdb/chartdb/commit/26dc299cd28e9890d191c13f84a15ac38ae48b11))
|
||||||
|
* **dbml:** export array fields without quotes ([#911](https://github.com/chartdb/chartdb/issues/911)) ([5e81c18](https://github.com/chartdb/chartdb/commit/5e81c1848aaa911990e1e881d62525f5254d6d34))
|
||||||
|
* diff logic ([#927](https://github.com/chartdb/chartdb/issues/927)) ([1b8d51b](https://github.com/chartdb/chartdb/commit/1b8d51b73c4ed4b7c5929adcb17a44927c7defca))
|
||||||
|
* export dbml issues after upgrade version ([#883](https://github.com/chartdb/chartdb/issues/883)) ([07937a2](https://github.com/chartdb/chartdb/commit/07937a2f51708b1c10b45c2bd1f9a9acf5c3f708))
|
||||||
|
* export sql + import metadata lib ([#902](https://github.com/chartdb/chartdb/issues/902)) ([ffddcdc](https://github.com/chartdb/chartdb/commit/ffddcdcc987bacb0e0d7e8dea27d08d3a8c5a8c8))
|
||||||
|
* handle bidirectional relationships in DBML export ([#924](https://github.com/chartdb/chartdb/issues/924)) ([9991077](https://github.com/chartdb/chartdb/commit/99910779789a9c6ef113d06bc3de31e35b9b04d1))
|
||||||
|
* import dbml set pk field unique ([#920](https://github.com/chartdb/chartdb/issues/920)) ([d6ba4a4](https://github.com/chartdb/chartdb/commit/d6ba4a40749d85d2703f120600df4345dab3c561))
|
||||||
|
* improve SQL default value parsing for PostgreSQL, MySQL, and SQL Server with proper type handling and casting support ([#900](https://github.com/chartdb/chartdb/issues/900)) ([fe9ef27](https://github.com/chartdb/chartdb/commit/fe9ef275b8619dcfd7e57541a62a6237a16d29a8))
|
||||||
|
* move area utils ([#932](https://github.com/chartdb/chartdb/issues/932)) ([2dc1a6f](https://github.com/chartdb/chartdb/commit/2dc1a6fc7519e0a455b0e1306601195deb156c96))
|
||||||
|
* move auto arrange to toolbar ([#904](https://github.com/chartdb/chartdb/issues/904)) ([b016a70](https://github.com/chartdb/chartdb/commit/b016a70691bc22af5720b4de683e8c9353994fcc))
|
||||||
|
* remove general db creation ([#901](https://github.com/chartdb/chartdb/issues/901)) ([df89f0b](https://github.com/chartdb/chartdb/commit/df89f0b6b9ba3fcc8b05bae4f60c0dc4ad1d2215))
|
||||||
|
* remove many to many rel option ([#933](https://github.com/chartdb/chartdb/issues/933)) ([c567c0a](https://github.com/chartdb/chartdb/commit/c567c0a5f39157b2c430e92192b6750304d7a834))
|
||||||
|
* reset increment and default when change field ([#896](https://github.com/chartdb/chartdb/issues/896)) ([e5e1d59](https://github.com/chartdb/chartdb/commit/e5e1d5932762422ea63acfd6cf9fe4f03aa822f7))
|
||||||
|
* **sql-import:** handle SQL Server DDL with multiple tables, inline foreign keys, and case-insensitive field matching ([#897](https://github.com/chartdb/chartdb/issues/897)) ([2a64dee](https://github.com/chartdb/chartdb/commit/2a64deebb87a11ee3892024c3273d682bb86f7ef))
|
||||||
|
* **sql-import:** support ALTER TABLE ALTER COLUMN TYPE in PostgreSQL importer ([#895](https://github.com/chartdb/chartdb/issues/895)) ([aa29061](https://github.com/chartdb/chartdb/commit/aa290615caf806d7d0374c848d50b4636fde7e96))
|
||||||
|
* **sqlite:** improve parser to handle tables without column types and fix column detection ([#914](https://github.com/chartdb/chartdb/issues/914)) ([d3dbf41](https://github.com/chartdb/chartdb/commit/d3dbf41894d74f0ffce9afe3bd810f065aa53017))
|
||||||
|
* trigger edit table on canvas from context menu ([#919](https://github.com/chartdb/chartdb/issues/919)) ([bdc41c0](https://github.com/chartdb/chartdb/commit/bdc41c0b74d9d9918e7b6cd2152fa07c0c58ce60))
|
||||||
|
* update deps vulns ([#909](https://github.com/chartdb/chartdb/issues/909)) ([2bd9ca2](https://github.com/chartdb/chartdb/commit/2bd9ca25b2c7b1f053ff4fdc8c5cfc1b0e65901d))
|
||||||
|
* upgrade dbml lib ([#880](https://github.com/chartdb/chartdb/issues/880)) ([d8e0bc7](https://github.com/chartdb/chartdb/commit/d8e0bc7db8881971ddaea7177bcebee13cc865f6))
|
||||||
|
|
||||||
|
## [1.15.1](https://github.com/chartdb/chartdb/compare/v1.15.0...v1.15.1) (2025-08-27)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add actions menu to diagram list + add duplicate diagram ([#876](https://github.com/chartdb/chartdb/issues/876)) ([abd2a6c](https://github.com/chartdb/chartdb/commit/abd2a6ccbe1aa63db44ec28b3eff525cc5d3f8b0))
|
||||||
|
* **custom-types:** Make schema optional ([#866](https://github.com/chartdb/chartdb/issues/866)) ([60c5675](https://github.com/chartdb/chartdb/commit/60c5675cbfe205859d2d0c9848d8345a0a854671))
|
||||||
|
* handle quoted identifiers with special characters in SQL import/export and DBML generation ([#877](https://github.com/chartdb/chartdb/issues/877)) ([66b0863](https://github.com/chartdb/chartdb/commit/66b086378cd63347acab5fc7f13db7db4feaa872))
|
||||||
|
|
||||||
|
## [1.15.0](https://github.com/chartdb/chartdb/compare/v1.14.0...v1.15.0) (2025-08-26)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add auto increment support for fields with database-specific export ([#851](https://github.com/chartdb/chartdb/issues/851)) ([c77c983](https://github.com/chartdb/chartdb/commit/c77c983989ae38a6b1139dd9015f4f3178d4e103))
|
||||||
|
* **filter:** filter tables by areas ([#836](https://github.com/chartdb/chartdb/issues/836)) ([e9c5442](https://github.com/chartdb/chartdb/commit/e9c5442d9df2beadad78187da3363bb6406636c4))
|
||||||
|
* include foreign keys inline in SQLite CREATE TABLE statements ([#833](https://github.com/chartdb/chartdb/issues/833)) ([43fc1d7](https://github.com/chartdb/chartdb/commit/43fc1d7fc26876b22c61405f6c3df89fc66b7992))
|
||||||
|
* **postgres:** add support hash index types ([#812](https://github.com/chartdb/chartdb/issues/812)) ([0d623a8](https://github.com/chartdb/chartdb/commit/0d623a86b1cb7cbd223e10ad23d09fc0e106c006))
|
||||||
|
* support create views ([#868](https://github.com/chartdb/chartdb/issues/868)) ([0a5874a](https://github.com/chartdb/chartdb/commit/0a5874a69b6323145430c1fb4e3482ac7da4916c))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* area filter logic ([#861](https://github.com/chartdb/chartdb/issues/861)) ([73daf0d](https://github.com/chartdb/chartdb/commit/73daf0df2142a29c2eeebe60b43198bcca869026))
|
||||||
|
* **area filter:** fix dragging tables over filtered areas ([#842](https://github.com/chartdb/chartdb/issues/842)) ([19fd94c](https://github.com/chartdb/chartdb/commit/19fd94c6bde3a9ec749cd1ccacbedb6abc96d037))
|
||||||
|
* **canvas:** delete table + area together bug ([#859](https://github.com/chartdb/chartdb/issues/859)) ([b697e26](https://github.com/chartdb/chartdb/commit/b697e26170da95dcb427ff6907b6f663c98ba59f))
|
||||||
|
* **cla:** Harden action ([#867](https://github.com/chartdb/chartdb/issues/867)) ([ad8e344](https://github.com/chartdb/chartdb/commit/ad8e34483fdf4226de76c9e7768bc2ba9bf154de))
|
||||||
|
* DBML export error with multi-line table comments for SQL Server ([#852](https://github.com/chartdb/chartdb/issues/852)) ([0545b41](https://github.com/chartdb/chartdb/commit/0545b411407b2449220d10981a04c3e368a90ca3))
|
||||||
|
* filter to default schema on load new diagram ([#849](https://github.com/chartdb/chartdb/issues/849)) ([712bdf5](https://github.com/chartdb/chartdb/commit/712bdf5b958919d940c4f2a1c3b7c7e969990f02))
|
||||||
|
* **filter:** filter toggle issues with no schemas dbs ([#856](https://github.com/chartdb/chartdb/issues/856)) ([d0dee84](https://github.com/chartdb/chartdb/commit/d0dee849702161d979b4f589a7e6579fbaade22d))
|
||||||
|
* **filters:** refactor diagram filters - remove schema filter ([#832](https://github.com/chartdb/chartdb/issues/832)) ([4f1d329](https://github.com/chartdb/chartdb/commit/4f1d3295c09782ab46d82ce21b662032aa094f22))
|
||||||
|
* for sqlite import - add more types & include type parameters ([#834](https://github.com/chartdb/chartdb/issues/834)) ([5936500](https://github.com/chartdb/chartdb/commit/5936500ca00a57b3f161616264c26152a13c36d2))
|
||||||
|
* improve creating view to table dependency ([#874](https://github.com/chartdb/chartdb/issues/874)) ([44be48f](https://github.com/chartdb/chartdb/commit/44be48ff3ad1361279331c17364090b13af471a1))
|
||||||
|
* initially show filter when filter active ([#853](https://github.com/chartdb/chartdb/issues/853)) ([ab4845c](https://github.com/chartdb/chartdb/commit/ab4845c7728e6e0b2d852f8005921fd90630eef9))
|
||||||
|
* **menu:** clear file menu ([#843](https://github.com/chartdb/chartdb/issues/843)) ([eaebe34](https://github.com/chartdb/chartdb/commit/eaebe3476824af779214a354b3e991923a22f195))
|
||||||
|
* merge relationship & dependency sections to ref section ([#870](https://github.com/chartdb/chartdb/issues/870)) ([ec3719e](https://github.com/chartdb/chartdb/commit/ec3719ebce4664b2aa6e3322fb3337e72bc21015))
|
||||||
|
* move dbml into sections menu ([#862](https://github.com/chartdb/chartdb/issues/862)) ([2531a70](https://github.com/chartdb/chartdb/commit/2531a7023f36ef29e67c0da6bca4fd0346b18a51))
|
||||||
|
* open filter by default ([#863](https://github.com/chartdb/chartdb/issues/863)) ([7e0fdd1](https://github.com/chartdb/chartdb/commit/7e0fdd1595bffe29e769d29602d04f42edfe417e))
|
||||||
|
* preserve composite primary key constraint names across import/export workflows ([#869](https://github.com/chartdb/chartdb/issues/869)) ([215d579](https://github.com/chartdb/chartdb/commit/215d57979df2e91fa61988acff590daad2f4e771))
|
||||||
|
* prevent false change detection in DBML editor by stripping public schema on import ([#858](https://github.com/chartdb/chartdb/issues/858)) ([0aaa451](https://github.com/chartdb/chartdb/commit/0aaa451479911d047e4cc83f063afa68a122ba9b))
|
||||||
|
* remove unnecessary space ([#845](https://github.com/chartdb/chartdb/issues/845)) ([f1a4298](https://github.com/chartdb/chartdb/commit/f1a429836221aacdda73b91665bf33ffb011164c))
|
||||||
|
* reorder with areas ([#846](https://github.com/chartdb/chartdb/issues/846)) ([d7c9536](https://github.com/chartdb/chartdb/commit/d7c9536272cf1d42104b7064ea448d128d091a20))
|
||||||
|
* **select-box:** fix select box issue in dialog ([#840](https://github.com/chartdb/chartdb/issues/840)) ([cb2ba66](https://github.com/chartdb/chartdb/commit/cb2ba66233c8c04e2d963cf2d210499d8512a268))
|
||||||
|
* set default filter only if has more than 1 schemas ([#855](https://github.com/chartdb/chartdb/issues/855)) ([b4ccfcd](https://github.com/chartdb/chartdb/commit/b4ccfcdcde2f3565b0d3bbc46fa1715feb6cd925))
|
||||||
|
* show default schema first ([#854](https://github.com/chartdb/chartdb/issues/854)) ([1759b0b](https://github.com/chartdb/chartdb/commit/1759b0b9f271ed25f7c71f26c344e3f1d97bc5fb))
|
||||||
|
* **sidebar:** add titles to sidebar ([#844](https://github.com/chartdb/chartdb/issues/844)) ([b8f2141](https://github.com/chartdb/chartdb/commit/b8f2141bd2e67272030896fb4009a7925f9f09e4))
|
||||||
|
* **sql-import:** fix SQL Server foreign key parsing for tables without schema prefix ([#857](https://github.com/chartdb/chartdb/issues/857)) ([04d91c6](https://github.com/chartdb/chartdb/commit/04d91c67b1075e94948f75186878e633df7abbca))
|
||||||
|
* **table colors:** switch to default table color ([#841](https://github.com/chartdb/chartdb/issues/841)) ([0da3cae](https://github.com/chartdb/chartdb/commit/0da3caeeac37926dd22f38d98423611f39c0412a))
|
||||||
|
* update filter on adding table ([#838](https://github.com/chartdb/chartdb/issues/838)) ([41ba251](https://github.com/chartdb/chartdb/commit/41ba25137789dda25266178cd7c96ecbb37e62a4))
|
||||||
|
|
||||||
## [1.14.0](https://github.com/chartdb/chartdb/compare/v1.13.2...v1.14.0) (2025-08-04)
|
## [1.14.0](https://github.com/chartdb/chartdb/compare/v1.13.2...v1.14.0) (2025-08-04)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
14
index.html
14
index.html
@@ -4,8 +4,9 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.ico" />
|
<link rel="icon" type="image/svg+xml" href="/favicon.ico" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta name="robots" content="max-image-preview:large" />
|
<meta name="robots" content="noindex, max-image-preview:large" />
|
||||||
<title>ChartDB - Create & Visualize Database Schema Diagrams</title>
|
<title>ChartDB - Create & Visualize Database Schema Diagrams</title>
|
||||||
|
<link rel="canonical" href="https://chartdb.io" />
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
<link
|
<link
|
||||||
@@ -15,14 +16,19 @@
|
|||||||
<script src="/config.js"></script>
|
<script src="/config.js"></script>
|
||||||
<script>
|
<script>
|
||||||
// Load analytics only if not disabled
|
// Load analytics only if not disabled
|
||||||
(function() {
|
(function () {
|
||||||
const disableAnalytics = (window.env && window.env.DISABLE_ANALYTICS === 'true') ||
|
const disableAnalytics =
|
||||||
(typeof process !== 'undefined' && process.env && process.env.VITE_DISABLE_ANALYTICS === 'true');
|
(window.env && window.env.DISABLE_ANALYTICS === 'true') ||
|
||||||
|
(typeof process !== 'undefined' &&
|
||||||
|
process.env &&
|
||||||
|
process.env.VITE_DISABLE_ANALYTICS === 'true');
|
||||||
|
|
||||||
if (!disableAnalytics) {
|
if (!disableAnalytics) {
|
||||||
const script = document.createElement('script');
|
const script = document.createElement('script');
|
||||||
script.src = 'https://cdn.usefathom.com/script.js';
|
script.src = 'https://cdn.usefathom.com/script.js';
|
||||||
script.setAttribute('data-site', 'PRHIVBNN');
|
script.setAttribute('data-site', 'PRHIVBNN');
|
||||||
|
script.setAttribute('data-canonical', 'false');
|
||||||
|
script.setAttribute('data-spa', 'auto');
|
||||||
script.defer = true;
|
script.defer = true;
|
||||||
document.head.appendChild(script);
|
document.head.appendChild(script);
|
||||||
}
|
}
|
||||||
|
|||||||
869
package-lock.json
generated
869
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
12
package.json
12
package.json
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "chartdb",
|
"name": "chartdb",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.14.0",
|
"version": "1.17.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ai-sdk/openai": "^0.0.51",
|
"@ai-sdk/openai": "^0.0.51",
|
||||||
"@dbml/core": "^3.9.5",
|
"@dbml/core": "^3.13.9",
|
||||||
"@dnd-kit/sortable": "^8.0.0",
|
"@dnd-kit/sortable": "^8.0.0",
|
||||||
"@monaco-editor/react": "^4.6.0",
|
"@monaco-editor/react": "^4.6.0",
|
||||||
"@radix-ui/react-accordion": "^1.2.0",
|
"@radix-ui/react-accordion": "^1.2.0",
|
||||||
@@ -26,22 +26,22 @@
|
|||||||
"@radix-ui/react-checkbox": "^1.1.1",
|
"@radix-ui/react-checkbox": "^1.1.1",
|
||||||
"@radix-ui/react-collapsible": "^1.1.0",
|
"@radix-ui/react-collapsible": "^1.1.0",
|
||||||
"@radix-ui/react-context-menu": "^2.2.1",
|
"@radix-ui/react-context-menu": "^2.2.1",
|
||||||
"@radix-ui/react-dialog": "^1.1.6",
|
"@radix-ui/react-dialog": "^1.1.14",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.1.1",
|
"@radix-ui/react-dropdown-menu": "^2.1.1",
|
||||||
"@radix-ui/react-hover-card": "^1.1.1",
|
"@radix-ui/react-hover-card": "^1.1.1",
|
||||||
"@radix-ui/react-icons": "^1.3.0",
|
"@radix-ui/react-icons": "^1.3.2",
|
||||||
"@radix-ui/react-label": "^2.1.0",
|
"@radix-ui/react-label": "^2.1.0",
|
||||||
"@radix-ui/react-menubar": "^1.1.1",
|
"@radix-ui/react-menubar": "^1.1.1",
|
||||||
"@radix-ui/react-popover": "^1.1.1",
|
"@radix-ui/react-popover": "^1.1.1",
|
||||||
"@radix-ui/react-scroll-area": "1.2.0",
|
"@radix-ui/react-scroll-area": "1.2.0",
|
||||||
"@radix-ui/react-select": "^2.1.1",
|
"@radix-ui/react-select": "^2.1.1",
|
||||||
"@radix-ui/react-separator": "^1.1.2",
|
"@radix-ui/react-separator": "^1.1.7",
|
||||||
"@radix-ui/react-slot": "^1.2.3",
|
"@radix-ui/react-slot": "^1.2.3",
|
||||||
"@radix-ui/react-tabs": "^1.1.0",
|
"@radix-ui/react-tabs": "^1.1.0",
|
||||||
"@radix-ui/react-toast": "^1.2.1",
|
"@radix-ui/react-toast": "^1.2.1",
|
||||||
"@radix-ui/react-toggle": "^1.1.0",
|
"@radix-ui/react-toggle": "^1.1.0",
|
||||||
"@radix-ui/react-toggle-group": "^1.1.0",
|
"@radix-ui/react-toggle-group": "^1.1.0",
|
||||||
"@radix-ui/react-tooltip": "^1.1.8",
|
"@radix-ui/react-tooltip": "^1.2.7",
|
||||||
"@uidotdev/usehooks": "^2.4.1",
|
"@uidotdev/usehooks": "^2.4.1",
|
||||||
"@xyflow/react": "^12.8.2",
|
"@xyflow/react": "^12.8.2",
|
||||||
"ahooks": "^3.8.1",
|
"ahooks": "^3.8.1",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
User-agent: *
|
User-agent: *
|
||||||
Allow: /
|
Disallow: /
|
||||||
|
|
||||||
Sitemap: https://app.chartdb.io/sitemap.xml
|
Sitemap: https://app.chartdb.io/sitemap.xml
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { cva } from 'class-variance-authority';
|
import { cva } from 'class-variance-authority';
|
||||||
|
|
||||||
export const buttonVariants = cva(
|
export const buttonVariants = cva(
|
||||||
'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50',
|
'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
|
||||||
{
|
{
|
||||||
variants: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
|
|||||||
137
src/components/button/button-with-alternatives.tsx
Normal file
137
src/components/button/button-with-alternatives.tsx
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { ChevronDownIcon } from '@radix-ui/react-icons';
|
||||||
|
import { Slot } from '@radix-ui/react-slot';
|
||||||
|
import { type VariantProps } from 'class-variance-authority';
|
||||||
|
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
import { buttonVariants } from './button-variants';
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from '@/components/dropdown-menu/dropdown-menu';
|
||||||
|
import {
|
||||||
|
Tooltip,
|
||||||
|
TooltipContent,
|
||||||
|
TooltipTrigger,
|
||||||
|
} from '@/components/tooltip/tooltip';
|
||||||
|
|
||||||
|
export interface ButtonAlternative {
|
||||||
|
label: string;
|
||||||
|
onClick: () => void;
|
||||||
|
disabled?: boolean;
|
||||||
|
icon?: React.ReactNode;
|
||||||
|
className?: string;
|
||||||
|
tooltip?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ButtonWithAlternativesProps
|
||||||
|
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||||
|
VariantProps<typeof buttonVariants> {
|
||||||
|
asChild?: boolean;
|
||||||
|
alternatives: Array<ButtonAlternative>;
|
||||||
|
dropdownTriggerClassName?: string;
|
||||||
|
chevronDownIconClassName?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ButtonWithAlternatives = React.forwardRef<
|
||||||
|
HTMLButtonElement,
|
||||||
|
ButtonWithAlternativesProps
|
||||||
|
>(
|
||||||
|
(
|
||||||
|
{
|
||||||
|
className,
|
||||||
|
variant,
|
||||||
|
size,
|
||||||
|
asChild = false,
|
||||||
|
alternatives,
|
||||||
|
children,
|
||||||
|
onClick,
|
||||||
|
dropdownTriggerClassName,
|
||||||
|
chevronDownIconClassName,
|
||||||
|
...props
|
||||||
|
},
|
||||||
|
ref
|
||||||
|
) => {
|
||||||
|
const Comp = asChild ? Slot : 'button';
|
||||||
|
const hasAlternatives = (alternatives?.length ?? 0) > 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="inline-flex items-stretch">
|
||||||
|
<Comp
|
||||||
|
className={cn(
|
||||||
|
buttonVariants({ variant, size }),
|
||||||
|
{ 'rounded-r-none': hasAlternatives },
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
ref={ref}
|
||||||
|
onClick={onClick}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Comp>
|
||||||
|
{hasAlternatives ? (
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<button
|
||||||
|
className={cn(
|
||||||
|
buttonVariants({ variant, size }),
|
||||||
|
'rounded-l-none border-l border-l-primary/5 px-2 min-w-0',
|
||||||
|
className?.includes('h-') &&
|
||||||
|
className.match(/h-\d+/)?.[0],
|
||||||
|
className?.includes('text-') &&
|
||||||
|
className.match(/text-\w+/)?.[0],
|
||||||
|
dropdownTriggerClassName
|
||||||
|
)}
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<ChevronDownIcon
|
||||||
|
className={cn(
|
||||||
|
'size-4 shrink-0',
|
||||||
|
chevronDownIconClassName
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="end">
|
||||||
|
{alternatives.map((alternative, index) => {
|
||||||
|
const menuItem = (
|
||||||
|
<DropdownMenuItem
|
||||||
|
key={index}
|
||||||
|
onClick={alternative.onClick}
|
||||||
|
disabled={alternative.disabled}
|
||||||
|
className={cn(alternative.className)}
|
||||||
|
>
|
||||||
|
<span className="flex w-full items-center justify-between gap-2">
|
||||||
|
{alternative.label}
|
||||||
|
{alternative.icon}
|
||||||
|
</span>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (alternative.tooltip) {
|
||||||
|
return (
|
||||||
|
<Tooltip key={index}>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
{menuItem}
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent side="left">
|
||||||
|
{alternative.tooltip}
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return menuItem;
|
||||||
|
})}
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
ButtonWithAlternatives.displayName = 'ButtonWithAlternatives';
|
||||||
|
|
||||||
|
export { ButtonWithAlternatives };
|
||||||
@@ -38,7 +38,7 @@ export interface CodeSnippetProps {
|
|||||||
className?: string;
|
className?: string;
|
||||||
code: string;
|
code: string;
|
||||||
codeToCopy?: string;
|
codeToCopy?: string;
|
||||||
language?: 'sql' | 'shell';
|
language?: 'sql' | 'shell' | 'dbml';
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
autoScroll?: boolean;
|
autoScroll?: boolean;
|
||||||
isComplete?: boolean;
|
isComplete?: boolean;
|
||||||
|
|||||||
@@ -9,12 +9,14 @@ export const setupDBMLLanguage = (monaco: Monaco) => {
|
|||||||
base: 'vs-dark',
|
base: 'vs-dark',
|
||||||
inherit: true,
|
inherit: true,
|
||||||
rules: [
|
rules: [
|
||||||
|
{ token: 'comment', foreground: '6A9955' }, // Comments
|
||||||
{ token: 'keyword', foreground: '569CD6' }, // Table, Ref keywords
|
{ token: 'keyword', foreground: '569CD6' }, // Table, Ref keywords
|
||||||
{ token: 'string', foreground: 'CE9178' }, // Strings
|
{ token: 'string', foreground: 'CE9178' }, // Strings
|
||||||
{ token: 'annotation', foreground: '9CDCFE' }, // [annotations]
|
{ token: 'annotation', foreground: '9CDCFE' }, // [annotations]
|
||||||
{ token: 'delimiter', foreground: 'D4D4D4' }, // Braces {}
|
{ token: 'delimiter', foreground: 'D4D4D4' }, // Braces {}
|
||||||
{ token: 'operator', foreground: 'D4D4D4' }, // Operators
|
{ token: 'operator', foreground: 'D4D4D4' }, // Operators
|
||||||
{ token: 'datatype', foreground: '4EC9B0' }, // Data types
|
{ token: 'type', foreground: '4EC9B0' }, // Data types
|
||||||
|
{ token: 'identifier', foreground: '9CDCFE' }, // Field names
|
||||||
],
|
],
|
||||||
colors: {},
|
colors: {},
|
||||||
});
|
});
|
||||||
@@ -23,12 +25,14 @@ export const setupDBMLLanguage = (monaco: Monaco) => {
|
|||||||
base: 'vs',
|
base: 'vs',
|
||||||
inherit: true,
|
inherit: true,
|
||||||
rules: [
|
rules: [
|
||||||
|
{ token: 'comment', foreground: '008000' }, // Comments
|
||||||
{ token: 'keyword', foreground: '0000FF' }, // Table, Ref keywords
|
{ token: 'keyword', foreground: '0000FF' }, // Table, Ref keywords
|
||||||
{ token: 'string', foreground: 'A31515' }, // Strings
|
{ token: 'string', foreground: 'A31515' }, // Strings
|
||||||
{ token: 'annotation', foreground: '001080' }, // [annotations]
|
{ token: 'annotation', foreground: '001080' }, // [annotations]
|
||||||
{ token: 'delimiter', foreground: '000000' }, // Braces {}
|
{ token: 'delimiter', foreground: '000000' }, // Braces {}
|
||||||
{ token: 'operator', foreground: '000000' }, // Operators
|
{ token: 'operator', foreground: '000000' }, // Operators
|
||||||
{ token: 'type', foreground: '267F99' }, // Data types
|
{ token: 'type', foreground: '267F99' }, // Data types
|
||||||
|
{ token: 'identifier', foreground: '001080' }, // Field names
|
||||||
],
|
],
|
||||||
colors: {},
|
colors: {},
|
||||||
});
|
});
|
||||||
@@ -37,23 +41,59 @@ export const setupDBMLLanguage = (monaco: Monaco) => {
|
|||||||
const datatypePattern = dataTypesNames.join('|');
|
const datatypePattern = dataTypesNames.join('|');
|
||||||
|
|
||||||
monaco.languages.setMonarchTokensProvider('dbml', {
|
monaco.languages.setMonarchTokensProvider('dbml', {
|
||||||
keywords: ['Table', 'Ref', 'Indexes', 'Note', 'Enum'],
|
keywords: ['Table', 'Ref', 'Indexes', 'Note', 'Enum', 'enum'],
|
||||||
datatypes: dataTypesNames,
|
datatypes: dataTypesNames,
|
||||||
|
operators: ['>', '<', '-'],
|
||||||
|
|
||||||
tokenizer: {
|
tokenizer: {
|
||||||
root: [
|
root: [
|
||||||
|
// Comments
|
||||||
|
[/\/\/.*$/, 'comment'],
|
||||||
|
|
||||||
|
// Keywords - case insensitive
|
||||||
[
|
[
|
||||||
/\b([Tt][Aa][Bb][Ll][Ee]|[Ee][Nn][Uu][Mm]|[Rr][Ee][Ff]|[Ii][Nn][Dd][Ee][Xx][Ee][Ss]|[Nn][Oo][Tt][Ee])\b/,
|
/\b([Tt][Aa][Bb][Ll][Ee]|[Ee][Nn][Uu][Mm]|[Rr][Ee][Ff]|[Ii][Nn][Dd][Ee][Xx][Ee][Ss]|[Nn][Oo][Tt][Ee])\b/,
|
||||||
'keyword',
|
'keyword',
|
||||||
],
|
],
|
||||||
|
|
||||||
|
// Annotations in brackets
|
||||||
[/\[.*?\]/, 'annotation'],
|
[/\[.*?\]/, 'annotation'],
|
||||||
|
|
||||||
|
// Strings
|
||||||
[/'''/, 'string', '@tripleQuoteString'],
|
[/'''/, 'string', '@tripleQuoteString'],
|
||||||
[/".*?"/, 'string'],
|
[/"([^"\\]|\\.)*$/, 'string.invalid'], // non-terminated string
|
||||||
[/'.*?'/, 'string'],
|
[/'([^'\\]|\\.)*$/, 'string.invalid'], // non-terminated string
|
||||||
|
[/"/, 'string', '@string_double'],
|
||||||
|
[/'/, 'string', '@string_single'],
|
||||||
[/`.*?`/, 'string'],
|
[/`.*?`/, 'string'],
|
||||||
[/[{}]/, 'delimiter'],
|
|
||||||
[/[<>]/, 'operator'],
|
// Delimiters and operators
|
||||||
[new RegExp(`\\b(${datatypePattern})\\b`, 'i'), 'type'], // Added 'i' flag for case-insensitive matching
|
[/[{}()]/, 'delimiter'],
|
||||||
|
[/[<>-]/, 'operator'],
|
||||||
|
[/:/, 'delimiter'],
|
||||||
|
|
||||||
|
// Data types
|
||||||
|
[new RegExp(`\\b(${datatypePattern})\\b`, 'i'), 'type'],
|
||||||
|
|
||||||
|
// Numbers
|
||||||
|
[/\d+/, 'number'],
|
||||||
|
|
||||||
|
// Identifiers
|
||||||
|
[/[a-zA-Z_]\w*/, 'identifier'],
|
||||||
],
|
],
|
||||||
|
|
||||||
|
string_double: [
|
||||||
|
[/[^\\"]+/, 'string'],
|
||||||
|
[/\\./, 'string.escape'],
|
||||||
|
[/"/, 'string', '@pop'],
|
||||||
|
],
|
||||||
|
|
||||||
|
string_single: [
|
||||||
|
[/[^\\']+/, 'string'],
|
||||||
|
[/\\./, 'string.escape'],
|
||||||
|
[/'/, 'string', '@pop'],
|
||||||
|
],
|
||||||
|
|
||||||
tripleQuoteString: [
|
tripleQuoteString: [
|
||||||
[/[^']+/, 'string'],
|
[/[^']+/, 'string'],
|
||||||
[/'''/, 'string', '@pop'],
|
[/'''/, 'string', '@pop'],
|
||||||
|
|||||||
@@ -5,27 +5,45 @@ import {
|
|||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
} from '@/components/popover/popover';
|
} from '@/components/popover/popover';
|
||||||
import { colorOptions } from '@/lib/colors';
|
import { colorOptions } from '@/lib/colors';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
export interface ColorPickerProps {
|
export interface ColorPickerProps {
|
||||||
color: string;
|
color: string;
|
||||||
onChange: (color: string) => void;
|
onChange: (color: string) => void;
|
||||||
|
disabled?: boolean;
|
||||||
|
popoverOnMouseDown?: (e: React.MouseEvent) => void;
|
||||||
|
popoverOnClick?: (e: React.MouseEvent) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ColorPicker = React.forwardRef<
|
export const ColorPicker = React.forwardRef<
|
||||||
React.ElementRef<typeof PopoverTrigger>,
|
React.ElementRef<typeof PopoverTrigger>,
|
||||||
ColorPickerProps
|
ColorPickerProps
|
||||||
>(({ color, onChange }, ref) => {
|
>(({ color, onChange, disabled, popoverOnMouseDown, popoverOnClick }, ref) => {
|
||||||
return (
|
return (
|
||||||
<Popover>
|
<Popover>
|
||||||
<PopoverTrigger asChild ref={ref}>
|
<PopoverTrigger
|
||||||
|
asChild
|
||||||
|
ref={ref}
|
||||||
|
disabled={disabled}
|
||||||
|
{...(disabled ? { onClick: (e) => e.preventDefault() } : {})}
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
className="h-6 w-8 cursor-pointer rounded-md border-2 border-muted transition-shadow hover:shadow-md"
|
className={cn(
|
||||||
|
'h-6 w-8 cursor-pointer rounded-md border-2 border-muted transition-shadow hover:shadow-md',
|
||||||
|
{
|
||||||
|
'hover:shadow-none cursor-default': disabled,
|
||||||
|
}
|
||||||
|
)}
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: color,
|
backgroundColor: color,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent className="w-fit">
|
<PopoverContent
|
||||||
|
className="w-fit"
|
||||||
|
onMouseDown={popoverOnMouseDown}
|
||||||
|
onClick={popoverOnClick}
|
||||||
|
>
|
||||||
<div className="grid grid-cols-4 gap-2">
|
<div className="grid grid-cols-4 gap-2">
|
||||||
{colorOptions.map((option) => (
|
{colorOptions.map((option) => (
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { Cross2Icon } from '@radix-ui/react-icons';
|
|||||||
|
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { ScrollArea } from '../scroll-area/scroll-area';
|
import { ScrollArea } from '../scroll-area/scroll-area';
|
||||||
|
import { ChevronLeft } from 'lucide-react';
|
||||||
|
|
||||||
const Dialog = DialogPrimitive.Root;
|
const Dialog = DialogPrimitive.Root;
|
||||||
|
|
||||||
@@ -32,10 +33,45 @@ const DialogContent = React.forwardRef<
|
|||||||
React.ElementRef<typeof DialogPrimitive.Content>,
|
React.ElementRef<typeof DialogPrimitive.Content>,
|
||||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & {
|
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & {
|
||||||
showClose?: boolean;
|
showClose?: boolean;
|
||||||
|
showBack?: boolean;
|
||||||
|
backButtonClassName?: string;
|
||||||
|
blurBackground?: boolean;
|
||||||
|
forceOverlay?: boolean;
|
||||||
|
onBackClick?: () => void;
|
||||||
}
|
}
|
||||||
>(({ className, children, showClose, ...props }, ref) => (
|
>(
|
||||||
|
(
|
||||||
|
{
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
showClose,
|
||||||
|
showBack,
|
||||||
|
onBackClick,
|
||||||
|
backButtonClassName,
|
||||||
|
blurBackground,
|
||||||
|
forceOverlay,
|
||||||
|
...props
|
||||||
|
},
|
||||||
|
ref
|
||||||
|
) => (
|
||||||
<DialogPortal>
|
<DialogPortal>
|
||||||
<DialogOverlay />
|
{forceOverlay ? (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
'fixed inset-0 z-50 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
|
||||||
|
{
|
||||||
|
'bg-black/80': !blurBackground,
|
||||||
|
'bg-black/30 backdrop-blur-sm': blurBackground,
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
data-state="open"
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
<DialogOverlay
|
||||||
|
className={cn({
|
||||||
|
'bg-black/30 backdrop-blur-sm': blurBackground,
|
||||||
|
})}
|
||||||
|
/>
|
||||||
<DialogPrimitive.Content
|
<DialogPrimitive.Content
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
@@ -45,6 +81,17 @@ const DialogContent = React.forwardRef<
|
|||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
{showBack && (
|
||||||
|
<button
|
||||||
|
onClick={() => onBackClick?.()}
|
||||||
|
className={cn(
|
||||||
|
'absolute left-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground',
|
||||||
|
backButtonClassName
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<ChevronLeft className="size-4" />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
{showClose && (
|
{showClose && (
|
||||||
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
|
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
|
||||||
<Cross2Icon className="size-4" />
|
<Cross2Icon className="size-4" />
|
||||||
@@ -53,7 +100,8 @@ const DialogContent = React.forwardRef<
|
|||||||
)}
|
)}
|
||||||
</DialogPrimitive.Content>
|
</DialogPrimitive.Content>
|
||||||
</DialogPortal>
|
</DialogPortal>
|
||||||
));
|
)
|
||||||
|
);
|
||||||
DialogContent.displayName = DialogPrimitive.Content.displayName;
|
DialogContent.displayName = DialogPrimitive.Content.displayName;
|
||||||
|
|
||||||
const DialogHeader = ({
|
const DialogHeader = ({
|
||||||
|
|||||||
@@ -2,16 +2,13 @@ import React from 'react';
|
|||||||
|
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
export interface InputProps
|
const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<'input'>>(
|
||||||
extends React.InputHTMLAttributes<HTMLInputElement> {}
|
|
||||||
|
|
||||||
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
||||||
({ className, type, ...props }, ref) => {
|
({ className, type, ...props }, ref) => {
|
||||||
return (
|
return (
|
||||||
<input
|
<input
|
||||||
type={type}
|
type={type}
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50',
|
'flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ export interface SelectBoxOption {
|
|||||||
regex?: string;
|
regex?: string;
|
||||||
extractRegex?: RegExp;
|
extractRegex?: RegExp;
|
||||||
group?: string;
|
group?: string;
|
||||||
|
icon?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SelectBoxProps {
|
export interface SelectBoxProps {
|
||||||
@@ -53,6 +54,11 @@ export interface SelectBoxProps {
|
|||||||
open?: boolean;
|
open?: boolean;
|
||||||
onOpenChange?: (open: boolean) => void;
|
onOpenChange?: (open: boolean) => void;
|
||||||
popoverClassName?: string;
|
popoverClassName?: string;
|
||||||
|
readonly?: boolean;
|
||||||
|
footerButtons?: React.ReactNode;
|
||||||
|
commandOnMouseDown?: (e: React.MouseEvent) => void;
|
||||||
|
commandOnClick?: (e: React.MouseEvent) => void;
|
||||||
|
onSearchChange?: (search: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
|
export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
|
||||||
@@ -78,6 +84,11 @@ export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
|
|||||||
open,
|
open,
|
||||||
onOpenChange: setOpen,
|
onOpenChange: setOpen,
|
||||||
popoverClassName,
|
popoverClassName,
|
||||||
|
readonly,
|
||||||
|
footerButtons,
|
||||||
|
commandOnMouseDown,
|
||||||
|
commandOnClick,
|
||||||
|
onSearchChange,
|
||||||
},
|
},
|
||||||
ref
|
ref
|
||||||
) => {
|
) => {
|
||||||
@@ -94,6 +105,10 @@ export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
|
|||||||
setOpen?.(isOpen);
|
setOpen?.(isOpen);
|
||||||
setIsOpen(isOpen);
|
setIsOpen(isOpen);
|
||||||
|
|
||||||
|
if (isOpen) {
|
||||||
|
setSearchTerm('');
|
||||||
|
}
|
||||||
|
|
||||||
setTimeout(() => (document.body.style.pointerEvents = ''), 500);
|
setTimeout(() => (document.body.style.pointerEvents = ''), 500);
|
||||||
},
|
},
|
||||||
[setOpen]
|
[setOpen]
|
||||||
@@ -148,6 +163,7 @@ export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
|
|||||||
className={`inline-flex min-w-0 shrink-0 items-center gap-1 rounded-md border py-0.5 pl-2 pr-1 text-xs font-medium text-foreground transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 ${oneLine ? 'mx-0.5' : ''}`}
|
className={`inline-flex min-w-0 shrink-0 items-center gap-1 rounded-md border py-0.5 pl-2 pr-1 text-xs font-medium text-foreground transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 ${oneLine ? 'mx-0.5' : ''}`}
|
||||||
>
|
>
|
||||||
<span>{option.label}</span>
|
<span>{option.label}</span>
|
||||||
|
{!readonly ? (
|
||||||
<span
|
<span
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -157,9 +173,10 @@ export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
|
|||||||
>
|
>
|
||||||
<Cross2Icon />
|
<Cross2Icon />
|
||||||
</span>
|
</span>
|
||||||
|
) : null}
|
||||||
</span>
|
</span>
|
||||||
)),
|
)),
|
||||||
[options, value, handleSelect, oneLine, keepOrder]
|
[options, value, handleSelect, oneLine, keepOrder, readonly]
|
||||||
);
|
);
|
||||||
|
|
||||||
const isAllSelected = React.useMemo(
|
const isAllSelected = React.useMemo(
|
||||||
@@ -225,6 +242,7 @@ export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
|
|||||||
<CommandItem
|
<CommandItem
|
||||||
className="flex items-center"
|
className="flex items-center"
|
||||||
key={option.value}
|
key={option.value}
|
||||||
|
value={option.label}
|
||||||
keywords={option.regex ? [option.regex] : undefined}
|
keywords={option.regex ? [option.regex] : undefined}
|
||||||
onSelect={() =>
|
onSelect={() =>
|
||||||
handleSelect(
|
handleSelect(
|
||||||
@@ -232,6 +250,8 @@ export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
|
|||||||
matches?.map((match) => match?.toString())
|
matches?.map((match) => match?.toString())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
onMouseDown={commandOnMouseDown}
|
||||||
|
onClick={commandOnClick}
|
||||||
>
|
>
|
||||||
{multiple && (
|
{multiple && (
|
||||||
<div
|
<div
|
||||||
@@ -246,6 +266,11 @@ export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="flex flex-1 items-center truncate">
|
<div className="flex flex-1 items-center truncate">
|
||||||
|
{option.icon ? (
|
||||||
|
<span className="mr-2 shrink-0">
|
||||||
|
{option.icon}
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
<span>
|
<span>
|
||||||
{isRegexMatch ? searchTerm : option.label}
|
{isRegexMatch ? searchTerm : option.label}
|
||||||
{!isRegexMatch && optionSuffix
|
{!isRegexMatch && optionSuffix
|
||||||
@@ -272,7 +297,15 @@ export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
|
|||||||
</CommandItem>
|
</CommandItem>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[value, multiple, searchTerm, handleSelect, optionSuffix]
|
[
|
||||||
|
value,
|
||||||
|
multiple,
|
||||||
|
searchTerm,
|
||||||
|
handleSelect,
|
||||||
|
optionSuffix,
|
||||||
|
commandOnClick,
|
||||||
|
commandOnMouseDown,
|
||||||
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -280,7 +313,7 @@ export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
|
|||||||
<PopoverTrigger asChild tabIndex={0} onKeyDown={handleKeyDown}>
|
<PopoverTrigger asChild tabIndex={0} onKeyDown={handleKeyDown}>
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
`flex min-h-[36px] cursor-pointer items-center justify-between rounded-md border px-3 py-1 data-[state=open]:border-ring ${disabled ? 'bg-muted pointer-events-none' : ''}`,
|
`flex min-h-[36px] cursor-pointer items-center justify-between rounded-md border px-3 py-1 data-[state=open]:border-ring ${disabled ? 'bg-muted pointer-events-none' : ''} ${readonly ? 'pointer-events-none' : ''}`,
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@@ -350,6 +383,8 @@ export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
|
|||||||
popoverClassName
|
popoverClassName
|
||||||
)}
|
)}
|
||||||
align="center"
|
align="center"
|
||||||
|
onMouseDown={(e) => e.stopPropagation()}
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
>
|
>
|
||||||
<Command
|
<Command
|
||||||
filter={(value, search, keywords) => {
|
filter={(value, search, keywords) => {
|
||||||
@@ -372,7 +407,10 @@ export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
|
|||||||
<div className="relative">
|
<div className="relative">
|
||||||
<CommandInput
|
<CommandInput
|
||||||
value={searchTerm}
|
value={searchTerm}
|
||||||
onValueChange={(e) => setSearchTerm(e)}
|
onValueChange={(e) => {
|
||||||
|
setSearchTerm(e);
|
||||||
|
onSearchChange?.(e);
|
||||||
|
}}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
placeholder={inputPlaceholder ?? 'Search...'}
|
placeholder={inputPlaceholder ?? 'Search...'}
|
||||||
className="h-9"
|
className="h-9"
|
||||||
@@ -439,6 +477,9 @@ export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
|
|||||||
</div>
|
</div>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
</Command>
|
</Command>
|
||||||
|
{footerButtons ? (
|
||||||
|
<div className="border-t">{footerButtons}</div>
|
||||||
|
) : null}
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
|
|||||||
const SIDEBAR_WIDTH = '16rem';
|
const SIDEBAR_WIDTH = '16rem';
|
||||||
const SIDEBAR_WIDTH_MOBILE = '18rem';
|
const SIDEBAR_WIDTH_MOBILE = '18rem';
|
||||||
const SIDEBAR_WIDTH_ICON = '3rem';
|
const SIDEBAR_WIDTH_ICON = '3rem';
|
||||||
|
const SIDEBAR_WIDTH_ICON_EXTENDED = '4rem';
|
||||||
const SIDEBAR_KEYBOARD_SHORTCUT = 'b';
|
const SIDEBAR_KEYBOARD_SHORTCUT = 'b';
|
||||||
|
|
||||||
type SidebarContext = {
|
type SidebarContext = {
|
||||||
@@ -142,6 +143,8 @@ const SidebarProvider = React.forwardRef<
|
|||||||
{
|
{
|
||||||
'--sidebar-width': SIDEBAR_WIDTH,
|
'--sidebar-width': SIDEBAR_WIDTH,
|
||||||
'--sidebar-width-icon': SIDEBAR_WIDTH_ICON,
|
'--sidebar-width-icon': SIDEBAR_WIDTH_ICON,
|
||||||
|
'--sidebar-width-icon-extended':
|
||||||
|
SIDEBAR_WIDTH_ICON_EXTENDED,
|
||||||
...style,
|
...style,
|
||||||
} as React.CSSProperties
|
} as React.CSSProperties
|
||||||
}
|
}
|
||||||
@@ -166,7 +169,7 @@ const Sidebar = React.forwardRef<
|
|||||||
React.ComponentProps<'div'> & {
|
React.ComponentProps<'div'> & {
|
||||||
side?: 'left' | 'right';
|
side?: 'left' | 'right';
|
||||||
variant?: 'sidebar' | 'floating' | 'inset';
|
variant?: 'sidebar' | 'floating' | 'inset';
|
||||||
collapsible?: 'offcanvas' | 'icon' | 'none';
|
collapsible?: 'offcanvas' | 'icon' | 'icon-extended' | 'none';
|
||||||
}
|
}
|
||||||
>(
|
>(
|
||||||
(
|
(
|
||||||
@@ -245,8 +248,8 @@ const Sidebar = React.forwardRef<
|
|||||||
'group-data-[collapsible=offcanvas]:w-0',
|
'group-data-[collapsible=offcanvas]:w-0',
|
||||||
'group-data-[side=right]:rotate-180',
|
'group-data-[side=right]:rotate-180',
|
||||||
variant === 'floating' || variant === 'inset'
|
variant === 'floating' || variant === 'inset'
|
||||||
? 'group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4))]'
|
? 'group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4))] group-data-[collapsible=icon-extended]:w-[calc(var(--sidebar-width-icon-extended)_+_theme(spacing.4))]'
|
||||||
: 'group-data-[collapsible=icon]:w-[--sidebar-width-icon]'
|
: 'group-data-[collapsible=icon]:w-[--sidebar-width-icon] group-data-[collapsible=icon-extended]:w-[--sidebar-width-icon-extended]'
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
@@ -257,8 +260,8 @@ const Sidebar = React.forwardRef<
|
|||||||
: 'right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]',
|
: 'right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]',
|
||||||
// Adjust the padding for floating and inset variants.
|
// Adjust the padding for floating and inset variants.
|
||||||
variant === 'floating' || variant === 'inset'
|
variant === 'floating' || variant === 'inset'
|
||||||
? 'p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4)_+2px)]'
|
? 'p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4)_+2px)] group-data-[collapsible=icon-extended]:w-[calc(var(--sidebar-width-icon-extended)_+_theme(spacing.4)_+2px)]'
|
||||||
: 'group-data-[collapsible=icon]:w-[--sidebar-width-icon] group-data-[side=left]:border-r group-data-[side=right]:border-l',
|
: 'group-data-[collapsible=icon]:w-[--sidebar-width-icon] group-data-[collapsible=icon-extended]:w-[--sidebar-width-icon-extended] group-data-[side=left]:border-r group-data-[side=right]:border-l',
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -421,7 +424,7 @@ const SidebarContent = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
data-sidebar="content"
|
data-sidebar="content"
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden',
|
'flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden group-data-[collapsible=icon-extended]:overflow-hidden',
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -461,6 +464,7 @@ const SidebarGroupLabel = React.forwardRef<
|
|||||||
className={cn(
|
className={cn(
|
||||||
'flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70 outline-none ring-sidebar-ring transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',
|
'flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70 outline-none ring-sidebar-ring transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',
|
||||||
'group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0',
|
'group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0',
|
||||||
|
'group-data-[collapsible=icon-extended]:-mt-8 group-data-[collapsible=icon-extended]:opacity-0',
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -483,7 +487,7 @@ const SidebarGroupAction = React.forwardRef<
|
|||||||
'absolute right-3 top-3.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground outline-none ring-sidebar-ring transition-transform hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',
|
'absolute right-3 top-3.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground outline-none ring-sidebar-ring transition-transform hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',
|
||||||
// Increases the hit area of the button on mobile.
|
// Increases the hit area of the button on mobile.
|
||||||
'after:absolute after:-inset-2 after:md:hidden',
|
'after:absolute after:-inset-2 after:md:hidden',
|
||||||
'group-data-[collapsible=icon]:hidden',
|
'group-data-[collapsible=icon]:hidden group-data-[collapsible=icon-extended]:hidden',
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -532,7 +536,7 @@ const SidebarMenuItem = React.forwardRef<
|
|||||||
SidebarMenuItem.displayName = 'SidebarMenuItem';
|
SidebarMenuItem.displayName = 'SidebarMenuItem';
|
||||||
|
|
||||||
const sidebarMenuButtonVariants = cva(
|
const sidebarMenuButtonVariants = cva(
|
||||||
'peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-none ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-[[data-sidebar=menu-action]]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:!size-8 group-data-[collapsible=icon]:!p-2 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0',
|
'peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-none ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-[[data-sidebar=menu-action]]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:!size-8 group-data-[collapsible=icon-extended]:h-auto group-data-[collapsible=icon-extended]:flex-col group-data-[collapsible=icon-extended]:gap-1 group-data-[collapsible=icon-extended]:p-2 group-data-[collapsible=icon]:!p-2 [&>span:last-child]:truncate group-data-[collapsible=icon-extended]:[&>span]:w-full group-data-[collapsible=icon-extended]:[&>span]:text-center group-data-[collapsible=icon-extended]:[&>span]:text-[10px] group-data-[collapsible=icon-extended]:[&>span]:leading-tight [&>svg]:size-4 [&>svg]:shrink-0',
|
||||||
{
|
{
|
||||||
variants: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
@@ -636,7 +640,7 @@ const SidebarMenuAction = React.forwardRef<
|
|||||||
'peer-data-[size=sm]/menu-button:top-1',
|
'peer-data-[size=sm]/menu-button:top-1',
|
||||||
'peer-data-[size=default]/menu-button:top-1.5',
|
'peer-data-[size=default]/menu-button:top-1.5',
|
||||||
'peer-data-[size=lg]/menu-button:top-2.5',
|
'peer-data-[size=lg]/menu-button:top-2.5',
|
||||||
'group-data-[collapsible=icon]:hidden',
|
'group-data-[collapsible=icon]:hidden group-data-[collapsible=icon-extended]:hidden',
|
||||||
showOnHover &&
|
showOnHover &&
|
||||||
'group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 peer-data-[active=true]/menu-button:text-sidebar-accent-foreground md:opacity-0',
|
'group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 peer-data-[active=true]/menu-button:text-sidebar-accent-foreground md:opacity-0',
|
||||||
className
|
className
|
||||||
@@ -753,7 +757,7 @@ const SidebarMenuSubButton = React.forwardRef<
|
|||||||
'data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground',
|
'data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground',
|
||||||
size === 'sm' && 'text-xs',
|
size === 'sm' && 'text-xs',
|
||||||
size === 'md' && 'text-sm',
|
size === 'md' && 'text-sm',
|
||||||
'group-data-[collapsible=icon]:hidden',
|
'group-data-[collapsible=icon]:hidden group-data-[collapsible=icon-extended]:hidden',
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -13,15 +13,17 @@ const TooltipContent = React.forwardRef<
|
|||||||
React.ElementRef<typeof TooltipPrimitive.Content>,
|
React.ElementRef<typeof TooltipPrimitive.Content>,
|
||||||
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
||||||
>(({ className, sideOffset = 4, ...props }, ref) => (
|
>(({ className, sideOffset = 4, ...props }, ref) => (
|
||||||
|
// <TooltipPrimitive.Portal>
|
||||||
<TooltipPrimitive.Content
|
<TooltipPrimitive.Content
|
||||||
ref={ref}
|
ref={ref}
|
||||||
sideOffset={sideOffset}
|
sideOffset={sideOffset}
|
||||||
className={cn(
|
className={cn(
|
||||||
'z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
'z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-tooltip-content-transform-origin]',
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
|
// </TooltipPrimitive.Portal>
|
||||||
));
|
));
|
||||||
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ interface TreeViewProps<
|
|||||||
renderHoverComponent?: (node: TreeNode<Type, Context>) => ReactNode;
|
renderHoverComponent?: (node: TreeNode<Type, Context>) => ReactNode;
|
||||||
renderActionsComponent?: (node: TreeNode<Type, Context>) => ReactNode;
|
renderActionsComponent?: (node: TreeNode<Type, Context>) => ReactNode;
|
||||||
loadingNodeIds?: string[];
|
loadingNodeIds?: string[];
|
||||||
|
disableCache?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TreeView<
|
export function TreeView<
|
||||||
@@ -62,12 +63,14 @@ export function TreeView<
|
|||||||
renderHoverComponent,
|
renderHoverComponent,
|
||||||
renderActionsComponent,
|
renderActionsComponent,
|
||||||
loadingNodeIds,
|
loadingNodeIds,
|
||||||
|
disableCache = false,
|
||||||
}: TreeViewProps<Type, Context>) {
|
}: TreeViewProps<Type, Context>) {
|
||||||
const { expanded, loading, loadedChildren, hasMoreChildren, toggleNode } =
|
const { expanded, loading, loadedChildren, hasMoreChildren, toggleNode } =
|
||||||
useTree({
|
useTree({
|
||||||
fetchChildren,
|
fetchChildren,
|
||||||
expanded: expandedProp,
|
expanded: expandedProp,
|
||||||
setExpanded: setExpandedProp,
|
setExpanded: setExpandedProp,
|
||||||
|
disableCache,
|
||||||
});
|
});
|
||||||
const [selectedIdInternal, setSelectedIdInternal] = React.useState<
|
const [selectedIdInternal, setSelectedIdInternal] = React.useState<
|
||||||
string | undefined
|
string | undefined
|
||||||
@@ -145,6 +148,7 @@ export function TreeView<
|
|||||||
renderHoverComponent={renderHoverComponent}
|
renderHoverComponent={renderHoverComponent}
|
||||||
renderActionsComponent={renderActionsComponent}
|
renderActionsComponent={renderActionsComponent}
|
||||||
loadingNodeIds={loadingNodeIds}
|
loadingNodeIds={loadingNodeIds}
|
||||||
|
disableCache={disableCache}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -179,6 +183,7 @@ interface TreeNodeProps<
|
|||||||
renderHoverComponent?: (node: TreeNode<Type, Context>) => ReactNode;
|
renderHoverComponent?: (node: TreeNode<Type, Context>) => ReactNode;
|
||||||
renderActionsComponent?: (node: TreeNode<Type, Context>) => ReactNode;
|
renderActionsComponent?: (node: TreeNode<Type, Context>) => ReactNode;
|
||||||
loadingNodeIds?: string[];
|
loadingNodeIds?: string[];
|
||||||
|
disableCache?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function TreeNode<Type extends string, Context extends Record<Type, unknown>>({
|
function TreeNode<Type extends string, Context extends Record<Type, unknown>>({
|
||||||
@@ -201,11 +206,16 @@ function TreeNode<Type extends string, Context extends Record<Type, unknown>>({
|
|||||||
renderHoverComponent,
|
renderHoverComponent,
|
||||||
renderActionsComponent,
|
renderActionsComponent,
|
||||||
loadingNodeIds,
|
loadingNodeIds,
|
||||||
|
disableCache = false,
|
||||||
}: TreeNodeProps<Type, Context>) {
|
}: TreeNodeProps<Type, Context>) {
|
||||||
const [isHovered, setIsHovered] = useState(false);
|
const [isHovered, setIsHovered] = useState(false);
|
||||||
const isExpanded = expanded[node.id];
|
const isExpanded = expanded[node.id];
|
||||||
const isLoading = loading[node.id];
|
const isLoading = loading[node.id];
|
||||||
const children = loadedChildren[node.id] || node.children;
|
// If cache is disabled, always use fresh node.children
|
||||||
|
// Otherwise, use cached loadedChildren if available (for async fetched data)
|
||||||
|
const children = disableCache
|
||||||
|
? node.children
|
||||||
|
: node.children || loadedChildren[node.id];
|
||||||
const isSelected = selectedId === node.id;
|
const isSelected = selectedId === node.id;
|
||||||
|
|
||||||
const IconComponent =
|
const IconComponent =
|
||||||
@@ -423,6 +433,7 @@ function TreeNode<Type extends string, Context extends Record<Type, unknown>>({
|
|||||||
renderHoverComponent={renderHoverComponent}
|
renderHoverComponent={renderHoverComponent}
|
||||||
renderActionsComponent={renderActionsComponent}
|
renderActionsComponent={renderActionsComponent}
|
||||||
loadingNodeIds={loadingNodeIds}
|
loadingNodeIds={loadingNodeIds}
|
||||||
|
disableCache={disableCache}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
|
|||||||
@@ -28,10 +28,12 @@ export function useTree<
|
|||||||
fetchChildren,
|
fetchChildren,
|
||||||
expanded: expandedProp,
|
expanded: expandedProp,
|
||||||
setExpanded: setExpandedProp,
|
setExpanded: setExpandedProp,
|
||||||
|
disableCache = false,
|
||||||
}: {
|
}: {
|
||||||
fetchChildren?: FetchChildrenFunction<Type, Context>;
|
fetchChildren?: FetchChildrenFunction<Type, Context>;
|
||||||
expanded?: ExpandedState;
|
expanded?: ExpandedState;
|
||||||
setExpanded?: Dispatch<SetStateAction<ExpandedState>>;
|
setExpanded?: Dispatch<SetStateAction<ExpandedState>>;
|
||||||
|
disableCache?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const [expandedInternal, setExpandedInternal] = useState<ExpandedState>({});
|
const [expandedInternal, setExpandedInternal] = useState<ExpandedState>({});
|
||||||
|
|
||||||
@@ -89,8 +91,8 @@ export function useTree<
|
|||||||
// Get any previously fetched children
|
// Get any previously fetched children
|
||||||
const previouslyFetchedChildren = loadedChildren[nodeId] || [];
|
const previouslyFetchedChildren = loadedChildren[nodeId] || [];
|
||||||
|
|
||||||
// If we have static children, merge them with any previously fetched children
|
// Only cache if caching is enabled
|
||||||
if (staticChildren?.length) {
|
if (!disableCache && staticChildren?.length) {
|
||||||
const mergedChildren = mergeChildren(
|
const mergedChildren = mergeChildren(
|
||||||
staticChildren,
|
staticChildren,
|
||||||
previouslyFetchedChildren
|
previouslyFetchedChildren
|
||||||
@@ -110,8 +112,8 @@ export function useTree<
|
|||||||
// Set expanded state immediately to show static/previously fetched children
|
// Set expanded state immediately to show static/previously fetched children
|
||||||
setExpanded((prev) => ({ ...prev, [nodeId]: true }));
|
setExpanded((prev) => ({ ...prev, [nodeId]: true }));
|
||||||
|
|
||||||
// If we haven't loaded dynamic children yet
|
// If we haven't loaded dynamic children yet and cache is enabled
|
||||||
if (!previouslyFetchedChildren.length) {
|
if (!disableCache && !previouslyFetchedChildren.length) {
|
||||||
setLoading((prev) => ({ ...prev, [nodeId]: true }));
|
setLoading((prev) => ({ ...prev, [nodeId]: true }));
|
||||||
try {
|
try {
|
||||||
const fetchedChildren = await fetchChildren?.(
|
const fetchedChildren = await fetchChildren?.(
|
||||||
@@ -140,7 +142,14 @@ export function useTree<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[expanded, loadedChildren, fetchChildren, mergeChildren, setExpanded]
|
[
|
||||||
|
expanded,
|
||||||
|
loadedChildren,
|
||||||
|
fetchChildren,
|
||||||
|
mergeChildren,
|
||||||
|
setExpanded,
|
||||||
|
disableCache,
|
||||||
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -14,6 +14,41 @@ export interface CanvasContext {
|
|||||||
overlapGraph: Graph<string>;
|
overlapGraph: Graph<string>;
|
||||||
setShowFilter: React.Dispatch<React.SetStateAction<boolean>>;
|
setShowFilter: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
showFilter: boolean;
|
showFilter: boolean;
|
||||||
|
editTableModeTable: {
|
||||||
|
tableId: string;
|
||||||
|
fieldId?: string;
|
||||||
|
} | null;
|
||||||
|
setEditTableModeTable: React.Dispatch<
|
||||||
|
React.SetStateAction<{
|
||||||
|
tableId: string;
|
||||||
|
fieldId?: string;
|
||||||
|
} | null>
|
||||||
|
>;
|
||||||
|
tempFloatingEdge: {
|
||||||
|
sourceNodeId: string;
|
||||||
|
targetNodeId?: string;
|
||||||
|
} | null;
|
||||||
|
setTempFloatingEdge: React.Dispatch<
|
||||||
|
React.SetStateAction<{
|
||||||
|
sourceNodeId: string;
|
||||||
|
targetNodeId?: string;
|
||||||
|
} | null>
|
||||||
|
>;
|
||||||
|
startFloatingEdgeCreation: ({
|
||||||
|
sourceNodeId,
|
||||||
|
}: {
|
||||||
|
sourceNodeId: string;
|
||||||
|
}) => void;
|
||||||
|
endFloatingEdgeCreation: () => void;
|
||||||
|
hoveringTableId: string | null;
|
||||||
|
setHoveringTableId: React.Dispatch<React.SetStateAction<string | null>>;
|
||||||
|
showCreateRelationshipNode: (params: {
|
||||||
|
sourceTableId: string;
|
||||||
|
targetTableId: string;
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
}) => void;
|
||||||
|
hideCreateRelationshipNode: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const canvasContext = createContext<CanvasContext>({
|
export const canvasContext = createContext<CanvasContext>({
|
||||||
@@ -23,4 +58,14 @@ export const canvasContext = createContext<CanvasContext>({
|
|||||||
overlapGraph: createGraph(),
|
overlapGraph: createGraph(),
|
||||||
setShowFilter: emptyFn,
|
setShowFilter: emptyFn,
|
||||||
showFilter: false,
|
showFilter: false,
|
||||||
|
editTableModeTable: null,
|
||||||
|
setEditTableModeTable: emptyFn,
|
||||||
|
tempFloatingEdge: null,
|
||||||
|
setTempFloatingEdge: emptyFn,
|
||||||
|
startFloatingEdgeCreation: emptyFn,
|
||||||
|
endFloatingEdgeCreation: emptyFn,
|
||||||
|
hoveringTableId: null,
|
||||||
|
setHoveringTableId: emptyFn,
|
||||||
|
showCreateRelationshipNode: emptyFn,
|
||||||
|
hideCreateRelationshipNode: emptyFn,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,28 +1,71 @@
|
|||||||
import React, { type ReactNode, useCallback, useState } from 'react';
|
import React, {
|
||||||
|
type ReactNode,
|
||||||
|
useCallback,
|
||||||
|
useState,
|
||||||
|
useEffect,
|
||||||
|
useRef,
|
||||||
|
} from 'react';
|
||||||
|
import type { CanvasContext } from './canvas-context';
|
||||||
import { canvasContext } from './canvas-context';
|
import { canvasContext } from './canvas-context';
|
||||||
import { useChartDB } from '@/hooks/use-chartdb';
|
import { useChartDB } from '@/hooks/use-chartdb';
|
||||||
import {
|
import { adjustTablePositions } from '@/lib/domain/db-table';
|
||||||
adjustTablePositions,
|
|
||||||
shouldShowTablesBySchemaFilter,
|
|
||||||
} from '@/lib/domain/db-table';
|
|
||||||
import { useReactFlow } from '@xyflow/react';
|
import { useReactFlow } from '@xyflow/react';
|
||||||
import { findOverlappingTables } from '@/pages/editor-page/canvas/canvas-utils';
|
import { findOverlappingTables } from '@/pages/editor-page/canvas/canvas-utils';
|
||||||
import type { Graph } from '@/lib/graph';
|
import type { Graph } from '@/lib/graph';
|
||||||
import { createGraph } from '@/lib/graph';
|
import { createGraph } from '@/lib/graph';
|
||||||
|
import { useDiagramFilter } from '../diagram-filter-context/use-diagram-filter';
|
||||||
|
import { filterTable } from '@/lib/domain/diagram-filter/filter';
|
||||||
|
import { defaultSchemas } from '@/lib/data/default-schemas';
|
||||||
|
import {
|
||||||
|
CREATE_RELATIONSHIP_NODE_ID,
|
||||||
|
type CreateRelationshipNodeType,
|
||||||
|
} from '@/pages/editor-page/canvas/create-relationship-node/create-relationship-node';
|
||||||
|
|
||||||
interface CanvasProviderProps {
|
interface CanvasProviderProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CanvasProvider = ({ children }: CanvasProviderProps) => {
|
export const CanvasProvider = ({ children }: CanvasProviderProps) => {
|
||||||
const { tables, relationships, updateTablesState, filteredSchemas } =
|
const {
|
||||||
useChartDB();
|
tables,
|
||||||
const { fitView } = useReactFlow();
|
relationships,
|
||||||
|
updateTablesState,
|
||||||
|
databaseType,
|
||||||
|
areas,
|
||||||
|
diagramId,
|
||||||
|
} = useChartDB();
|
||||||
|
const { filter, loading: filterLoading } = useDiagramFilter();
|
||||||
|
const { fitView, screenToFlowPosition, setNodes } = useReactFlow();
|
||||||
const [overlapGraph, setOverlapGraph] =
|
const [overlapGraph, setOverlapGraph] =
|
||||||
useState<Graph<string>>(createGraph());
|
useState<Graph<string>>(createGraph());
|
||||||
|
const [editTableModeTable, setEditTableModeTable] = useState<{
|
||||||
|
tableId: string;
|
||||||
|
fieldId?: string;
|
||||||
|
} | null>(null);
|
||||||
|
|
||||||
const [showFilter, setShowFilter] = useState(false);
|
const [showFilter, setShowFilter] = useState(false);
|
||||||
|
|
||||||
|
const [tempFloatingEdge, setTempFloatingEdge] =
|
||||||
|
useState<CanvasContext['tempFloatingEdge']>(null);
|
||||||
|
|
||||||
|
const [hoveringTableId, setHoveringTableId] = useState<string | null>(null);
|
||||||
|
|
||||||
|
const diagramIdActiveFilterRef = useRef<string>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (filterLoading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (diagramIdActiveFilterRef.current === diagramId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
diagramIdActiveFilterRef.current = diagramId;
|
||||||
|
|
||||||
|
setShowFilter(true);
|
||||||
|
}, [filterLoading, diagramId]);
|
||||||
|
|
||||||
const reorderTables = useCallback(
|
const reorderTables = useCallback(
|
||||||
(
|
(
|
||||||
options: { updateHistory?: boolean } = {
|
options: { updateHistory?: boolean } = {
|
||||||
@@ -32,9 +75,19 @@ export const CanvasProvider = ({ children }: CanvasProviderProps) => {
|
|||||||
const newTables = adjustTablePositions({
|
const newTables = adjustTablePositions({
|
||||||
relationships,
|
relationships,
|
||||||
tables: tables.filter((table) =>
|
tables: tables.filter((table) =>
|
||||||
shouldShowTablesBySchemaFilter(table, filteredSchemas)
|
filterTable({
|
||||||
|
table: {
|
||||||
|
id: table.id,
|
||||||
|
schema: table.schema,
|
||||||
|
},
|
||||||
|
filter,
|
||||||
|
options: {
|
||||||
|
defaultSchema: defaultSchemas[databaseType],
|
||||||
|
},
|
||||||
|
})
|
||||||
),
|
),
|
||||||
mode: 'all', // Use 'all' mode for manual reordering
|
areas,
|
||||||
|
mode: 'all',
|
||||||
});
|
});
|
||||||
|
|
||||||
const updatedOverlapGraph = findOverlappingTables({
|
const updatedOverlapGraph = findOverlappingTables({
|
||||||
@@ -69,7 +122,75 @@ export const CanvasProvider = ({ children }: CanvasProviderProps) => {
|
|||||||
});
|
});
|
||||||
}, 500);
|
}, 500);
|
||||||
},
|
},
|
||||||
[filteredSchemas, relationships, tables, updateTablesState, fitView]
|
[
|
||||||
|
filter,
|
||||||
|
relationships,
|
||||||
|
tables,
|
||||||
|
updateTablesState,
|
||||||
|
fitView,
|
||||||
|
databaseType,
|
||||||
|
areas,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
const startFloatingEdgeCreation: CanvasContext['startFloatingEdgeCreation'] =
|
||||||
|
useCallback(({ sourceNodeId }) => {
|
||||||
|
setShowFilter(false);
|
||||||
|
setTempFloatingEdge({
|
||||||
|
sourceNodeId,
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const endFloatingEdgeCreation: CanvasContext['endFloatingEdgeCreation'] =
|
||||||
|
useCallback(() => {
|
||||||
|
setTempFloatingEdge(null);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const hideCreateRelationshipNode: CanvasContext['hideCreateRelationshipNode'] =
|
||||||
|
useCallback(() => {
|
||||||
|
setNodes((nds) =>
|
||||||
|
nds.filter((n) => n.id !== CREATE_RELATIONSHIP_NODE_ID)
|
||||||
|
);
|
||||||
|
endFloatingEdgeCreation();
|
||||||
|
}, [setNodes, endFloatingEdgeCreation]);
|
||||||
|
|
||||||
|
const showCreateRelationshipNode: CanvasContext['showCreateRelationshipNode'] =
|
||||||
|
useCallback(
|
||||||
|
({ sourceTableId, targetTableId, x, y }) => {
|
||||||
|
setTempFloatingEdge((edge) =>
|
||||||
|
edge
|
||||||
|
? {
|
||||||
|
...edge,
|
||||||
|
targetNodeId: targetTableId,
|
||||||
|
}
|
||||||
|
: null
|
||||||
|
);
|
||||||
|
const cursorPos = screenToFlowPosition({
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
});
|
||||||
|
|
||||||
|
const newNode: CreateRelationshipNodeType = {
|
||||||
|
id: CREATE_RELATIONSHIP_NODE_ID,
|
||||||
|
type: 'create-relationship',
|
||||||
|
position: cursorPos,
|
||||||
|
data: {
|
||||||
|
sourceTableId,
|
||||||
|
targetTableId,
|
||||||
|
},
|
||||||
|
draggable: true,
|
||||||
|
selectable: false,
|
||||||
|
zIndex: 1000,
|
||||||
|
};
|
||||||
|
|
||||||
|
setNodes((nds) => {
|
||||||
|
const nodesWithoutOldCreateRelationshipNode = nds.filter(
|
||||||
|
(n) => n.id !== CREATE_RELATIONSHIP_NODE_ID
|
||||||
|
);
|
||||||
|
return [...nodesWithoutOldCreateRelationshipNode, newNode];
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[screenToFlowPosition, setNodes]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -81,6 +202,16 @@ export const CanvasProvider = ({ children }: CanvasProviderProps) => {
|
|||||||
overlapGraph,
|
overlapGraph,
|
||||||
setShowFilter,
|
setShowFilter,
|
||||||
showFilter,
|
showFilter,
|
||||||
|
editTableModeTable,
|
||||||
|
setEditTableModeTable,
|
||||||
|
tempFloatingEdge: tempFloatingEdge,
|
||||||
|
setTempFloatingEdge: setTempFloatingEdge,
|
||||||
|
startFloatingEdgeCreation: startFloatingEdgeCreation,
|
||||||
|
endFloatingEdgeCreation: endFloatingEdgeCreation,
|
||||||
|
hoveringTableId,
|
||||||
|
setHoveringTableId,
|
||||||
|
showCreateRelationshipNode,
|
||||||
|
hideCreateRelationshipNode,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -81,9 +81,6 @@ export interface ChartDBContext {
|
|||||||
highlightedCustomType?: DBCustomType;
|
highlightedCustomType?: DBCustomType;
|
||||||
highlightCustomTypeId: (id?: string) => void;
|
highlightCustomTypeId: (id?: string) => void;
|
||||||
|
|
||||||
filteredSchemas?: string[];
|
|
||||||
filterSchemas: (schemaIds: string[]) => void;
|
|
||||||
|
|
||||||
// General operations
|
// General operations
|
||||||
updateDiagramId: (id: string) => Promise<void>;
|
updateDiagramId: (id: string) => Promise<void>;
|
||||||
updateDiagramName: (
|
updateDiagramName: (
|
||||||
@@ -284,11 +281,6 @@ export interface ChartDBContext {
|
|||||||
customType: Partial<DBCustomType>,
|
customType: Partial<DBCustomType>,
|
||||||
options?: { updateHistory: boolean }
|
options?: { updateHistory: boolean }
|
||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
|
|
||||||
// Filters
|
|
||||||
hiddenTableIds?: string[];
|
|
||||||
addHiddenTableId: (tableId: string) => Promise<void>;
|
|
||||||
removeHiddenTableId: (tableId: string) => Promise<void>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const chartDBContext = createContext<ChartDBContext>({
|
export const chartDBContext = createContext<ChartDBContext>({
|
||||||
@@ -302,8 +294,6 @@ export const chartDBContext = createContext<ChartDBContext>({
|
|||||||
customTypes: [],
|
customTypes: [],
|
||||||
schemas: [],
|
schemas: [],
|
||||||
highlightCustomTypeId: emptyFn,
|
highlightCustomTypeId: emptyFn,
|
||||||
filteredSchemas: [],
|
|
||||||
filterSchemas: emptyFn,
|
|
||||||
currentDiagram: {
|
currentDiagram: {
|
||||||
id: '',
|
id: '',
|
||||||
name: '',
|
name: '',
|
||||||
@@ -386,9 +376,4 @@ export const chartDBContext = createContext<ChartDBContext>({
|
|||||||
removeCustomType: emptyFn,
|
removeCustomType: emptyFn,
|
||||||
removeCustomTypes: emptyFn,
|
removeCustomTypes: emptyFn,
|
||||||
updateCustomType: emptyFn,
|
updateCustomType: emptyFn,
|
||||||
|
|
||||||
// Filters
|
|
||||||
hiddenTableIds: [],
|
|
||||||
addHiddenTableId: emptyFn,
|
|
||||||
removeHiddenTableId: emptyFn,
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
import React, { useCallback, useMemo, useState } from 'react';
|
||||||
import type { DBTable } from '@/lib/domain/db-table';
|
import type { DBTable } from '@/lib/domain/db-table';
|
||||||
import { deepCopy, generateId } from '@/lib/utils';
|
import { deepCopy, generateId } from '@/lib/utils';
|
||||||
import { randomColor } from '@/lib/colors';
|
import { defaultTableColor, defaultAreaColor, viewColor } from '@/lib/colors';
|
||||||
import type { ChartDBContext, ChartDBEvent } from './chartdb-context';
|
import type { ChartDBContext, ChartDBEvent } from './chartdb-context';
|
||||||
import { chartDBContext } from './chartdb-context';
|
import { chartDBContext } from './chartdb-context';
|
||||||
import { DatabaseType } from '@/lib/domain/database-type';
|
import { DatabaseType } from '@/lib/domain/database-type';
|
||||||
import type { DBField } from '@/lib/domain/db-field';
|
import type { DBField } from '@/lib/domain/db-field';
|
||||||
import type { DBIndex } from '@/lib/domain/db-index';
|
import {
|
||||||
|
getTableIndexesWithPrimaryKey,
|
||||||
|
type DBIndex,
|
||||||
|
} from '@/lib/domain/db-index';
|
||||||
import type { DBRelationship } from '@/lib/domain/db-relationship';
|
import type { DBRelationship } from '@/lib/domain/db-relationship';
|
||||||
import { useStorage } from '@/hooks/use-storage';
|
import { useStorage } from '@/hooks/use-storage';
|
||||||
import { useRedoUndoStack } from '@/hooks/use-redo-undo-stack';
|
import { useRedoUndoStack } from '@/hooks/use-redo-undo-stack';
|
||||||
@@ -17,7 +20,6 @@ import {
|
|||||||
databasesWithSchemas,
|
databasesWithSchemas,
|
||||||
schemaNameToSchemaId,
|
schemaNameToSchemaId,
|
||||||
} from '@/lib/domain/db-schema';
|
} from '@/lib/domain/db-schema';
|
||||||
import { useLocalConfig } from '@/hooks/use-local-config';
|
|
||||||
import { defaultSchemas } from '@/lib/data/default-schemas';
|
import { defaultSchemas } from '@/lib/data/default-schemas';
|
||||||
import { useEventEmitter } from 'ahooks';
|
import { useEventEmitter } from 'ahooks';
|
||||||
import type { DBDependency } from '@/lib/domain/db-dependency';
|
import type { DBDependency } from '@/lib/domain/db-dependency';
|
||||||
@@ -29,7 +31,6 @@ import {
|
|||||||
DBCustomTypeKind,
|
DBCustomTypeKind,
|
||||||
type DBCustomType,
|
type DBCustomType,
|
||||||
} from '@/lib/domain/db-custom-type';
|
} from '@/lib/domain/db-custom-type';
|
||||||
import { useConfig } from '@/hooks/use-config';
|
|
||||||
|
|
||||||
export interface ChartDBProviderProps {
|
export interface ChartDBProviderProps {
|
||||||
diagram?: Diagram;
|
diagram?: Diagram;
|
||||||
@@ -40,17 +41,11 @@ export const ChartDBProvider: React.FC<
|
|||||||
React.PropsWithChildren<ChartDBProviderProps>
|
React.PropsWithChildren<ChartDBProviderProps>
|
||||||
> = ({ children, diagram, readonly: readonlyProp }) => {
|
> = ({ children, diagram, readonly: readonlyProp }) => {
|
||||||
const { hasDiff } = useDiff();
|
const { hasDiff } = useDiff();
|
||||||
const dbStorage = useStorage();
|
const storageDB = useStorage();
|
||||||
let db = dbStorage;
|
|
||||||
const events = useEventEmitter<ChartDBEvent>();
|
const events = useEventEmitter<ChartDBEvent>();
|
||||||
const { setSchemasFilter, schemasFilter } = useLocalConfig();
|
|
||||||
const { addUndoAction, resetRedoStack, resetUndoStack } =
|
const { addUndoAction, resetRedoStack, resetUndoStack } =
|
||||||
useRedoUndoStack();
|
useRedoUndoStack();
|
||||||
const {
|
|
||||||
getHiddenTablesForDiagram,
|
|
||||||
hideTableForDiagram,
|
|
||||||
unhideTableForDiagram,
|
|
||||||
} = useConfig();
|
|
||||||
const [diagramId, setDiagramId] = useState('');
|
const [diagramId, setDiagramId] = useState('');
|
||||||
const [diagramName, setDiagramName] = useState('');
|
const [diagramName, setDiagramName] = useState('');
|
||||||
const [diagramCreatedAt, setDiagramCreatedAt] = useState<Date>(new Date());
|
const [diagramCreatedAt, setDiagramCreatedAt] = useState<Date>(new Date());
|
||||||
@@ -72,17 +67,17 @@ export const ChartDBProvider: React.FC<
|
|||||||
const [customTypes, setCustomTypes] = useState<DBCustomType[]>(
|
const [customTypes, setCustomTypes] = useState<DBCustomType[]>(
|
||||||
diagram?.customTypes ?? []
|
diagram?.customTypes ?? []
|
||||||
);
|
);
|
||||||
const [hiddenTableIds, setHiddenTableIds] = useState<string[]>([]);
|
|
||||||
const { events: diffEvents } = useDiff();
|
const { events: diffEvents } = useDiff();
|
||||||
|
|
||||||
const [highlightedCustomTypeId, setHighlightedCustomTypeId] =
|
const [highlightedCustomTypeId, setHighlightedCustomTypeId] =
|
||||||
useState<string>();
|
useState<string>();
|
||||||
|
|
||||||
const diffCalculatedHandler = useCallback((event: DiffCalculatedEvent) => {
|
const diffCalculatedHandler = useCallback((event: DiffCalculatedEvent) => {
|
||||||
const { tablesAdded, fieldsAdded, relationshipsAdded } = event.data;
|
const { tablesToAdd, fieldsToAdd, relationshipsToAdd } = event.data;
|
||||||
setTables((tables) =>
|
setTables((tables) =>
|
||||||
[...tables, ...(tablesAdded ?? [])].map((table) => {
|
[...tables, ...(tablesToAdd ?? [])].map((table) => {
|
||||||
const fields = fieldsAdded.get(table.id);
|
const fields = fieldsToAdd.get(table.id);
|
||||||
return fields
|
return fields
|
||||||
? { ...table, fields: [...table.fields, ...fields] }
|
? { ...table, fields: [...table.fields, ...fields] }
|
||||||
: table;
|
: table;
|
||||||
@@ -90,31 +85,22 @@ export const ChartDBProvider: React.FC<
|
|||||||
);
|
);
|
||||||
setRelationships((relationships) => [
|
setRelationships((relationships) => [
|
||||||
...relationships,
|
...relationships,
|
||||||
...(relationshipsAdded ?? []),
|
...(relationshipsToAdd ?? []),
|
||||||
]);
|
]);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
diffEvents.useSubscription(diffCalculatedHandler);
|
diffEvents.useSubscription(diffCalculatedHandler);
|
||||||
|
|
||||||
// Sync hiddenTableIds with config
|
const defaultSchemaName = useMemo(
|
||||||
useEffect(() => {
|
() => defaultSchemas[databaseType],
|
||||||
if (diagramId) {
|
[databaseType]
|
||||||
const hiddenTables = getHiddenTablesForDiagram(diagramId);
|
);
|
||||||
setHiddenTableIds(hiddenTables);
|
|
||||||
}
|
|
||||||
}, [diagramId, getHiddenTablesForDiagram]);
|
|
||||||
|
|
||||||
const defaultSchemaName = defaultSchemas[databaseType];
|
|
||||||
|
|
||||||
const readonly = useMemo(
|
const readonly = useMemo(
|
||||||
() => readonlyProp ?? hasDiff ?? false,
|
() => readonlyProp ?? hasDiff ?? false,
|
||||||
[readonlyProp, hasDiff]
|
[readonlyProp, hasDiff]
|
||||||
);
|
);
|
||||||
|
|
||||||
if (readonly) {
|
|
||||||
db = storageInitialValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const schemas = useMemo(
|
const schemas = useMemo(
|
||||||
() =>
|
() =>
|
||||||
databasesWithSchemas.includes(databaseType)
|
databasesWithSchemas.includes(databaseType)
|
||||||
@@ -125,9 +111,11 @@ export const ChartDBProvider: React.FC<
|
|||||||
.filter((schema) => !!schema) as string[]
|
.filter((schema) => !!schema) as string[]
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
.sort((a, b) =>
|
.sort((a, b) => {
|
||||||
a === defaultSchemaName ? -1 : a.localeCompare(b)
|
if (a === defaultSchemaName) return -1;
|
||||||
)
|
if (b === defaultSchemaName) return 1;
|
||||||
|
return a.localeCompare(b);
|
||||||
|
})
|
||||||
.map(
|
.map(
|
||||||
(schema): DBSchema => ({
|
(schema): DBSchema => ({
|
||||||
id: schemaNameToSchemaId(schema),
|
id: schemaNameToSchemaId(schema),
|
||||||
@@ -141,34 +129,11 @@ export const ChartDBProvider: React.FC<
|
|||||||
[tables, defaultSchemaName, databaseType]
|
[tables, defaultSchemaName, databaseType]
|
||||||
);
|
);
|
||||||
|
|
||||||
const filterSchemas: ChartDBContext['filterSchemas'] = useCallback(
|
const db = useMemo(
|
||||||
(schemaIds) => {
|
() => (readonly ? storageInitialValue : storageDB),
|
||||||
setSchemasFilter((prev) => ({
|
[storageDB, readonly]
|
||||||
...prev,
|
|
||||||
[diagramId]: schemaIds,
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
[diagramId, setSchemasFilter]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const filteredSchemas: ChartDBContext['filteredSchemas'] = useMemo(() => {
|
|
||||||
if (schemas.length === 0) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const schemasFilterFromCache =
|
|
||||||
(schemasFilter[diagramId] ?? []).length === 0
|
|
||||||
? undefined // in case of empty filter, skip cache
|
|
||||||
: schemasFilter[diagramId];
|
|
||||||
|
|
||||||
return (
|
|
||||||
schemasFilterFromCache ?? [
|
|
||||||
schemas.find((s) => s.name === defaultSchemaName)?.id ??
|
|
||||||
schemas[0]?.id,
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}, [schemasFilter, diagramId, schemas, defaultSchemaName]);
|
|
||||||
|
|
||||||
const currentDiagram: Diagram = useMemo(
|
const currentDiagram: Diagram = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
id: diagramId,
|
id: diagramId,
|
||||||
@@ -380,12 +345,18 @@ export const ChartDBProvider: React.FC<
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
indexes: [],
|
indexes: [],
|
||||||
color: randomColor(),
|
color: attributes?.isView ? viewColor : defaultTableColor,
|
||||||
createdAt: Date.now(),
|
createdAt: Date.now(),
|
||||||
isView: false,
|
isView: false,
|
||||||
order: tables.length,
|
order: tables.length,
|
||||||
...attributes,
|
...attributes,
|
||||||
|
schema: attributes?.schema ?? defaultSchemas[databaseType],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
table.indexes = getTableIndexesWithPrimaryKey({
|
||||||
|
table,
|
||||||
|
});
|
||||||
|
|
||||||
await addTable(table);
|
await addTable(table);
|
||||||
|
|
||||||
return table;
|
return table;
|
||||||
@@ -677,17 +648,30 @@ export const ChartDBProvider: React.FC<
|
|||||||
options = { updateHistory: true }
|
options = { updateHistory: true }
|
||||||
) => {
|
) => {
|
||||||
const prevField = getField(tableId, fieldId);
|
const prevField = getField(tableId, fieldId);
|
||||||
setTables((tables) =>
|
|
||||||
tables.map((table) =>
|
const updateTableFn = (table: DBTable) => {
|
||||||
table.id === tableId
|
const updatedTable: DBTable = {
|
||||||
? {
|
|
||||||
...table,
|
...table,
|
||||||
fields: table.fields.map((f) =>
|
fields: table.fields.map((f) =>
|
||||||
f.id === fieldId ? { ...f, ...field } : f
|
f.id === fieldId ? { ...f, ...field } : f
|
||||||
),
|
),
|
||||||
|
} satisfies DBTable;
|
||||||
|
|
||||||
|
updatedTable.indexes = getTableIndexesWithPrimaryKey({
|
||||||
|
table: updatedTable,
|
||||||
|
});
|
||||||
|
|
||||||
|
return updatedTable;
|
||||||
|
};
|
||||||
|
|
||||||
|
setTables((tables) =>
|
||||||
|
tables.map((table) => {
|
||||||
|
if (table.id === tableId) {
|
||||||
|
return updateTableFn(table);
|
||||||
}
|
}
|
||||||
: table
|
|
||||||
)
|
return table;
|
||||||
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const table = await db.getTable({ diagramId, id: tableId });
|
const table = await db.getTable({ diagramId, id: tableId });
|
||||||
@@ -702,10 +686,7 @@ export const ChartDBProvider: React.FC<
|
|||||||
db.updateTable({
|
db.updateTable({
|
||||||
id: tableId,
|
id: tableId,
|
||||||
attributes: {
|
attributes: {
|
||||||
...table,
|
...updateTableFn(table),
|
||||||
fields: table.fields.map((f) =>
|
|
||||||
f.id === fieldId ? { ...f, ...field } : f
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
@@ -732,19 +713,29 @@ export const ChartDBProvider: React.FC<
|
|||||||
fieldId: string,
|
fieldId: string,
|
||||||
options = { updateHistory: true }
|
options = { updateHistory: true }
|
||||||
) => {
|
) => {
|
||||||
|
const updateTableFn = (table: DBTable) => {
|
||||||
|
const updatedTable: DBTable = {
|
||||||
|
...table,
|
||||||
|
fields: table.fields.filter((f) => f.id !== fieldId),
|
||||||
|
} satisfies DBTable;
|
||||||
|
|
||||||
|
updatedTable.indexes = getTableIndexesWithPrimaryKey({
|
||||||
|
table: updatedTable,
|
||||||
|
});
|
||||||
|
|
||||||
|
return updatedTable;
|
||||||
|
};
|
||||||
|
|
||||||
const fields = getTable(tableId)?.fields ?? [];
|
const fields = getTable(tableId)?.fields ?? [];
|
||||||
const prevField = getField(tableId, fieldId);
|
const prevField = getField(tableId, fieldId);
|
||||||
setTables((tables) =>
|
setTables((tables) =>
|
||||||
tables.map((table) =>
|
tables.map((table) => {
|
||||||
table.id === tableId
|
if (table.id === tableId) {
|
||||||
? {
|
return updateTableFn(table);
|
||||||
...table,
|
|
||||||
fields: table.fields.filter(
|
|
||||||
(f) => f.id !== fieldId
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
: table
|
|
||||||
)
|
return table;
|
||||||
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
events.emit({
|
events.emit({
|
||||||
@@ -768,8 +759,7 @@ export const ChartDBProvider: React.FC<
|
|||||||
db.updateTable({
|
db.updateTable({
|
||||||
id: tableId,
|
id: tableId,
|
||||||
attributes: {
|
attributes: {
|
||||||
...table,
|
...updateTableFn(table),
|
||||||
fields: table.fields.filter((f) => f.id !== fieldId),
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
@@ -1125,12 +1115,15 @@ export const ChartDBProvider: React.FC<
|
|||||||
|
|
||||||
const sourceFieldName = sourceField?.name ?? '';
|
const sourceFieldName = sourceField?.name ?? '';
|
||||||
|
|
||||||
|
const targetTable = getTable(targetTableId);
|
||||||
|
const targetTableSchema = targetTable?.schema;
|
||||||
|
|
||||||
const relationship: DBRelationship = {
|
const relationship: DBRelationship = {
|
||||||
id: generateId(),
|
id: generateId(),
|
||||||
name: `${sourceTableName}_${sourceFieldName}_fk`,
|
name: `${sourceTableName}_${sourceFieldName}_fk`,
|
||||||
sourceSchema: sourceTable?.schema,
|
sourceSchema: sourceTable?.schema,
|
||||||
sourceTableId,
|
sourceTableId,
|
||||||
targetSchema: sourceTable?.schema,
|
targetSchema: targetTableSchema,
|
||||||
targetTableId,
|
targetTableId,
|
||||||
sourceFieldId,
|
sourceFieldId,
|
||||||
targetFieldId,
|
targetFieldId,
|
||||||
@@ -1452,7 +1445,7 @@ export const ChartDBProvider: React.FC<
|
|||||||
y: 0,
|
y: 0,
|
||||||
width: 300,
|
width: 300,
|
||||||
height: 200,
|
height: 200,
|
||||||
color: randomColor(),
|
color: defaultAreaColor,
|
||||||
...attributes,
|
...attributes,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1588,17 +1581,17 @@ export const ChartDBProvider: React.FC<
|
|||||||
|
|
||||||
const updateDiagramData: ChartDBContext['updateDiagramData'] = useCallback(
|
const updateDiagramData: ChartDBContext['updateDiagramData'] = useCallback(
|
||||||
async (diagram, options) => {
|
async (diagram, options) => {
|
||||||
const st = options?.forceUpdateStorage ? dbStorage : db;
|
const st = options?.forceUpdateStorage ? storageDB : db;
|
||||||
await st.deleteDiagram(diagram.id);
|
await st.deleteDiagram(diagram.id);
|
||||||
await st.addDiagram({ diagram });
|
await st.addDiagram({ diagram });
|
||||||
loadDiagramFromData(diagram);
|
loadDiagramFromData(diagram);
|
||||||
},
|
},
|
||||||
[db, dbStorage, loadDiagramFromData]
|
[db, storageDB, loadDiagramFromData]
|
||||||
);
|
);
|
||||||
|
|
||||||
const loadDiagram: ChartDBContext['loadDiagram'] = useCallback(
|
const loadDiagram: ChartDBContext['loadDiagram'] = useCallback(
|
||||||
async (diagramId: string) => {
|
async (diagramId: string) => {
|
||||||
const diagram = await db.getDiagram(diagramId, {
|
const diagram = await storageDB.getDiagram(diagramId, {
|
||||||
includeRelationships: true,
|
includeRelationships: true,
|
||||||
includeTables: true,
|
includeTables: true,
|
||||||
includeDependencies: true,
|
includeDependencies: true,
|
||||||
@@ -1612,7 +1605,7 @@ export const ChartDBProvider: React.FC<
|
|||||||
|
|
||||||
return diagram;
|
return diagram;
|
||||||
},
|
},
|
||||||
[db, loadDiagramFromData]
|
[storageDB, loadDiagramFromData]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Custom type operations
|
// Custom type operations
|
||||||
@@ -1759,29 +1752,6 @@ export const ChartDBProvider: React.FC<
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
const addHiddenTableId: ChartDBContext['addHiddenTableId'] = useCallback(
|
|
||||||
async (tableId: string) => {
|
|
||||||
if (!hiddenTableIds.includes(tableId)) {
|
|
||||||
setHiddenTableIds((prev) => [...prev, tableId]);
|
|
||||||
await hideTableForDiagram(diagramId, tableId);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[hiddenTableIds, diagramId, hideTableForDiagram]
|
|
||||||
);
|
|
||||||
|
|
||||||
const removeHiddenTableId: ChartDBContext['removeHiddenTableId'] =
|
|
||||||
useCallback(
|
|
||||||
async (tableId: string) => {
|
|
||||||
if (hiddenTableIds.includes(tableId)) {
|
|
||||||
setHiddenTableIds((prev) =>
|
|
||||||
prev.filter((id) => id !== tableId)
|
|
||||||
);
|
|
||||||
await unhideTableForDiagram(diagramId, tableId);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[hiddenTableIds, diagramId, unhideTableForDiagram]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<chartDBContext.Provider
|
<chartDBContext.Provider
|
||||||
value={{
|
value={{
|
||||||
@@ -1794,10 +1764,8 @@ export const ChartDBProvider: React.FC<
|
|||||||
areas,
|
areas,
|
||||||
currentDiagram,
|
currentDiagram,
|
||||||
schemas,
|
schemas,
|
||||||
filteredSchemas,
|
|
||||||
events,
|
events,
|
||||||
readonly,
|
readonly,
|
||||||
filterSchemas,
|
|
||||||
updateDiagramData,
|
updateDiagramData,
|
||||||
updateDiagramId,
|
updateDiagramId,
|
||||||
updateDiagramName,
|
updateDiagramName,
|
||||||
@@ -1855,9 +1823,6 @@ export const ChartDBProvider: React.FC<
|
|||||||
removeCustomType,
|
removeCustomType,
|
||||||
removeCustomTypes,
|
removeCustomTypes,
|
||||||
updateCustomType,
|
updateCustomType,
|
||||||
hiddenTableIds,
|
|
||||||
addHiddenTableId,
|
|
||||||
removeHiddenTableId,
|
|
||||||
highlightCustomTypeId,
|
highlightCustomTypeId,
|
||||||
highlightedCustomType,
|
highlightedCustomType,
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -8,23 +8,9 @@ export interface ConfigContext {
|
|||||||
config?: Partial<ChartDBConfig>;
|
config?: Partial<ChartDBConfig>;
|
||||||
updateFn?: (config: ChartDBConfig) => ChartDBConfig;
|
updateFn?: (config: ChartDBConfig) => ChartDBConfig;
|
||||||
}) => Promise<void>;
|
}) => Promise<void>;
|
||||||
getHiddenTablesForDiagram: (diagramId: string) => string[];
|
|
||||||
setHiddenTablesForDiagram: (
|
|
||||||
diagramId: string,
|
|
||||||
hiddenTableIds: string[]
|
|
||||||
) => Promise<void>;
|
|
||||||
hideTableForDiagram: (diagramId: string, tableId: string) => Promise<void>;
|
|
||||||
unhideTableForDiagram: (
|
|
||||||
diagramId: string,
|
|
||||||
tableId: string
|
|
||||||
) => Promise<void>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ConfigContext = createContext<ConfigContext>({
|
export const ConfigContext = createContext<ConfigContext>({
|
||||||
config: undefined,
|
config: undefined,
|
||||||
updateConfig: emptyFn,
|
updateConfig: emptyFn,
|
||||||
getHiddenTablesForDiagram: () => [],
|
|
||||||
setHiddenTablesForDiagram: emptyFn,
|
|
||||||
hideTableForDiagram: emptyFn,
|
|
||||||
unhideTableForDiagram: emptyFn,
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { ConfigContext } from './config-context';
|
import { ConfigContext } from './config-context';
|
||||||
|
|
||||||
import { useStorage } from '@/hooks/use-storage';
|
import { useStorage } from '@/hooks/use-storage';
|
||||||
@@ -8,7 +8,7 @@ export const ConfigProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
children,
|
children,
|
||||||
}) => {
|
}) => {
|
||||||
const { getConfig, updateConfig: updateDataConfig } = useStorage();
|
const { getConfig, updateConfig: updateDataConfig } = useStorage();
|
||||||
const [config, setConfig] = React.useState<ChartDBConfig | undefined>();
|
const [config, setConfig] = useState<ChartDBConfig | undefined>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loadConfig = async () => {
|
const loadConfig = async () => {
|
||||||
@@ -44,84 +44,11 @@ export const ConfigProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
return promise;
|
return promise;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getHiddenTablesForDiagram = (diagramId: string): string[] => {
|
|
||||||
return config?.hiddenTablesByDiagram?.[diagramId] ?? [];
|
|
||||||
};
|
|
||||||
|
|
||||||
const setHiddenTablesForDiagram = async (
|
|
||||||
diagramId: string,
|
|
||||||
hiddenTableIds: string[]
|
|
||||||
): Promise<void> => {
|
|
||||||
return updateConfig({
|
|
||||||
updateFn: (currentConfig) => ({
|
|
||||||
...currentConfig,
|
|
||||||
hiddenTablesByDiagram: {
|
|
||||||
...currentConfig.hiddenTablesByDiagram,
|
|
||||||
[diagramId]: hiddenTableIds,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const hideTableForDiagram = async (
|
|
||||||
diagramId: string,
|
|
||||||
tableId: string
|
|
||||||
): Promise<void> => {
|
|
||||||
return updateConfig({
|
|
||||||
updateFn: (currentConfig) => {
|
|
||||||
const currentHiddenTables =
|
|
||||||
currentConfig.hiddenTablesByDiagram?.[diagramId] ?? [];
|
|
||||||
if (currentHiddenTables.includes(tableId)) {
|
|
||||||
return currentConfig; // Already hidden, no change needed
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...currentConfig,
|
|
||||||
hiddenTablesByDiagram: {
|
|
||||||
...currentConfig.hiddenTablesByDiagram,
|
|
||||||
[diagramId]: [...currentHiddenTables, tableId],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const unhideTableForDiagram = async (
|
|
||||||
diagramId: string,
|
|
||||||
tableId: string
|
|
||||||
): Promise<void> => {
|
|
||||||
return updateConfig({
|
|
||||||
updateFn: (currentConfig) => {
|
|
||||||
const currentHiddenTables =
|
|
||||||
currentConfig.hiddenTablesByDiagram?.[diagramId] ?? [];
|
|
||||||
const filteredTables = currentHiddenTables.filter(
|
|
||||||
(id) => id !== tableId
|
|
||||||
);
|
|
||||||
|
|
||||||
if (filteredTables.length === currentHiddenTables.length) {
|
|
||||||
return currentConfig; // Not hidden, no change needed
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...currentConfig,
|
|
||||||
hiddenTablesByDiagram: {
|
|
||||||
...currentConfig.hiddenTablesByDiagram,
|
|
||||||
[diagramId]: filteredTables,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ConfigContext.Provider
|
<ConfigContext.Provider
|
||||||
value={{
|
value={{
|
||||||
config,
|
config,
|
||||||
updateConfig,
|
updateConfig,
|
||||||
getHiddenTablesForDiagram,
|
|
||||||
setHiddenTablesForDiagram,
|
|
||||||
hideTableForDiagram,
|
|
||||||
unhideTableForDiagram,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import type { DBSchema } from '@/lib/domain';
|
||||||
|
import type {
|
||||||
|
DiagramFilter,
|
||||||
|
FilterTableInfo,
|
||||||
|
} from '@/lib/domain/diagram-filter/diagram-filter';
|
||||||
|
import { emptyFn } from '@/lib/utils';
|
||||||
|
import { createContext } from 'react';
|
||||||
|
|
||||||
|
export interface DiagramFilterContext {
|
||||||
|
filter?: DiagramFilter;
|
||||||
|
loading: boolean;
|
||||||
|
|
||||||
|
hasActiveFilter: boolean;
|
||||||
|
schemasDisplayed: DBSchema[];
|
||||||
|
|
||||||
|
clearSchemaIdsFilter: () => void;
|
||||||
|
clearTableIdsFilter: () => void;
|
||||||
|
|
||||||
|
setTableIdsFilterEmpty: () => void;
|
||||||
|
|
||||||
|
// reset
|
||||||
|
resetFilter: () => void;
|
||||||
|
|
||||||
|
toggleSchemaFilter: (schemaId: string) => void;
|
||||||
|
toggleTableFilter: (tableId: string) => void;
|
||||||
|
addSchemaToFilter: (schemaId: string) => void;
|
||||||
|
addTablesToFilter: (attrs: {
|
||||||
|
tableIds?: string[];
|
||||||
|
filterCallback?: (table: FilterTableInfo) => boolean;
|
||||||
|
}) => void;
|
||||||
|
removeTablesFromFilter: (attrs: {
|
||||||
|
tableIds?: string[];
|
||||||
|
filterCallback?: (table: FilterTableInfo) => boolean;
|
||||||
|
}) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const diagramFilterContext = createContext<DiagramFilterContext>({
|
||||||
|
hasActiveFilter: false,
|
||||||
|
clearSchemaIdsFilter: emptyFn,
|
||||||
|
clearTableIdsFilter: emptyFn,
|
||||||
|
setTableIdsFilterEmpty: emptyFn,
|
||||||
|
resetFilter: emptyFn,
|
||||||
|
toggleSchemaFilter: emptyFn,
|
||||||
|
toggleTableFilter: emptyFn,
|
||||||
|
addSchemaToFilter: emptyFn,
|
||||||
|
schemasDisplayed: [],
|
||||||
|
addTablesToFilter: emptyFn,
|
||||||
|
removeTablesFromFilter: emptyFn,
|
||||||
|
loading: false,
|
||||||
|
});
|
||||||
559
src/context/diagram-filter-context/diagram-filter-provider.tsx
Normal file
559
src/context/diagram-filter-context/diagram-filter-provider.tsx
Normal file
@@ -0,0 +1,559 @@
|
|||||||
|
import React, {
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from 'react';
|
||||||
|
import type { DiagramFilterContext } from './diagram-filter-context';
|
||||||
|
import { diagramFilterContext } from './diagram-filter-context';
|
||||||
|
import type {
|
||||||
|
DiagramFilter,
|
||||||
|
FilterTableInfo,
|
||||||
|
} from '@/lib/domain/diagram-filter/diagram-filter';
|
||||||
|
import {
|
||||||
|
reduceFilter,
|
||||||
|
spreadFilterTables,
|
||||||
|
} from '@/lib/domain/diagram-filter/diagram-filter';
|
||||||
|
import { useStorage } from '@/hooks/use-storage';
|
||||||
|
import { useChartDB } from '@/hooks/use-chartdb';
|
||||||
|
import { filterTable } from '@/lib/domain/diagram-filter/filter';
|
||||||
|
import { databasesWithSchemas, schemaNameToSchemaId } from '@/lib/domain';
|
||||||
|
import { defaultSchemas } from '@/lib/data/default-schemas';
|
||||||
|
import type { ChartDBEvent } from '../chartdb-context/chartdb-context';
|
||||||
|
|
||||||
|
export const DiagramFilterProvider: React.FC<React.PropsWithChildren> = ({
|
||||||
|
children,
|
||||||
|
}) => {
|
||||||
|
const { diagramId, tables, schemas, databaseType, events } = useChartDB();
|
||||||
|
const { getDiagramFilter, updateDiagramFilter } = useStorage();
|
||||||
|
const [filter, setFilter] = useState<DiagramFilter>({});
|
||||||
|
const [loading, setLoading] = useState<boolean>(true);
|
||||||
|
|
||||||
|
const allSchemasIds = useMemo(() => {
|
||||||
|
return schemas.map((schema) => schema.id);
|
||||||
|
}, [schemas]);
|
||||||
|
|
||||||
|
const allTables: FilterTableInfo[] = useMemo(() => {
|
||||||
|
return tables.map(
|
||||||
|
(table) =>
|
||||||
|
({
|
||||||
|
id: table.id,
|
||||||
|
schemaId: table.schema
|
||||||
|
? schemaNameToSchemaId(table.schema)
|
||||||
|
: defaultSchemas[databaseType],
|
||||||
|
schema: table.schema ?? defaultSchemas[databaseType],
|
||||||
|
areaId: table.parentAreaId ?? undefined,
|
||||||
|
}) satisfies FilterTableInfo
|
||||||
|
);
|
||||||
|
}, [tables, databaseType]);
|
||||||
|
|
||||||
|
const diagramIdOfLoadedFilter = useRef<string | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (diagramId && diagramId === diagramIdOfLoadedFilter.current) {
|
||||||
|
updateDiagramFilter(diagramId, filter);
|
||||||
|
}
|
||||||
|
}, [diagramId, filter, updateDiagramFilter]);
|
||||||
|
|
||||||
|
// Reset filter when diagram changes
|
||||||
|
useEffect(() => {
|
||||||
|
if (diagramIdOfLoadedFilter.current === diagramId) {
|
||||||
|
// If the diagramId hasn't changed, do not reset the filter
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
|
const loadFilterFromStorage = async (diagramId: string) => {
|
||||||
|
if (diagramId) {
|
||||||
|
const storedFilter = await getDiagramFilter(diagramId);
|
||||||
|
|
||||||
|
let filterToSet = storedFilter;
|
||||||
|
|
||||||
|
if (!filterToSet) {
|
||||||
|
// If no filter is stored, set default based on database type
|
||||||
|
filterToSet =
|
||||||
|
schemas.length > 1
|
||||||
|
? { schemaIds: [schemas[0].id] }
|
||||||
|
: {};
|
||||||
|
}
|
||||||
|
|
||||||
|
setFilter(filterToSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
setFilter({});
|
||||||
|
|
||||||
|
if (diagramId) {
|
||||||
|
loadFilterFromStorage(diagramId);
|
||||||
|
diagramIdOfLoadedFilter.current = diagramId;
|
||||||
|
}
|
||||||
|
}, [diagramId, getDiagramFilter, schemas]);
|
||||||
|
|
||||||
|
const clearSchemaIds: DiagramFilterContext['clearSchemaIdsFilter'] =
|
||||||
|
useCallback(() => {
|
||||||
|
setFilter(
|
||||||
|
(prev) =>
|
||||||
|
({
|
||||||
|
...prev,
|
||||||
|
schemaIds: undefined,
|
||||||
|
}) satisfies DiagramFilter
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const clearTableIds: DiagramFilterContext['clearTableIdsFilter'] =
|
||||||
|
useCallback(() => {
|
||||||
|
setFilter(
|
||||||
|
(prev) =>
|
||||||
|
({
|
||||||
|
...prev,
|
||||||
|
tableIds: undefined,
|
||||||
|
}) satisfies DiagramFilter
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const setTableIdsEmpty: DiagramFilterContext['setTableIdsFilterEmpty'] =
|
||||||
|
useCallback(() => {
|
||||||
|
setFilter(
|
||||||
|
(prev) =>
|
||||||
|
({
|
||||||
|
...prev,
|
||||||
|
tableIds: [],
|
||||||
|
}) satisfies DiagramFilter
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Reset filter
|
||||||
|
const resetFilter: DiagramFilterContext['resetFilter'] = useCallback(() => {
|
||||||
|
setFilter({});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const toggleSchemaFilter: DiagramFilterContext['toggleSchemaFilter'] =
|
||||||
|
useCallback(
|
||||||
|
(schemaId: string) => {
|
||||||
|
setFilter((prev) => {
|
||||||
|
const currentSchemaIds = prev.schemaIds;
|
||||||
|
|
||||||
|
// Check if schema is currently visible
|
||||||
|
const isSchemaVisible = !allTables.some(
|
||||||
|
(table) =>
|
||||||
|
table.schemaId === schemaId &&
|
||||||
|
filterTable({
|
||||||
|
table: {
|
||||||
|
id: table.id,
|
||||||
|
schema: table.schema,
|
||||||
|
},
|
||||||
|
filter: prev,
|
||||||
|
options: {
|
||||||
|
defaultSchema: defaultSchemas[databaseType],
|
||||||
|
},
|
||||||
|
}) === false
|
||||||
|
);
|
||||||
|
|
||||||
|
let newSchemaIds: string[] | undefined;
|
||||||
|
let newTableIds: string[] | undefined = prev.tableIds;
|
||||||
|
|
||||||
|
if (isSchemaVisible) {
|
||||||
|
// Schema is visible, make it not visible
|
||||||
|
if (!currentSchemaIds) {
|
||||||
|
// All schemas are visible, create filter with all except this one
|
||||||
|
newSchemaIds = allSchemasIds.filter(
|
||||||
|
(id) => id !== schemaId
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Remove this schema from the filter
|
||||||
|
newSchemaIds = currentSchemaIds.filter(
|
||||||
|
(id) => id !== schemaId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove tables from this schema from tableIds if present
|
||||||
|
if (prev.tableIds) {
|
||||||
|
const schemaTableIds = allTables
|
||||||
|
.filter((table) => table.schemaId === schemaId)
|
||||||
|
.map((table) => table.id);
|
||||||
|
newTableIds = prev.tableIds.filter(
|
||||||
|
(id) => !schemaTableIds.includes(id)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Schema is not visible, make it visible
|
||||||
|
newSchemaIds = [
|
||||||
|
...new Set([...(currentSchemaIds || []), schemaId]),
|
||||||
|
];
|
||||||
|
|
||||||
|
// Add tables from this schema to tableIds if tableIds is defined
|
||||||
|
if (prev.tableIds) {
|
||||||
|
const schemaTableIds = allTables
|
||||||
|
.filter((table) => table.schemaId === schemaId)
|
||||||
|
.map((table) => table.id);
|
||||||
|
newTableIds = [
|
||||||
|
...new Set([
|
||||||
|
...prev.tableIds,
|
||||||
|
...schemaTableIds,
|
||||||
|
]),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use reduceFilter to optimize and handle edge cases
|
||||||
|
return reduceFilter(
|
||||||
|
{
|
||||||
|
schemaIds: newSchemaIds,
|
||||||
|
tableIds: newTableIds,
|
||||||
|
},
|
||||||
|
allTables satisfies FilterTableInfo[],
|
||||||
|
{
|
||||||
|
databaseWithSchemas:
|
||||||
|
databasesWithSchemas.includes(databaseType),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[allSchemasIds, allTables, databaseType]
|
||||||
|
);
|
||||||
|
|
||||||
|
const toggleTableFilterForNoSchema = useCallback(
|
||||||
|
(tableId: string) => {
|
||||||
|
setFilter((prev) => {
|
||||||
|
const currentTableIds = prev.tableIds;
|
||||||
|
|
||||||
|
// Check if table is currently visible
|
||||||
|
const isTableVisible = filterTable({
|
||||||
|
table: { id: tableId, schema: undefined },
|
||||||
|
filter: prev,
|
||||||
|
options: { defaultSchema: undefined },
|
||||||
|
});
|
||||||
|
|
||||||
|
let newTableIds: string[] | undefined;
|
||||||
|
|
||||||
|
if (isTableVisible) {
|
||||||
|
// Table is visible, make it not visible
|
||||||
|
if (!currentTableIds) {
|
||||||
|
// All tables are visible, create filter with all except this one
|
||||||
|
newTableIds = allTables
|
||||||
|
.filter((t) => t.id !== tableId)
|
||||||
|
.map((t) => t.id);
|
||||||
|
} else {
|
||||||
|
// Remove this table from the filter
|
||||||
|
newTableIds = currentTableIds.filter(
|
||||||
|
(id) => id !== tableId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Table is not visible, make it visible
|
||||||
|
newTableIds = [
|
||||||
|
...new Set([...(currentTableIds || []), tableId]),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use reduceFilter to optimize and handle edge cases
|
||||||
|
return reduceFilter(
|
||||||
|
{
|
||||||
|
schemaIds: undefined,
|
||||||
|
tableIds: newTableIds,
|
||||||
|
},
|
||||||
|
allTables satisfies FilterTableInfo[],
|
||||||
|
{
|
||||||
|
databaseWithSchemas:
|
||||||
|
databasesWithSchemas.includes(databaseType),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[allTables, databaseType]
|
||||||
|
);
|
||||||
|
|
||||||
|
const toggleTableFilter: DiagramFilterContext['toggleTableFilter'] =
|
||||||
|
useCallback(
|
||||||
|
(tableId: string) => {
|
||||||
|
if (!databasesWithSchemas.includes(databaseType)) {
|
||||||
|
// No schemas, toggle table filter without schema context
|
||||||
|
toggleTableFilterForNoSchema(tableId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setFilter((prev) => {
|
||||||
|
// Find the table in the tables list
|
||||||
|
const tableInfo = allTables.find((t) => t.id === tableId);
|
||||||
|
|
||||||
|
if (!tableInfo) {
|
||||||
|
return prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if table is currently visible using filterTable
|
||||||
|
const isTableVisible = filterTable({
|
||||||
|
table: {
|
||||||
|
id: tableInfo.id,
|
||||||
|
schema: tableInfo.schema,
|
||||||
|
},
|
||||||
|
filter: prev,
|
||||||
|
options: {
|
||||||
|
defaultSchema: defaultSchemas[databaseType],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let newSchemaIds = prev.schemaIds;
|
||||||
|
let newTableIds = prev.tableIds;
|
||||||
|
|
||||||
|
if (isTableVisible) {
|
||||||
|
// Table is visible, make it not visible
|
||||||
|
|
||||||
|
// If the table is visible due to its schema being in schemaIds
|
||||||
|
if (
|
||||||
|
tableInfo?.schemaId &&
|
||||||
|
prev.schemaIds?.includes(tableInfo.schemaId)
|
||||||
|
) {
|
||||||
|
// Remove the schema from schemaIds and add all other tables from that schema to tableIds
|
||||||
|
newSchemaIds = prev.schemaIds.filter(
|
||||||
|
(id) => id !== tableInfo.schemaId
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get all other tables from this schema (except the one being toggled)
|
||||||
|
const otherTablesFromSchema = allTables
|
||||||
|
.filter(
|
||||||
|
(t) =>
|
||||||
|
t.schemaId === tableInfo.schemaId &&
|
||||||
|
t.id !== tableId
|
||||||
|
)
|
||||||
|
.map((t) => t.id);
|
||||||
|
|
||||||
|
// Add these tables to tableIds
|
||||||
|
newTableIds = [
|
||||||
|
...(prev.tableIds || []),
|
||||||
|
...otherTablesFromSchema,
|
||||||
|
];
|
||||||
|
} else if (prev.tableIds?.includes(tableId)) {
|
||||||
|
// Table is visible because it's in tableIds, remove it
|
||||||
|
newTableIds = prev.tableIds.filter(
|
||||||
|
(id) => id !== tableId
|
||||||
|
);
|
||||||
|
} else if (!prev.tableIds && !prev.schemaIds) {
|
||||||
|
// No filters = all visible, create filter with all tables except this one
|
||||||
|
newTableIds = allTables
|
||||||
|
.filter((t) => t.id !== tableId)
|
||||||
|
.map((t) => t.id);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Table is not visible, make it visible by adding to tableIds
|
||||||
|
newTableIds = [...(prev.tableIds || []), tableId];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use reduceFilter to optimize and handle edge cases
|
||||||
|
return reduceFilter(
|
||||||
|
{
|
||||||
|
schemaIds: newSchemaIds,
|
||||||
|
tableIds: newTableIds,
|
||||||
|
},
|
||||||
|
allTables satisfies FilterTableInfo[],
|
||||||
|
{
|
||||||
|
databaseWithSchemas:
|
||||||
|
databasesWithSchemas.includes(databaseType),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[allTables, databaseType, toggleTableFilterForNoSchema]
|
||||||
|
);
|
||||||
|
|
||||||
|
const addSchemaToFilter: DiagramFilterContext['addSchemaToFilter'] =
|
||||||
|
useCallback(
|
||||||
|
(schemaId: string) => {
|
||||||
|
setFilter((prev) => {
|
||||||
|
const currentSchemaIds = prev.schemaIds;
|
||||||
|
if (!currentSchemaIds) {
|
||||||
|
// No schemas are filtered
|
||||||
|
return prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If schema is already filtered, do nothing
|
||||||
|
if (currentSchemaIds.includes(schemaId)) {
|
||||||
|
return prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add schema to the filter
|
||||||
|
const newSchemaIds = [...currentSchemaIds, schemaId];
|
||||||
|
|
||||||
|
if (newSchemaIds.length === allSchemasIds.length) {
|
||||||
|
// All schemas are now filtered, set to undefined
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
schemaIds: undefined,
|
||||||
|
} satisfies DiagramFilter;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
schemaIds: newSchemaIds,
|
||||||
|
} satisfies DiagramFilter;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[allSchemasIds.length]
|
||||||
|
);
|
||||||
|
|
||||||
|
const hasActiveFilter: boolean = useMemo(() => {
|
||||||
|
return !!filter.schemaIds || !!filter.tableIds;
|
||||||
|
}, [filter]);
|
||||||
|
|
||||||
|
const schemasDisplayed: DiagramFilterContext['schemasDisplayed'] =
|
||||||
|
useMemo(() => {
|
||||||
|
if (!hasActiveFilter) {
|
||||||
|
return schemas;
|
||||||
|
}
|
||||||
|
|
||||||
|
const displayedSchemaIds = new Set<string>();
|
||||||
|
for (const table of allTables) {
|
||||||
|
if (
|
||||||
|
filterTable({
|
||||||
|
table: {
|
||||||
|
id: table.id,
|
||||||
|
schema: table.schema,
|
||||||
|
},
|
||||||
|
filter,
|
||||||
|
options: {
|
||||||
|
defaultSchema: defaultSchemas[databaseType],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
) {
|
||||||
|
if (table.schemaId) {
|
||||||
|
displayedSchemaIds.add(table.schemaId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return schemas.filter((schema) =>
|
||||||
|
displayedSchemaIds.has(schema.id)
|
||||||
|
);
|
||||||
|
}, [hasActiveFilter, schemas, allTables, filter, databaseType]);
|
||||||
|
|
||||||
|
const addTablesToFilter: DiagramFilterContext['addTablesToFilter'] =
|
||||||
|
useCallback(
|
||||||
|
({ tableIds, filterCallback }) => {
|
||||||
|
setFilter((prev) => {
|
||||||
|
let tableIdsToAdd: string[];
|
||||||
|
|
||||||
|
if (tableIds) {
|
||||||
|
// If tableIds are provided, use them directly
|
||||||
|
tableIdsToAdd = tableIds;
|
||||||
|
} else if (filterCallback) {
|
||||||
|
// If filterCallback is provided, filter tables based on it
|
||||||
|
tableIdsToAdd = allTables
|
||||||
|
.filter(filterCallback)
|
||||||
|
.map((table) => table.id);
|
||||||
|
} else {
|
||||||
|
// If neither is provided, do nothing
|
||||||
|
return prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
const filterByTableIds = spreadFilterTables(
|
||||||
|
prev,
|
||||||
|
allTables satisfies FilterTableInfo[]
|
||||||
|
);
|
||||||
|
|
||||||
|
const currentTableIds = filterByTableIds.tableIds || [];
|
||||||
|
const newTableIds = [
|
||||||
|
...new Set([...currentTableIds, ...tableIdsToAdd]),
|
||||||
|
];
|
||||||
|
|
||||||
|
return reduceFilter(
|
||||||
|
{
|
||||||
|
...filterByTableIds,
|
||||||
|
tableIds: newTableIds,
|
||||||
|
},
|
||||||
|
allTables satisfies FilterTableInfo[],
|
||||||
|
{
|
||||||
|
databaseWithSchemas:
|
||||||
|
databasesWithSchemas.includes(databaseType),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[allTables, databaseType]
|
||||||
|
);
|
||||||
|
|
||||||
|
const removeTablesFromFilter: DiagramFilterContext['removeTablesFromFilter'] =
|
||||||
|
useCallback(
|
||||||
|
({ tableIds, filterCallback }) => {
|
||||||
|
setFilter((prev) => {
|
||||||
|
let tableIdsToRemovoe: string[];
|
||||||
|
|
||||||
|
if (tableIds) {
|
||||||
|
// If tableIds are provided, use them directly
|
||||||
|
tableIdsToRemovoe = tableIds;
|
||||||
|
} else if (filterCallback) {
|
||||||
|
// If filterCallback is provided, filter tables based on it
|
||||||
|
tableIdsToRemovoe = allTables
|
||||||
|
.filter(filterCallback)
|
||||||
|
.map((table) => table.id);
|
||||||
|
} else {
|
||||||
|
// If neither is provided, do nothing
|
||||||
|
return prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
const filterByTableIds = spreadFilterTables(
|
||||||
|
prev,
|
||||||
|
allTables satisfies FilterTableInfo[]
|
||||||
|
);
|
||||||
|
|
||||||
|
const currentTableIds = filterByTableIds.tableIds || [];
|
||||||
|
const newTableIds = currentTableIds.filter(
|
||||||
|
(id) => !tableIdsToRemovoe.includes(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
return reduceFilter(
|
||||||
|
{
|
||||||
|
...filterByTableIds,
|
||||||
|
tableIds: newTableIds,
|
||||||
|
},
|
||||||
|
allTables satisfies FilterTableInfo[],
|
||||||
|
{
|
||||||
|
databaseWithSchemas:
|
||||||
|
databasesWithSchemas.includes(databaseType),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[allTables, databaseType]
|
||||||
|
);
|
||||||
|
|
||||||
|
const eventConsumer = useCallback(
|
||||||
|
(event: ChartDBEvent) => {
|
||||||
|
if (!hasActiveFilter) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.action === 'add_tables') {
|
||||||
|
addTablesToFilter({
|
||||||
|
tableIds: event.data.tables.map((table) => table.id),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[hasActiveFilter, addTablesToFilter]
|
||||||
|
);
|
||||||
|
|
||||||
|
events.useSubscription(eventConsumer);
|
||||||
|
|
||||||
|
const value: DiagramFilterContext = {
|
||||||
|
loading,
|
||||||
|
filter,
|
||||||
|
clearSchemaIdsFilter: clearSchemaIds,
|
||||||
|
setTableIdsFilterEmpty: setTableIdsEmpty,
|
||||||
|
clearTableIdsFilter: clearTableIds,
|
||||||
|
resetFilter,
|
||||||
|
toggleSchemaFilter,
|
||||||
|
toggleTableFilter,
|
||||||
|
addSchemaToFilter,
|
||||||
|
hasActiveFilter,
|
||||||
|
schemasDisplayed,
|
||||||
|
addTablesToFilter,
|
||||||
|
removeTablesFromFilter,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<diagramFilterContext.Provider value={value}>
|
||||||
|
{children}
|
||||||
|
</diagramFilterContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
4
src/context/diagram-filter-context/use-diagram-filter.ts
Normal file
4
src/context/diagram-filter-context/use-diagram-filter.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import { useContext } from 'react';
|
||||||
|
import { diagramFilterContext } from './diagram-filter-context';
|
||||||
|
|
||||||
|
export const useDiagramFilter = () => useContext(diagramFilterContext);
|
||||||
@@ -7,7 +7,6 @@ import type { ExportImageDialogProps } from '@/dialogs/export-image-dialog/expor
|
|||||||
import type { ExportDiagramDialogProps } from '@/dialogs/export-diagram-dialog/export-diagram-dialog';
|
import type { ExportDiagramDialogProps } from '@/dialogs/export-diagram-dialog/export-diagram-dialog';
|
||||||
import type { ImportDiagramDialogProps } from '@/dialogs/import-diagram-dialog/import-diagram-dialog';
|
import type { ImportDiagramDialogProps } from '@/dialogs/import-diagram-dialog/import-diagram-dialog';
|
||||||
import type { CreateRelationshipDialogProps } from '@/dialogs/create-relationship-dialog/create-relationship-dialog';
|
import type { CreateRelationshipDialogProps } from '@/dialogs/create-relationship-dialog/create-relationship-dialog';
|
||||||
import type { ImportDBMLDialogProps } from '@/dialogs/import-dbml-dialog/import-dbml-dialog';
|
|
||||||
import type { OpenDiagramDialogProps } from '@/dialogs/open-diagram-dialog/open-diagram-dialog';
|
import type { OpenDiagramDialogProps } from '@/dialogs/open-diagram-dialog/open-diagram-dialog';
|
||||||
import type { CreateDiagramDialogProps } from '@/dialogs/create-diagram-dialog/create-diagram-dialog';
|
import type { CreateDiagramDialogProps } from '@/dialogs/create-diagram-dialog/create-diagram-dialog';
|
||||||
|
|
||||||
@@ -67,12 +66,6 @@ export interface DialogContext {
|
|||||||
params: Omit<ImportDiagramDialogProps, 'dialog'>
|
params: Omit<ImportDiagramDialogProps, 'dialog'>
|
||||||
) => void;
|
) => void;
|
||||||
closeImportDiagramDialog: () => void;
|
closeImportDiagramDialog: () => void;
|
||||||
|
|
||||||
// Import DBML dialog
|
|
||||||
openImportDBMLDialog: (
|
|
||||||
params?: Omit<ImportDBMLDialogProps, 'dialog'>
|
|
||||||
) => void;
|
|
||||||
closeImportDBMLDialog: () => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const dialogContext = createContext<DialogContext>({
|
export const dialogContext = createContext<DialogContext>({
|
||||||
@@ -96,6 +89,4 @@ export const dialogContext = createContext<DialogContext>({
|
|||||||
closeExportDiagramDialog: emptyFn,
|
closeExportDiagramDialog: emptyFn,
|
||||||
openImportDiagramDialog: emptyFn,
|
openImportDiagramDialog: emptyFn,
|
||||||
closeImportDiagramDialog: emptyFn,
|
closeImportDiagramDialog: emptyFn,
|
||||||
openImportDBMLDialog: emptyFn,
|
|
||||||
closeImportDBMLDialog: emptyFn,
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -20,8 +20,6 @@ import type { ExportImageDialogProps } from '@/dialogs/export-image-dialog/expor
|
|||||||
import { ExportImageDialog } from '@/dialogs/export-image-dialog/export-image-dialog';
|
import { ExportImageDialog } from '@/dialogs/export-image-dialog/export-image-dialog';
|
||||||
import { ExportDiagramDialog } from '@/dialogs/export-diagram-dialog/export-diagram-dialog';
|
import { ExportDiagramDialog } from '@/dialogs/export-diagram-dialog/export-diagram-dialog';
|
||||||
import { ImportDiagramDialog } from '@/dialogs/import-diagram-dialog/import-diagram-dialog';
|
import { ImportDiagramDialog } from '@/dialogs/import-diagram-dialog/import-diagram-dialog';
|
||||||
import type { ImportDBMLDialogProps } from '@/dialogs/import-dbml-dialog/import-dbml-dialog';
|
|
||||||
import { ImportDBMLDialog } from '@/dialogs/import-dbml-dialog/import-dbml-dialog';
|
|
||||||
|
|
||||||
export const DialogProvider: React.FC<React.PropsWithChildren> = ({
|
export const DialogProvider: React.FC<React.PropsWithChildren> = ({
|
||||||
children,
|
children,
|
||||||
@@ -132,11 +130,6 @@ export const DialogProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
const [openImportDiagramDialog, setOpenImportDiagramDialog] =
|
const [openImportDiagramDialog, setOpenImportDiagramDialog] =
|
||||||
useState(false);
|
useState(false);
|
||||||
|
|
||||||
// Import DBML dialog
|
|
||||||
const [openImportDBMLDialog, setOpenImportDBMLDialog] = useState(false);
|
|
||||||
const [importDBMLDialogParams, setImportDBMLDialogParams] =
|
|
||||||
useState<Omit<ImportDBMLDialogProps, 'dialog'>>();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<dialogContext.Provider
|
<dialogContext.Provider
|
||||||
value={{
|
value={{
|
||||||
@@ -165,11 +158,6 @@ export const DialogProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
openImportDiagramDialog: () => setOpenImportDiagramDialog(true),
|
openImportDiagramDialog: () => setOpenImportDiagramDialog(true),
|
||||||
closeImportDiagramDialog: () =>
|
closeImportDiagramDialog: () =>
|
||||||
setOpenImportDiagramDialog(false),
|
setOpenImportDiagramDialog(false),
|
||||||
openImportDBMLDialog: (params) => {
|
|
||||||
setImportDBMLDialogParams(params);
|
|
||||||
setOpenImportDBMLDialog(true);
|
|
||||||
},
|
|
||||||
closeImportDBMLDialog: () => setOpenImportDBMLDialog(false),
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
@@ -204,10 +192,6 @@ export const DialogProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
/>
|
/>
|
||||||
<ExportDiagramDialog dialog={{ open: openExportDiagramDialog }} />
|
<ExportDiagramDialog dialog={{ open: openExportDiagramDialog }} />
|
||||||
<ImportDiagramDialog dialog={{ open: openImportDiagramDialog }} />
|
<ImportDiagramDialog dialog={{ open: openImportDiagramDialog }} />
|
||||||
<ImportDBMLDialog
|
|
||||||
dialog={{ open: openImportDBMLDialog }}
|
|
||||||
{...importDBMLDialogParams}
|
|
||||||
/>
|
|
||||||
</dialogContext.Provider>
|
</dialogContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ export type DiffEventBase<T extends DiffEventType, D> = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type DiffCalculatedData = {
|
export type DiffCalculatedData = {
|
||||||
tablesAdded: DBTable[];
|
tablesToAdd: DBTable[];
|
||||||
fieldsAdded: Map<string, DBField[]>;
|
fieldsToAdd: Map<string, DBField[]>;
|
||||||
relationshipsAdded: DBRelationship[];
|
relationshipsToAdd: DBRelationship[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DiffCalculatedEvent = DiffEventBase<
|
export type DiffCalculatedEvent = DiffEventBase<
|
||||||
@@ -44,15 +44,21 @@ export interface DiffContext {
|
|||||||
options?: {
|
options?: {
|
||||||
summaryOnly?: boolean;
|
summaryOnly?: boolean;
|
||||||
};
|
};
|
||||||
}) => void;
|
}) => { foundDiff: boolean };
|
||||||
resetDiff: () => void;
|
resetDiff: () => void;
|
||||||
|
|
||||||
// table diff
|
// table diff
|
||||||
checkIfTableHasChange: ({ tableId }: { tableId: string }) => boolean;
|
checkIfTableHasChange: ({ tableId }: { tableId: string }) => boolean;
|
||||||
checkIfNewTable: ({ tableId }: { tableId: string }) => boolean;
|
checkIfNewTable: ({ tableId }: { tableId: string }) => boolean;
|
||||||
checkIfTableRemoved: ({ tableId }: { tableId: string }) => boolean;
|
checkIfTableRemoved: ({ tableId }: { tableId: string }) => boolean;
|
||||||
getTableNewName: ({ tableId }: { tableId: string }) => string | null;
|
getTableNewName: ({ tableId }: { tableId: string }) => {
|
||||||
getTableNewColor: ({ tableId }: { tableId: string }) => string | null;
|
old: string;
|
||||||
|
new: string;
|
||||||
|
} | null;
|
||||||
|
getTableNewColor: ({ tableId }: { tableId: string }) => {
|
||||||
|
old: string;
|
||||||
|
new: string;
|
||||||
|
} | null;
|
||||||
|
|
||||||
// field diff
|
// field diff
|
||||||
checkIfFieldHasChange: ({
|
checkIfFieldHasChange: ({
|
||||||
@@ -64,17 +70,46 @@ export interface DiffContext {
|
|||||||
}) => boolean;
|
}) => boolean;
|
||||||
checkIfFieldRemoved: ({ fieldId }: { fieldId: string }) => boolean;
|
checkIfFieldRemoved: ({ fieldId }: { fieldId: string }) => boolean;
|
||||||
checkIfNewField: ({ fieldId }: { fieldId: string }) => boolean;
|
checkIfNewField: ({ fieldId }: { fieldId: string }) => boolean;
|
||||||
getFieldNewName: ({ fieldId }: { fieldId: string }) => string | null;
|
getFieldNewName: ({
|
||||||
getFieldNewType: ({ fieldId }: { fieldId: string }) => DataType | null;
|
fieldId,
|
||||||
getFieldNewPrimaryKey: ({ fieldId }: { fieldId: string }) => boolean | null;
|
}: {
|
||||||
getFieldNewNullable: ({ fieldId }: { fieldId: string }) => boolean | null;
|
fieldId: string;
|
||||||
|
}) => { old: string; new: string } | null;
|
||||||
|
getFieldNewType: ({
|
||||||
|
fieldId,
|
||||||
|
}: {
|
||||||
|
fieldId: string;
|
||||||
|
}) => { old: DataType; new: DataType } | null;
|
||||||
|
getFieldNewPrimaryKey: ({
|
||||||
|
fieldId,
|
||||||
|
}: {
|
||||||
|
fieldId: string;
|
||||||
|
}) => { old: boolean; new: boolean } | null;
|
||||||
|
getFieldNewNullable: ({
|
||||||
|
fieldId,
|
||||||
|
}: {
|
||||||
|
fieldId: string;
|
||||||
|
}) => { old: boolean; new: boolean } | null;
|
||||||
getFieldNewCharacterMaximumLength: ({
|
getFieldNewCharacterMaximumLength: ({
|
||||||
fieldId,
|
fieldId,
|
||||||
}: {
|
}: {
|
||||||
fieldId: string;
|
fieldId: string;
|
||||||
}) => string | null;
|
}) => { old: string; new: string } | null;
|
||||||
getFieldNewScale: ({ fieldId }: { fieldId: string }) => number | null;
|
getFieldNewScale: ({
|
||||||
getFieldNewPrecision: ({ fieldId }: { fieldId: string }) => number | null;
|
fieldId,
|
||||||
|
}: {
|
||||||
|
fieldId: string;
|
||||||
|
}) => { old: number; new: number } | null;
|
||||||
|
getFieldNewPrecision: ({
|
||||||
|
fieldId,
|
||||||
|
}: {
|
||||||
|
fieldId: string;
|
||||||
|
}) => { old: number; new: number } | null;
|
||||||
|
getFieldNewIsArray: ({
|
||||||
|
fieldId,
|
||||||
|
}: {
|
||||||
|
fieldId: string;
|
||||||
|
}) => { old: boolean; new: boolean } | null;
|
||||||
|
|
||||||
// relationship diff
|
// relationship diff
|
||||||
checkIfNewRelationship: ({
|
checkIfNewRelationship: ({
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export const DiffProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
|
|
||||||
const events = useEventEmitter<DiffEvent>();
|
const events = useEventEmitter<DiffEvent>();
|
||||||
|
|
||||||
const generateNewFieldsMap = useCallback(
|
const generateFieldsToAddMap = useCallback(
|
||||||
({
|
({
|
||||||
diffMap,
|
diffMap,
|
||||||
newDiagram,
|
newDiagram,
|
||||||
@@ -66,7 +66,7 @@ export const DiffProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
const findNewRelationships = useCallback(
|
const findRelationshipsToAdd = useCallback(
|
||||||
({
|
({
|
||||||
diffMap,
|
diffMap,
|
||||||
newDiagram,
|
newDiagram,
|
||||||
@@ -101,7 +101,7 @@ export const DiffProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
diffMap: DiffMap;
|
diffMap: DiffMap;
|
||||||
}): DiffCalculatedData => {
|
}): DiffCalculatedData => {
|
||||||
return {
|
return {
|
||||||
tablesAdded:
|
tablesToAdd:
|
||||||
newDiagram?.tables?.filter((table) => {
|
newDiagram?.tables?.filter((table) => {
|
||||||
const tableKey = getDiffMapKey({
|
const tableKey = getDiffMapKey({
|
||||||
diffObject: 'table',
|
diffObject: 'table',
|
||||||
@@ -114,17 +114,17 @@ export const DiffProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
);
|
);
|
||||||
}) ?? [],
|
}) ?? [],
|
||||||
|
|
||||||
fieldsAdded: generateNewFieldsMap({
|
fieldsToAdd: generateFieldsToAddMap({
|
||||||
diffMap: diffMap,
|
diffMap: diffMap,
|
||||||
newDiagram: newDiagram,
|
newDiagram: newDiagram,
|
||||||
}),
|
}),
|
||||||
relationshipsAdded: findNewRelationships({
|
relationshipsToAdd: findRelationshipsToAdd({
|
||||||
diffMap: diffMap,
|
diffMap: diffMap,
|
||||||
newDiagram: newDiagram,
|
newDiagram: newDiagram,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
[findNewRelationships, generateNewFieldsMap]
|
[findRelationshipsToAdd, generateFieldsToAddMap]
|
||||||
);
|
);
|
||||||
|
|
||||||
const calculateDiff: DiffContext['calculateDiff'] = useCallback(
|
const calculateDiff: DiffContext['calculateDiff'] = useCallback(
|
||||||
@@ -149,6 +149,8 @@ export const DiffProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
newDiagram: newDiagramArg,
|
newDiagram: newDiagramArg,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return { foundDiff: !!newDiffs.size };
|
||||||
},
|
},
|
||||||
[setDiffMap, events, generateDiffCalculatedData]
|
[setDiffMap, events, generateDiffCalculatedData]
|
||||||
);
|
);
|
||||||
@@ -165,7 +167,10 @@ export const DiffProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
const diff = diffMap.get(tableNameKey);
|
const diff = diffMap.get(tableNameKey);
|
||||||
|
|
||||||
if (diff?.type === 'changed') {
|
if (diff?.type === 'changed') {
|
||||||
return diff.newValue as string;
|
return {
|
||||||
|
new: diff.newValue as string,
|
||||||
|
old: diff.oldValue as string,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,7 +191,10 @@ export const DiffProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
const diff = diffMap.get(tableColorKey);
|
const diff = diffMap.get(tableColorKey);
|
||||||
|
|
||||||
if (diff?.type === 'changed') {
|
if (diff?.type === 'changed') {
|
||||||
return diff.newValue as string;
|
return {
|
||||||
|
new: diff.newValue as string,
|
||||||
|
old: diff.oldValue as string,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -277,7 +285,10 @@ export const DiffProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
const diff = diffMap.get(fieldKey);
|
const diff = diffMap.get(fieldKey);
|
||||||
|
|
||||||
if (diff?.type === 'changed') {
|
if (diff?.type === 'changed') {
|
||||||
return diff.newValue as string;
|
return {
|
||||||
|
old: diff.oldValue as string,
|
||||||
|
new: diff.newValue as string,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,7 +309,10 @@ export const DiffProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
const diff = diffMap.get(fieldKey);
|
const diff = diffMap.get(fieldKey);
|
||||||
|
|
||||||
if (diff?.type === 'changed') {
|
if (diff?.type === 'changed') {
|
||||||
return diff.newValue as DataType;
|
return {
|
||||||
|
old: diff.oldValue as DataType,
|
||||||
|
new: diff.newValue as DataType,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,7 +335,10 @@ export const DiffProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
const diff = diffMap.get(fieldKey);
|
const diff = diffMap.get(fieldKey);
|
||||||
|
|
||||||
if (diff?.type === 'changed') {
|
if (diff?.type === 'changed') {
|
||||||
return diff.newValue as boolean;
|
return {
|
||||||
|
old: diff.oldValue as boolean,
|
||||||
|
new: diff.newValue as boolean,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,7 +359,10 @@ export const DiffProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
const diff = diffMap.get(fieldKey);
|
const diff = diffMap.get(fieldKey);
|
||||||
|
|
||||||
if (diff?.type === 'changed') {
|
if (diff?.type === 'changed') {
|
||||||
return diff.newValue as boolean;
|
return {
|
||||||
|
old: diff.oldValue as boolean,
|
||||||
|
new: diff.newValue as boolean,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -365,7 +385,10 @@ export const DiffProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
const diff = diffMap.get(fieldKey);
|
const diff = diffMap.get(fieldKey);
|
||||||
|
|
||||||
if (diff?.type === 'changed') {
|
if (diff?.type === 'changed') {
|
||||||
return diff.newValue as string;
|
return {
|
||||||
|
old: diff.oldValue as string,
|
||||||
|
new: diff.newValue as string,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -386,7 +409,10 @@ export const DiffProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
const diff = diffMap.get(fieldKey);
|
const diff = diffMap.get(fieldKey);
|
||||||
|
|
||||||
if (diff?.type === 'changed') {
|
if (diff?.type === 'changed') {
|
||||||
return diff.newValue as number;
|
return {
|
||||||
|
old: diff.oldValue as number,
|
||||||
|
new: diff.newValue as number,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -409,7 +435,34 @@ export const DiffProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
const diff = diffMap.get(fieldKey);
|
const diff = diffMap.get(fieldKey);
|
||||||
|
|
||||||
if (diff?.type === 'changed') {
|
if (diff?.type === 'changed') {
|
||||||
return diff.newValue as number;
|
return {
|
||||||
|
old: diff.oldValue as number,
|
||||||
|
new: diff.newValue as number,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
[diffMap]
|
||||||
|
);
|
||||||
|
|
||||||
|
const getFieldNewIsArray = useCallback<DiffContext['getFieldNewIsArray']>(
|
||||||
|
({ fieldId }) => {
|
||||||
|
const fieldKey = getDiffMapKey({
|
||||||
|
diffObject: 'field',
|
||||||
|
objectId: fieldId,
|
||||||
|
attribute: 'isArray',
|
||||||
|
});
|
||||||
|
|
||||||
|
if (diffMap.has(fieldKey)) {
|
||||||
|
const diff = diffMap.get(fieldKey);
|
||||||
|
|
||||||
|
if (diff?.type === 'changed') {
|
||||||
|
return {
|
||||||
|
old: diff.oldValue as boolean,
|
||||||
|
new: diff.newValue as boolean,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -491,6 +544,7 @@ export const DiffProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
getFieldNewCharacterMaximumLength,
|
getFieldNewCharacterMaximumLength,
|
||||||
getFieldNewScale,
|
getFieldNewScale,
|
||||||
getFieldNewPrecision,
|
getFieldNewPrecision,
|
||||||
|
getFieldNewIsArray,
|
||||||
|
|
||||||
// relationship diff
|
// relationship diff
|
||||||
checkIfNewRelationship,
|
checkIfNewRelationship,
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ import { emptyFn } from '@/lib/utils';
|
|||||||
import { createContext } from 'react';
|
import { createContext } from 'react';
|
||||||
|
|
||||||
export type SidebarSection =
|
export type SidebarSection =
|
||||||
|
| 'dbml'
|
||||||
| 'tables'
|
| 'tables'
|
||||||
| 'relationships'
|
| 'refs'
|
||||||
| 'dependencies'
|
|
||||||
| 'areas'
|
| 'areas'
|
||||||
| 'customTypes';
|
| 'customTypes';
|
||||||
|
|
||||||
@@ -13,14 +13,16 @@ export interface LayoutContext {
|
|||||||
openTableFromSidebar: (tableId: string) => void;
|
openTableFromSidebar: (tableId: string) => void;
|
||||||
closeAllTablesInSidebar: () => void;
|
closeAllTablesInSidebar: () => void;
|
||||||
|
|
||||||
openedRelationshipInSidebar: string | undefined;
|
|
||||||
openRelationshipFromSidebar: (relationshipId: string) => void;
|
openRelationshipFromSidebar: (relationshipId: string) => void;
|
||||||
closeAllRelationshipsInSidebar: () => void;
|
closeAllRelationshipsInSidebar: () => void;
|
||||||
|
|
||||||
openedDependencyInSidebar: string | undefined;
|
|
||||||
openDependencyFromSidebar: (dependencyId: string) => void;
|
openDependencyFromSidebar: (dependencyId: string) => void;
|
||||||
closeAllDependenciesInSidebar: () => void;
|
closeAllDependenciesInSidebar: () => void;
|
||||||
|
|
||||||
|
openedRefInSidebar: string | undefined;
|
||||||
|
openRefFromSidebar: (refId: string) => void;
|
||||||
|
closeAllRefsInSidebar: () => void;
|
||||||
|
|
||||||
openedAreaInSidebar: string | undefined;
|
openedAreaInSidebar: string | undefined;
|
||||||
openAreaFromSidebar: (areaId: string) => void;
|
openAreaFromSidebar: (areaId: string) => void;
|
||||||
closeAllAreasInSidebar: () => void;
|
closeAllAreasInSidebar: () => void;
|
||||||
@@ -36,24 +38,22 @@ export interface LayoutContext {
|
|||||||
hideSidePanel: () => void;
|
hideSidePanel: () => void;
|
||||||
showSidePanel: () => void;
|
showSidePanel: () => void;
|
||||||
toggleSidePanel: () => void;
|
toggleSidePanel: () => void;
|
||||||
|
|
||||||
isSelectSchemaOpen: boolean;
|
|
||||||
openSelectSchema: () => void;
|
|
||||||
closeSelectSchema: () => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const layoutContext = createContext<LayoutContext>({
|
export const layoutContext = createContext<LayoutContext>({
|
||||||
openedTableInSidebar: undefined,
|
openedTableInSidebar: undefined,
|
||||||
selectedSidebarSection: 'tables',
|
selectedSidebarSection: 'tables',
|
||||||
|
|
||||||
openedRelationshipInSidebar: undefined,
|
|
||||||
openRelationshipFromSidebar: emptyFn,
|
openRelationshipFromSidebar: emptyFn,
|
||||||
closeAllRelationshipsInSidebar: emptyFn,
|
closeAllRelationshipsInSidebar: emptyFn,
|
||||||
|
|
||||||
openedDependencyInSidebar: undefined,
|
|
||||||
openDependencyFromSidebar: emptyFn,
|
openDependencyFromSidebar: emptyFn,
|
||||||
closeAllDependenciesInSidebar: emptyFn,
|
closeAllDependenciesInSidebar: emptyFn,
|
||||||
|
|
||||||
|
openedRefInSidebar: undefined,
|
||||||
|
openRefFromSidebar: emptyFn,
|
||||||
|
closeAllRefsInSidebar: emptyFn,
|
||||||
|
|
||||||
openedAreaInSidebar: undefined,
|
openedAreaInSidebar: undefined,
|
||||||
openAreaFromSidebar: emptyFn,
|
openAreaFromSidebar: emptyFn,
|
||||||
closeAllAreasInSidebar: emptyFn,
|
closeAllAreasInSidebar: emptyFn,
|
||||||
@@ -70,8 +70,4 @@ export const layoutContext = createContext<LayoutContext>({
|
|||||||
hideSidePanel: emptyFn,
|
hideSidePanel: emptyFn,
|
||||||
showSidePanel: emptyFn,
|
showSidePanel: emptyFn,
|
||||||
toggleSidePanel: emptyFn,
|
toggleSidePanel: emptyFn,
|
||||||
|
|
||||||
isSelectSchemaOpen: false,
|
|
||||||
openSelectSchema: emptyFn,
|
|
||||||
closeSelectSchema: emptyFn,
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,10 +10,9 @@ export const LayoutProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
const [openedTableInSidebar, setOpenedTableInSidebar] = React.useState<
|
const [openedTableInSidebar, setOpenedTableInSidebar] = React.useState<
|
||||||
string | undefined
|
string | undefined
|
||||||
>();
|
>();
|
||||||
const [openedRelationshipInSidebar, setOpenedRelationshipInSidebar] =
|
const [openedRefInSidebar, setOpenedRefInSidebar] = React.useState<
|
||||||
React.useState<string | undefined>();
|
string | undefined
|
||||||
const [openedDependencyInSidebar, setOpenedDependencyInSidebar] =
|
>();
|
||||||
React.useState<string | undefined>();
|
|
||||||
const [openedAreaInSidebar, setOpenedAreaInSidebar] = React.useState<
|
const [openedAreaInSidebar, setOpenedAreaInSidebar] = React.useState<
|
||||||
string | undefined
|
string | undefined
|
||||||
>();
|
>();
|
||||||
@@ -23,17 +22,18 @@ export const LayoutProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
React.useState<SidebarSection>('tables');
|
React.useState<SidebarSection>('tables');
|
||||||
const [isSidePanelShowed, setIsSidePanelShowed] =
|
const [isSidePanelShowed, setIsSidePanelShowed] =
|
||||||
React.useState<boolean>(isDesktop);
|
React.useState<boolean>(isDesktop);
|
||||||
const [isSelectSchemaOpen, setIsSelectSchemaOpen] =
|
|
||||||
React.useState<boolean>(false);
|
|
||||||
|
|
||||||
const closeAllTablesInSidebar: LayoutContext['closeAllTablesInSidebar'] =
|
const closeAllTablesInSidebar: LayoutContext['closeAllTablesInSidebar'] =
|
||||||
() => setOpenedTableInSidebar('');
|
() => setOpenedTableInSidebar('');
|
||||||
|
|
||||||
const closeAllRelationshipsInSidebar: LayoutContext['closeAllRelationshipsInSidebar'] =
|
const closeAllRelationshipsInSidebar: LayoutContext['closeAllRelationshipsInSidebar'] =
|
||||||
() => setOpenedRelationshipInSidebar('');
|
() => setOpenedRefInSidebar('');
|
||||||
|
|
||||||
const closeAllDependenciesInSidebar: LayoutContext['closeAllDependenciesInSidebar'] =
|
const closeAllDependenciesInSidebar: LayoutContext['closeAllDependenciesInSidebar'] =
|
||||||
() => setOpenedDependencyInSidebar('');
|
() => setOpenedRefInSidebar('');
|
||||||
|
|
||||||
|
const closeAllRefsInSidebar: LayoutContext['closeAllRefsInSidebar'] = () =>
|
||||||
|
setOpenedRefInSidebar('');
|
||||||
|
|
||||||
const closeAllAreasInSidebar: LayoutContext['closeAllAreasInSidebar'] =
|
const closeAllAreasInSidebar: LayoutContext['closeAllAreasInSidebar'] =
|
||||||
() => setOpenedAreaInSidebar('');
|
() => setOpenedAreaInSidebar('');
|
||||||
@@ -62,15 +62,21 @@ export const LayoutProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
const openRelationshipFromSidebar: LayoutContext['openRelationshipFromSidebar'] =
|
const openRelationshipFromSidebar: LayoutContext['openRelationshipFromSidebar'] =
|
||||||
(relationshipId) => {
|
(relationshipId) => {
|
||||||
showSidePanel();
|
showSidePanel();
|
||||||
setSelectedSidebarSection('relationships');
|
setSelectedSidebarSection('refs');
|
||||||
setOpenedRelationshipInSidebar(relationshipId);
|
setOpenedRefInSidebar(relationshipId);
|
||||||
};
|
};
|
||||||
|
|
||||||
const openDependencyFromSidebar: LayoutContext['openDependencyFromSidebar'] =
|
const openDependencyFromSidebar: LayoutContext['openDependencyFromSidebar'] =
|
||||||
(dependencyId) => {
|
(dependencyId) => {
|
||||||
showSidePanel();
|
showSidePanel();
|
||||||
setSelectedSidebarSection('dependencies');
|
setSelectedSidebarSection('refs');
|
||||||
setOpenedDependencyInSidebar(dependencyId);
|
setOpenedRefInSidebar(dependencyId);
|
||||||
|
};
|
||||||
|
|
||||||
|
const openRefFromSidebar: LayoutContext['openRefFromSidebar'] = (refId) => {
|
||||||
|
showSidePanel();
|
||||||
|
setSelectedSidebarSection('refs');
|
||||||
|
setOpenedRefInSidebar(refId);
|
||||||
};
|
};
|
||||||
|
|
||||||
const openAreaFromSidebar: LayoutContext['openAreaFromSidebar'] = (
|
const openAreaFromSidebar: LayoutContext['openAreaFromSidebar'] = (
|
||||||
@@ -88,11 +94,6 @@ export const LayoutProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
setOpenedTableInSidebar(customTypeId);
|
setOpenedTableInSidebar(customTypeId);
|
||||||
};
|
};
|
||||||
|
|
||||||
const openSelectSchema: LayoutContext['openSelectSchema'] = () =>
|
|
||||||
setIsSelectSchemaOpen(true);
|
|
||||||
|
|
||||||
const closeSelectSchema: LayoutContext['closeSelectSchema'] = () =>
|
|
||||||
setIsSelectSchemaOpen(false);
|
|
||||||
return (
|
return (
|
||||||
<layoutContext.Provider
|
<layoutContext.Provider
|
||||||
value={{
|
value={{
|
||||||
@@ -100,7 +101,6 @@ export const LayoutProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
selectedSidebarSection,
|
selectedSidebarSection,
|
||||||
openTableFromSidebar,
|
openTableFromSidebar,
|
||||||
selectSidebarSection: setSelectedSidebarSection,
|
selectSidebarSection: setSelectedSidebarSection,
|
||||||
openedRelationshipInSidebar,
|
|
||||||
openRelationshipFromSidebar,
|
openRelationshipFromSidebar,
|
||||||
closeAllTablesInSidebar,
|
closeAllTablesInSidebar,
|
||||||
closeAllRelationshipsInSidebar,
|
closeAllRelationshipsInSidebar,
|
||||||
@@ -108,12 +108,11 @@ export const LayoutProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
hideSidePanel,
|
hideSidePanel,
|
||||||
showSidePanel,
|
showSidePanel,
|
||||||
toggleSidePanel,
|
toggleSidePanel,
|
||||||
isSelectSchemaOpen,
|
|
||||||
openSelectSchema,
|
|
||||||
closeSelectSchema,
|
|
||||||
openedDependencyInSidebar,
|
|
||||||
openDependencyFromSidebar,
|
openDependencyFromSidebar,
|
||||||
closeAllDependenciesInSidebar,
|
closeAllDependenciesInSidebar,
|
||||||
|
openedRefInSidebar,
|
||||||
|
openRefFromSidebar,
|
||||||
|
closeAllRefsInSidebar,
|
||||||
openedAreaInSidebar,
|
openedAreaInSidebar,
|
||||||
openAreaFromSidebar,
|
openAreaFromSidebar,
|
||||||
closeAllAreasInSidebar,
|
closeAllAreasInSidebar,
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ import type { Theme } from '../theme-context/theme-context';
|
|||||||
|
|
||||||
export type ScrollAction = 'pan' | 'zoom';
|
export type ScrollAction = 'pan' | 'zoom';
|
||||||
|
|
||||||
export type SchemasFilter = Record<string, string[]>;
|
|
||||||
|
|
||||||
export interface LocalConfigContext {
|
export interface LocalConfigContext {
|
||||||
theme: Theme;
|
theme: Theme;
|
||||||
setTheme: (theme: Theme) => void;
|
setTheme: (theme: Theme) => void;
|
||||||
@@ -13,8 +11,8 @@ export interface LocalConfigContext {
|
|||||||
scrollAction: ScrollAction;
|
scrollAction: ScrollAction;
|
||||||
setScrollAction: (action: ScrollAction) => void;
|
setScrollAction: (action: ScrollAction) => void;
|
||||||
|
|
||||||
schemasFilter: SchemasFilter;
|
showDBViews: boolean;
|
||||||
setSchemasFilter: React.Dispatch<React.SetStateAction<SchemasFilter>>;
|
setShowDBViews: (showViews: boolean) => void;
|
||||||
|
|
||||||
showCardinality: boolean;
|
showCardinality: boolean;
|
||||||
setShowCardinality: (showCardinality: boolean) => void;
|
setShowCardinality: (showCardinality: boolean) => void;
|
||||||
@@ -22,20 +20,12 @@ export interface LocalConfigContext {
|
|||||||
showFieldAttributes: boolean;
|
showFieldAttributes: boolean;
|
||||||
setShowFieldAttributes: (showFieldAttributes: boolean) => void;
|
setShowFieldAttributes: (showFieldAttributes: boolean) => void;
|
||||||
|
|
||||||
hideMultiSchemaNotification: boolean;
|
|
||||||
setHideMultiSchemaNotification: (
|
|
||||||
hideMultiSchemaNotification: boolean
|
|
||||||
) => void;
|
|
||||||
|
|
||||||
githubRepoOpened: boolean;
|
githubRepoOpened: boolean;
|
||||||
setGithubRepoOpened: (githubRepoOpened: boolean) => void;
|
setGithubRepoOpened: (githubRepoOpened: boolean) => void;
|
||||||
|
|
||||||
starUsDialogLastOpen: number;
|
starUsDialogLastOpen: number;
|
||||||
setStarUsDialogLastOpen: (lastOpen: number) => void;
|
setStarUsDialogLastOpen: (lastOpen: number) => void;
|
||||||
|
|
||||||
showDependenciesOnCanvas: boolean;
|
|
||||||
setShowDependenciesOnCanvas: (showDependenciesOnCanvas: boolean) => void;
|
|
||||||
|
|
||||||
showMiniMapOnCanvas: boolean;
|
showMiniMapOnCanvas: boolean;
|
||||||
setShowMiniMapOnCanvas: (showMiniMapOnCanvas: boolean) => void;
|
setShowMiniMapOnCanvas: (showMiniMapOnCanvas: boolean) => void;
|
||||||
}
|
}
|
||||||
@@ -47,8 +37,8 @@ export const LocalConfigContext = createContext<LocalConfigContext>({
|
|||||||
scrollAction: 'pan',
|
scrollAction: 'pan',
|
||||||
setScrollAction: emptyFn,
|
setScrollAction: emptyFn,
|
||||||
|
|
||||||
schemasFilter: {},
|
showDBViews: false,
|
||||||
setSchemasFilter: emptyFn,
|
setShowDBViews: emptyFn,
|
||||||
|
|
||||||
showCardinality: true,
|
showCardinality: true,
|
||||||
setShowCardinality: emptyFn,
|
setShowCardinality: emptyFn,
|
||||||
@@ -56,18 +46,12 @@ export const LocalConfigContext = createContext<LocalConfigContext>({
|
|||||||
showFieldAttributes: true,
|
showFieldAttributes: true,
|
||||||
setShowFieldAttributes: emptyFn,
|
setShowFieldAttributes: emptyFn,
|
||||||
|
|
||||||
hideMultiSchemaNotification: false,
|
|
||||||
setHideMultiSchemaNotification: emptyFn,
|
|
||||||
|
|
||||||
githubRepoOpened: false,
|
githubRepoOpened: false,
|
||||||
setGithubRepoOpened: emptyFn,
|
setGithubRepoOpened: emptyFn,
|
||||||
|
|
||||||
starUsDialogLastOpen: 0,
|
starUsDialogLastOpen: 0,
|
||||||
setStarUsDialogLastOpen: emptyFn,
|
setStarUsDialogLastOpen: emptyFn,
|
||||||
|
|
||||||
showDependenciesOnCanvas: false,
|
|
||||||
setShowDependenciesOnCanvas: emptyFn,
|
|
||||||
|
|
||||||
showMiniMapOnCanvas: false,
|
showMiniMapOnCanvas: false,
|
||||||
setShowMiniMapOnCanvas: emptyFn,
|
setShowMiniMapOnCanvas: emptyFn,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,18 +1,16 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import type { SchemasFilter, ScrollAction } from './local-config-context';
|
import type { ScrollAction } from './local-config-context';
|
||||||
import { LocalConfigContext } from './local-config-context';
|
import { LocalConfigContext } from './local-config-context';
|
||||||
import type { Theme } from '../theme-context/theme-context';
|
import type { Theme } from '../theme-context/theme-context';
|
||||||
|
|
||||||
const themeKey = 'theme';
|
const themeKey = 'theme';
|
||||||
const scrollActionKey = 'scroll_action';
|
const scrollActionKey = 'scroll_action';
|
||||||
const schemasFilterKey = 'schemas_filter';
|
|
||||||
const showCardinalityKey = 'show_cardinality';
|
const showCardinalityKey = 'show_cardinality';
|
||||||
const showFieldAttributesKey = 'show_field_attributes';
|
const showFieldAttributesKey = 'show_field_attributes';
|
||||||
const hideMultiSchemaNotificationKey = 'hide_multi_schema_notification';
|
|
||||||
const githubRepoOpenedKey = 'github_repo_opened';
|
const githubRepoOpenedKey = 'github_repo_opened';
|
||||||
const starUsDialogLastOpenKey = 'star_us_dialog_last_open';
|
const starUsDialogLastOpenKey = 'star_us_dialog_last_open';
|
||||||
const showDependenciesOnCanvasKey = 'show_dependencies_on_canvas';
|
|
||||||
const showMiniMapOnCanvasKey = 'show_minimap_on_canvas';
|
const showMiniMapOnCanvasKey = 'show_minimap_on_canvas';
|
||||||
|
const showDBViewsKey = 'show_db_views';
|
||||||
|
|
||||||
export const LocalConfigProvider: React.FC<React.PropsWithChildren> = ({
|
export const LocalConfigProvider: React.FC<React.PropsWithChildren> = ({
|
||||||
children,
|
children,
|
||||||
@@ -25,10 +23,8 @@ export const LocalConfigProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
(localStorage.getItem(scrollActionKey) as ScrollAction) || 'pan'
|
(localStorage.getItem(scrollActionKey) as ScrollAction) || 'pan'
|
||||||
);
|
);
|
||||||
|
|
||||||
const [schemasFilter, setSchemasFilter] = React.useState<SchemasFilter>(
|
const [showDBViews, setShowDBViews] = React.useState<boolean>(
|
||||||
JSON.parse(
|
(localStorage.getItem(showDBViewsKey) || 'false') === 'true'
|
||||||
localStorage.getItem(schemasFilterKey) || '{}'
|
|
||||||
) as SchemasFilter
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const [showCardinality, setShowCardinality] = React.useState<boolean>(
|
const [showCardinality, setShowCardinality] = React.useState<boolean>(
|
||||||
@@ -40,12 +36,6 @@ export const LocalConfigProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
(localStorage.getItem(showFieldAttributesKey) || 'true') === 'true'
|
(localStorage.getItem(showFieldAttributesKey) || 'true') === 'true'
|
||||||
);
|
);
|
||||||
|
|
||||||
const [hideMultiSchemaNotification, setHideMultiSchemaNotification] =
|
|
||||||
React.useState<boolean>(
|
|
||||||
(localStorage.getItem(hideMultiSchemaNotificationKey) ||
|
|
||||||
'false') === 'true'
|
|
||||||
);
|
|
||||||
|
|
||||||
const [githubRepoOpened, setGithubRepoOpened] = React.useState<boolean>(
|
const [githubRepoOpened, setGithubRepoOpened] = React.useState<boolean>(
|
||||||
(localStorage.getItem(githubRepoOpenedKey) || 'false') === 'true'
|
(localStorage.getItem(githubRepoOpenedKey) || 'false') === 'true'
|
||||||
);
|
);
|
||||||
@@ -55,12 +45,6 @@ export const LocalConfigProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
parseInt(localStorage.getItem(starUsDialogLastOpenKey) || '0')
|
parseInt(localStorage.getItem(starUsDialogLastOpenKey) || '0')
|
||||||
);
|
);
|
||||||
|
|
||||||
const [showDependenciesOnCanvas, setShowDependenciesOnCanvas] =
|
|
||||||
React.useState<boolean>(
|
|
||||||
(localStorage.getItem(showDependenciesOnCanvasKey) || 'false') ===
|
|
||||||
'true'
|
|
||||||
);
|
|
||||||
|
|
||||||
const [showMiniMapOnCanvas, setShowMiniMapOnCanvas] =
|
const [showMiniMapOnCanvas, setShowMiniMapOnCanvas] =
|
||||||
React.useState<boolean>(
|
React.useState<boolean>(
|
||||||
(localStorage.getItem(showMiniMapOnCanvasKey) || 'true') === 'true'
|
(localStorage.getItem(showMiniMapOnCanvasKey) || 'true') === 'true'
|
||||||
@@ -77,13 +61,6 @@ export const LocalConfigProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
localStorage.setItem(githubRepoOpenedKey, githubRepoOpened.toString());
|
localStorage.setItem(githubRepoOpenedKey, githubRepoOpened.toString());
|
||||||
}, [githubRepoOpened]);
|
}, [githubRepoOpened]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
localStorage.setItem(
|
|
||||||
hideMultiSchemaNotificationKey,
|
|
||||||
hideMultiSchemaNotification.toString()
|
|
||||||
);
|
|
||||||
}, [hideMultiSchemaNotification]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
localStorage.setItem(themeKey, theme);
|
localStorage.setItem(themeKey, theme);
|
||||||
}, [theme]);
|
}, [theme]);
|
||||||
@@ -93,20 +70,13 @@ export const LocalConfigProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
}, [scrollAction]);
|
}, [scrollAction]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
localStorage.setItem(schemasFilterKey, JSON.stringify(schemasFilter));
|
localStorage.setItem(showDBViewsKey, showDBViews.toString());
|
||||||
}, [schemasFilter]);
|
}, [showDBViews]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
localStorage.setItem(showCardinalityKey, showCardinality.toString());
|
localStorage.setItem(showCardinalityKey, showCardinality.toString());
|
||||||
}, [showCardinality]);
|
}, [showCardinality]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
localStorage.setItem(
|
|
||||||
showDependenciesOnCanvasKey,
|
|
||||||
showDependenciesOnCanvas.toString()
|
|
||||||
);
|
|
||||||
}, [showDependenciesOnCanvas]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
showMiniMapOnCanvasKey,
|
showMiniMapOnCanvasKey,
|
||||||
@@ -121,20 +91,16 @@ export const LocalConfigProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
setTheme,
|
setTheme,
|
||||||
scrollAction,
|
scrollAction,
|
||||||
setScrollAction,
|
setScrollAction,
|
||||||
schemasFilter,
|
showDBViews,
|
||||||
setSchemasFilter,
|
setShowDBViews,
|
||||||
showCardinality,
|
showCardinality,
|
||||||
setShowCardinality,
|
setShowCardinality,
|
||||||
showFieldAttributes,
|
showFieldAttributes,
|
||||||
setShowFieldAttributes,
|
setShowFieldAttributes,
|
||||||
hideMultiSchemaNotification,
|
|
||||||
setHideMultiSchemaNotification,
|
|
||||||
setGithubRepoOpened,
|
setGithubRepoOpened,
|
||||||
githubRepoOpened,
|
githubRepoOpened,
|
||||||
starUsDialogLastOpen,
|
starUsDialogLastOpen,
|
||||||
setStarUsDialogLastOpen,
|
setStarUsDialogLastOpen,
|
||||||
showDependenciesOnCanvas,
|
|
||||||
setShowDependenciesOnCanvas,
|
|
||||||
showMiniMapOnCanvas,
|
showMiniMapOnCanvas,
|
||||||
setShowMiniMapOnCanvas,
|
setShowMiniMapOnCanvas,
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -7,12 +7,21 @@ import type { ChartDBConfig } from '@/lib/domain/config';
|
|||||||
import type { DBDependency } from '@/lib/domain/db-dependency';
|
import type { DBDependency } from '@/lib/domain/db-dependency';
|
||||||
import type { Area } from '@/lib/domain/area';
|
import type { Area } from '@/lib/domain/area';
|
||||||
import type { DBCustomType } from '@/lib/domain/db-custom-type';
|
import type { DBCustomType } from '@/lib/domain/db-custom-type';
|
||||||
|
import type { DiagramFilter } from '@/lib/domain/diagram-filter/diagram-filter';
|
||||||
|
|
||||||
export interface StorageContext {
|
export interface StorageContext {
|
||||||
// Config operations
|
// Config operations
|
||||||
getConfig: () => Promise<ChartDBConfig | undefined>;
|
getConfig: () => Promise<ChartDBConfig | undefined>;
|
||||||
updateConfig: (config: Partial<ChartDBConfig>) => Promise<void>;
|
updateConfig: (config: Partial<ChartDBConfig>) => Promise<void>;
|
||||||
|
|
||||||
|
// Diagram filter operations
|
||||||
|
getDiagramFilter: (diagramId: string) => Promise<DiagramFilter | undefined>;
|
||||||
|
updateDiagramFilter: (
|
||||||
|
diagramId: string,
|
||||||
|
filter: DiagramFilter
|
||||||
|
) => Promise<void>;
|
||||||
|
deleteDiagramFilter: (diagramId: string) => Promise<void>;
|
||||||
|
|
||||||
// Diagram operations
|
// Diagram operations
|
||||||
addDiagram: (params: { diagram: Diagram }) => Promise<void>;
|
addDiagram: (params: { diagram: Diagram }) => Promise<void>;
|
||||||
listDiagrams: (options?: {
|
listDiagrams: (options?: {
|
||||||
@@ -132,6 +141,10 @@ export const storageInitialValue: StorageContext = {
|
|||||||
getConfig: emptyFn,
|
getConfig: emptyFn,
|
||||||
updateConfig: emptyFn,
|
updateConfig: emptyFn,
|
||||||
|
|
||||||
|
getDiagramFilter: emptyFn,
|
||||||
|
updateDiagramFilter: emptyFn,
|
||||||
|
deleteDiagramFilter: emptyFn,
|
||||||
|
|
||||||
addDiagram: emptyFn,
|
addDiagram: emptyFn,
|
||||||
listDiagrams: emptyFn,
|
listDiagrams: emptyFn,
|
||||||
getDiagram: emptyFn,
|
getDiagram: emptyFn,
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import type { ChartDBConfig } from '@/lib/domain/config';
|
|||||||
import type { DBDependency } from '@/lib/domain/db-dependency';
|
import type { DBDependency } from '@/lib/domain/db-dependency';
|
||||||
import type { Area } from '@/lib/domain/area';
|
import type { Area } from '@/lib/domain/area';
|
||||||
import type { DBCustomType } from '@/lib/domain/db-custom-type';
|
import type { DBCustomType } from '@/lib/domain/db-custom-type';
|
||||||
|
import type { DiagramFilter } from '@/lib/domain/diagram-filter/diagram-filter';
|
||||||
|
|
||||||
export const StorageProvider: React.FC<React.PropsWithChildren> = ({
|
export const StorageProvider: React.FC<React.PropsWithChildren> = ({
|
||||||
children,
|
children,
|
||||||
@@ -44,6 +45,10 @@ export const StorageProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
ChartDBConfig & { id: number },
|
ChartDBConfig & { id: number },
|
||||||
'id' // primary key "id" (for the typings only)
|
'id' // primary key "id" (for the typings only)
|
||||||
>;
|
>;
|
||||||
|
diagram_filters: EntityTable<
|
||||||
|
DiagramFilter & { diagramId: string },
|
||||||
|
'diagramId' // primary key "id" (for the typings only)
|
||||||
|
>;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Schema declaration:
|
// Schema declaration:
|
||||||
@@ -190,6 +195,27 @@ export const StorageProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
config: '++id, defaultDiagramId',
|
config: '++id, defaultDiagramId',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
dexieDB
|
||||||
|
.version(12)
|
||||||
|
.stores({
|
||||||
|
diagrams:
|
||||||
|
'++id, name, databaseType, databaseEdition, createdAt, updatedAt',
|
||||||
|
db_tables:
|
||||||
|
'++id, diagramId, name, schema, x, y, fields, indexes, color, createdAt, width, comment, isView, isMaterializedView, order',
|
||||||
|
db_relationships:
|
||||||
|
'++id, diagramId, name, sourceSchema, sourceTableId, targetSchema, targetTableId, sourceFieldId, targetFieldId, type, createdAt',
|
||||||
|
db_dependencies:
|
||||||
|
'++id, diagramId, schema, tableId, dependentSchema, dependentTableId, createdAt',
|
||||||
|
areas: '++id, diagramId, name, x, y, width, height, color',
|
||||||
|
db_custom_types:
|
||||||
|
'++id, diagramId, schema, type, kind, values, fields',
|
||||||
|
config: '++id, defaultDiagramId',
|
||||||
|
diagram_filters: 'diagramId, tableIds, schemasIds',
|
||||||
|
})
|
||||||
|
.upgrade((tx) => {
|
||||||
|
tx.table('config').clear();
|
||||||
|
});
|
||||||
|
|
||||||
dexieDB.on('ready', async () => {
|
dexieDB.on('ready', async () => {
|
||||||
const config = await dexieDB.config.get(1);
|
const config = await dexieDB.config.get(1);
|
||||||
|
|
||||||
@@ -217,6 +243,34 @@ export const StorageProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
[db]
|
[db]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const getDiagramFilter: StorageContext['getDiagramFilter'] = useCallback(
|
||||||
|
async (diagramId: string): Promise<DiagramFilter | undefined> => {
|
||||||
|
const filter = await db.diagram_filters.get({ diagramId });
|
||||||
|
|
||||||
|
return filter;
|
||||||
|
},
|
||||||
|
[db]
|
||||||
|
);
|
||||||
|
|
||||||
|
const updateDiagramFilter: StorageContext['updateDiagramFilter'] =
|
||||||
|
useCallback(
|
||||||
|
async (diagramId, filter): Promise<void> => {
|
||||||
|
await db.diagram_filters.put({
|
||||||
|
diagramId,
|
||||||
|
...filter,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[db]
|
||||||
|
);
|
||||||
|
|
||||||
|
const deleteDiagramFilter: StorageContext['deleteDiagramFilter'] =
|
||||||
|
useCallback(
|
||||||
|
async (diagramId: string): Promise<void> => {
|
||||||
|
await db.diagram_filters.where({ diagramId }).delete();
|
||||||
|
},
|
||||||
|
[db]
|
||||||
|
);
|
||||||
|
|
||||||
const addTable: StorageContext['addTable'] = useCallback(
|
const addTable: StorageContext['addTable'] = useCallback(
|
||||||
async ({ diagramId, table }) => {
|
async ({ diagramId, table }) => {
|
||||||
await db.db_tables.add({
|
await db.db_tables.add({
|
||||||
@@ -756,6 +810,9 @@ export const StorageProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
deleteCustomType,
|
deleteCustomType,
|
||||||
listCustomTypes,
|
listCustomTypes,
|
||||||
deleteDiagramCustomTypes,
|
deleteDiagramCustomTypes,
|
||||||
|
getDiagramFilter,
|
||||||
|
updateDiagramFilter,
|
||||||
|
deleteDiagramFilter,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -42,6 +42,14 @@ import {
|
|||||||
type ValidationResult,
|
type ValidationResult,
|
||||||
} from '@/lib/data/sql-import/sql-validator';
|
} from '@/lib/data/sql-import/sql-validator';
|
||||||
import { SQLValidationStatus } from './sql-validation-status';
|
import { SQLValidationStatus } from './sql-validation-status';
|
||||||
|
import { setupDBMLLanguage } from '@/components/code-snippet/languages/dbml-language';
|
||||||
|
import type { ImportMethod } from '@/lib/import-method/import-method';
|
||||||
|
import { detectImportMethod } from '@/lib/import-method/detect-import-method';
|
||||||
|
import { verifyDBML } from '@/lib/dbml/dbml-import/verify-dbml';
|
||||||
|
import {
|
||||||
|
clearErrorHighlight,
|
||||||
|
highlightErrorLine,
|
||||||
|
} from '@/components/code-snippet/dbml/utils';
|
||||||
|
|
||||||
const calculateContentSizeMB = (content: string): number => {
|
const calculateContentSizeMB = (content: string): number => {
|
||||||
return content.length / (1024 * 1024); // Convert to MB
|
return content.length / (1024 * 1024); // Convert to MB
|
||||||
@@ -55,49 +63,6 @@ const calculateIsLargeFile = (content: string): boolean => {
|
|||||||
const errorScriptOutputMessage =
|
const errorScriptOutputMessage =
|
||||||
'Invalid JSON. Please correct it or contact us at support@chartdb.io for help.';
|
'Invalid JSON. Please correct it or contact us at support@chartdb.io for help.';
|
||||||
|
|
||||||
// Helper to detect if content is likely SQL DDL or JSON
|
|
||||||
const detectContentType = (content: string): 'query' | 'ddl' | null => {
|
|
||||||
if (!content || content.trim().length === 0) return null;
|
|
||||||
|
|
||||||
// Common SQL DDL keywords
|
|
||||||
const ddlKeywords = [
|
|
||||||
'CREATE TABLE',
|
|
||||||
'ALTER TABLE',
|
|
||||||
'DROP TABLE',
|
|
||||||
'CREATE INDEX',
|
|
||||||
'CREATE VIEW',
|
|
||||||
'CREATE PROCEDURE',
|
|
||||||
'CREATE FUNCTION',
|
|
||||||
'CREATE SCHEMA',
|
|
||||||
'CREATE DATABASE',
|
|
||||||
];
|
|
||||||
|
|
||||||
const upperContent = content.toUpperCase();
|
|
||||||
|
|
||||||
// Check for SQL DDL patterns
|
|
||||||
const hasDDLKeywords = ddlKeywords.some((keyword) =>
|
|
||||||
upperContent.includes(keyword)
|
|
||||||
);
|
|
||||||
if (hasDDLKeywords) return 'ddl';
|
|
||||||
|
|
||||||
// Check if it looks like JSON
|
|
||||||
try {
|
|
||||||
// Just check structure, don't need full parse for detection
|
|
||||||
if (
|
|
||||||
(content.trim().startsWith('{') && content.trim().endsWith('}')) ||
|
|
||||||
(content.trim().startsWith('[') && content.trim().endsWith(']'))
|
|
||||||
) {
|
|
||||||
return 'query';
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
// Not valid JSON, might be partial
|
|
||||||
console.error('Error detecting content type:', error);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we can't confidently detect, return null
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface ImportDatabaseProps {
|
export interface ImportDatabaseProps {
|
||||||
goBack?: () => void;
|
goBack?: () => void;
|
||||||
onImport: () => void;
|
onImport: () => void;
|
||||||
@@ -111,8 +76,8 @@ export interface ImportDatabaseProps {
|
|||||||
>;
|
>;
|
||||||
keepDialogAfterImport?: boolean;
|
keepDialogAfterImport?: boolean;
|
||||||
title: string;
|
title: string;
|
||||||
importMethod: 'query' | 'ddl';
|
importMethod: ImportMethod;
|
||||||
setImportMethod: (method: 'query' | 'ddl') => void;
|
setImportMethod: (method: ImportMethod) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ImportDatabase: React.FC<ImportDatabaseProps> = ({
|
export const ImportDatabase: React.FC<ImportDatabaseProps> = ({
|
||||||
@@ -132,6 +97,7 @@ export const ImportDatabase: React.FC<ImportDatabaseProps> = ({
|
|||||||
const { effectiveTheme } = useTheme();
|
const { effectiveTheme } = useTheme();
|
||||||
const [errorMessage, setErrorMessage] = useState('');
|
const [errorMessage, setErrorMessage] = useState('');
|
||||||
const editorRef = useRef<editor.IStandaloneCodeEditor | null>(null);
|
const editorRef = useRef<editor.IStandaloneCodeEditor | null>(null);
|
||||||
|
const decorationsCollection = useRef<editor.IEditorDecorationsCollection>();
|
||||||
const pasteDisposableRef = useRef<IDisposable | null>(null);
|
const pasteDisposableRef = useRef<IDisposable | null>(null);
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -146,15 +112,20 @@ export const ImportDatabase: React.FC<ImportDatabaseProps> = ({
|
|||||||
const [isAutoFixing, setIsAutoFixing] = useState(false);
|
const [isAutoFixing, setIsAutoFixing] = useState(false);
|
||||||
const [showAutoFixButton, setShowAutoFixButton] = useState(false);
|
const [showAutoFixButton, setShowAutoFixButton] = useState(false);
|
||||||
|
|
||||||
|
const clearDecorations = useCallback(() => {
|
||||||
|
clearErrorHighlight(decorationsCollection.current);
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setScriptResult('');
|
setScriptResult('');
|
||||||
setErrorMessage('');
|
setErrorMessage('');
|
||||||
setShowCheckJsonButton(false);
|
setShowCheckJsonButton(false);
|
||||||
}, [importMethod, setScriptResult]);
|
}, [importMethod, setScriptResult]);
|
||||||
|
|
||||||
// Check if the ddl is valid
|
// Check if the ddl or dbml is valid
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (importMethod !== 'ddl') {
|
clearDecorations();
|
||||||
|
if (importMethod === 'query') {
|
||||||
setSqlValidation(null);
|
setSqlValidation(null);
|
||||||
setShowAutoFixButton(false);
|
setShowAutoFixButton(false);
|
||||||
return;
|
return;
|
||||||
@@ -163,9 +134,54 @@ export const ImportDatabase: React.FC<ImportDatabaseProps> = ({
|
|||||||
if (!scriptResult.trim()) {
|
if (!scriptResult.trim()) {
|
||||||
setSqlValidation(null);
|
setSqlValidation(null);
|
||||||
setShowAutoFixButton(false);
|
setShowAutoFixButton(false);
|
||||||
|
setErrorMessage('');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (importMethod === 'dbml') {
|
||||||
|
// Validate DBML by parsing it
|
||||||
|
const validateResponse = verifyDBML(scriptResult, { databaseType });
|
||||||
|
if (!validateResponse.hasError) {
|
||||||
|
setErrorMessage('');
|
||||||
|
setSqlValidation({
|
||||||
|
isValid: true,
|
||||||
|
errors: [],
|
||||||
|
warnings: [],
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
let errorMsg = 'Invalid DBML syntax';
|
||||||
|
let line: number = 1;
|
||||||
|
|
||||||
|
if (validateResponse.parsedError) {
|
||||||
|
errorMsg = validateResponse.parsedError.message;
|
||||||
|
line = validateResponse.parsedError.line;
|
||||||
|
highlightErrorLine({
|
||||||
|
error: validateResponse.parsedError,
|
||||||
|
model: editorRef.current?.getModel(),
|
||||||
|
editorDecorationsCollection:
|
||||||
|
decorationsCollection.current,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setSqlValidation({
|
||||||
|
isValid: false,
|
||||||
|
errors: [
|
||||||
|
{
|
||||||
|
message: errorMsg,
|
||||||
|
line: line,
|
||||||
|
type: 'syntax' as const,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
warnings: [],
|
||||||
|
});
|
||||||
|
setErrorMessage(errorMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
setShowAutoFixButton(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SQL validation
|
||||||
// First run our validation based on database type
|
// First run our validation based on database type
|
||||||
const validation = validateSQL(scriptResult, databaseType);
|
const validation = validateSQL(scriptResult, databaseType);
|
||||||
setSqlValidation(validation);
|
setSqlValidation(validation);
|
||||||
@@ -192,7 +208,7 @@ export const ImportDatabase: React.FC<ImportDatabaseProps> = ({
|
|||||||
setErrorMessage(result.error);
|
setErrorMessage(result.error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, [importMethod, scriptResult, databaseType]);
|
}, [importMethod, scriptResult, databaseType, clearDecorations]);
|
||||||
|
|
||||||
// Check if the script result is a valid JSON
|
// Check if the script result is a valid JSON
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -320,6 +336,8 @@ export const ImportDatabase: React.FC<ImportDatabaseProps> = ({
|
|||||||
const handleEditorDidMount = useCallback(
|
const handleEditorDidMount = useCallback(
|
||||||
(editor: editor.IStandaloneCodeEditor) => {
|
(editor: editor.IStandaloneCodeEditor) => {
|
||||||
editorRef.current = editor;
|
editorRef.current = editor;
|
||||||
|
decorationsCollection.current =
|
||||||
|
editor.createDecorationsCollection();
|
||||||
|
|
||||||
// Cleanup previous disposable if it exists
|
// Cleanup previous disposable if it exists
|
||||||
if (pasteDisposableRef.current) {
|
if (pasteDisposableRef.current) {
|
||||||
@@ -338,7 +356,7 @@ export const ImportDatabase: React.FC<ImportDatabaseProps> = ({
|
|||||||
const isLargeFile = calculateIsLargeFile(content);
|
const isLargeFile = calculateIsLargeFile(content);
|
||||||
|
|
||||||
// First, detect content type to determine if we should switch modes
|
// First, detect content type to determine if we should switch modes
|
||||||
const detectedType = detectContentType(content);
|
const detectedType = detectImportMethod(content);
|
||||||
if (detectedType && detectedType !== importMethod) {
|
if (detectedType && detectedType !== importMethod) {
|
||||||
// Switch to the detected mode immediately
|
// Switch to the detected mode immediately
|
||||||
setImportMethod(detectedType);
|
setImportMethod(detectedType);
|
||||||
@@ -352,7 +370,7 @@ export const ImportDatabase: React.FC<ImportDatabaseProps> = ({
|
|||||||
?.run();
|
?.run();
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
// For DDL mode, do NOT format as it can break the SQL
|
// For DDL and DBML modes, do NOT format as it can break the syntax
|
||||||
} else {
|
} else {
|
||||||
// Content type didn't change, apply formatting based on current mode
|
// Content type didn't change, apply formatting based on current mode
|
||||||
if (importMethod === 'query' && !isLargeFile) {
|
if (importMethod === 'query' && !isLargeFile) {
|
||||||
@@ -363,7 +381,7 @@ export const ImportDatabase: React.FC<ImportDatabaseProps> = ({
|
|||||||
?.run();
|
?.run();
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
// For DDL mode or large files, do NOT format
|
// For DDL and DBML modes or large files, do NOT format
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -410,6 +428,8 @@ export const ImportDatabase: React.FC<ImportDatabaseProps> = ({
|
|||||||
<div className="w-full text-center text-xs text-muted-foreground">
|
<div className="w-full text-center text-xs text-muted-foreground">
|
||||||
{importMethod === 'query'
|
{importMethod === 'query'
|
||||||
? 'Smart Query Output'
|
? 'Smart Query Output'
|
||||||
|
: importMethod === 'dbml'
|
||||||
|
? 'DBML Script'
|
||||||
: 'SQL Script'}
|
: 'SQL Script'}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 overflow-hidden">
|
<div className="flex-1 overflow-hidden">
|
||||||
@@ -417,9 +437,16 @@ export const ImportDatabase: React.FC<ImportDatabaseProps> = ({
|
|||||||
<Editor
|
<Editor
|
||||||
value={scriptResult}
|
value={scriptResult}
|
||||||
onChange={debouncedHandleInputChange}
|
onChange={debouncedHandleInputChange}
|
||||||
language={importMethod === 'query' ? 'json' : 'sql'}
|
language={
|
||||||
|
importMethod === 'query'
|
||||||
|
? 'json'
|
||||||
|
: importMethod === 'dbml'
|
||||||
|
? 'dbml'
|
||||||
|
: 'sql'
|
||||||
|
}
|
||||||
loading={<Spinner />}
|
loading={<Spinner />}
|
||||||
onMount={handleEditorDidMount}
|
onMount={handleEditorDidMount}
|
||||||
|
beforeMount={setupDBMLLanguage}
|
||||||
theme={
|
theme={
|
||||||
effectiveTheme === 'dark'
|
effectiveTheme === 'dark'
|
||||||
? 'dbml-dark'
|
? 'dbml-dark'
|
||||||
@@ -430,7 +457,6 @@ export const ImportDatabase: React.FC<ImportDatabaseProps> = ({
|
|||||||
minimap: { enabled: false },
|
minimap: { enabled: false },
|
||||||
scrollBeyondLastLine: false,
|
scrollBeyondLastLine: false,
|
||||||
automaticLayout: true,
|
automaticLayout: true,
|
||||||
glyphMargin: false,
|
|
||||||
lineNumbers: 'on',
|
lineNumbers: 'on',
|
||||||
guides: {
|
guides: {
|
||||||
indentation: false,
|
indentation: false,
|
||||||
@@ -455,7 +481,9 @@ export const ImportDatabase: React.FC<ImportDatabaseProps> = ({
|
|||||||
</Suspense>
|
</Suspense>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{errorMessage || (importMethod === 'ddl' && sqlValidation) ? (
|
{errorMessage ||
|
||||||
|
((importMethod === 'ddl' || importMethod === 'dbml') &&
|
||||||
|
sqlValidation) ? (
|
||||||
<SQLValidationStatus
|
<SQLValidationStatus
|
||||||
validation={sqlValidation}
|
validation={sqlValidation}
|
||||||
errorMessage={errorMessage}
|
errorMessage={errorMessage}
|
||||||
|
|||||||
@@ -15,9 +15,11 @@ import {
|
|||||||
AvatarImage,
|
AvatarImage,
|
||||||
} from '@/components/avatar/avatar';
|
} from '@/components/avatar/avatar';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Code } from 'lucide-react';
|
import { Code, FileCode } from 'lucide-react';
|
||||||
import { SmartQueryInstructions } from './instructions/smart-query-instructions';
|
import { SmartQueryInstructions } from './instructions/smart-query-instructions';
|
||||||
import { DDLInstructions } from './instructions/ddl-instructions';
|
import { DDLInstructions } from './instructions/ddl-instructions';
|
||||||
|
import { DBMLInstructions } from './instructions/dbml-instructions';
|
||||||
|
import type { ImportMethod } from '@/lib/import-method/import-method';
|
||||||
|
|
||||||
const DatabasesWithoutDDLInstructions: DatabaseType[] = [
|
const DatabasesWithoutDDLInstructions: DatabaseType[] = [
|
||||||
DatabaseType.CLICKHOUSE,
|
DatabaseType.CLICKHOUSE,
|
||||||
@@ -30,8 +32,8 @@ export interface InstructionsSectionProps {
|
|||||||
setDatabaseEdition: React.Dispatch<
|
setDatabaseEdition: React.Dispatch<
|
||||||
React.SetStateAction<DatabaseEdition | undefined>
|
React.SetStateAction<DatabaseEdition | undefined>
|
||||||
>;
|
>;
|
||||||
importMethod: 'query' | 'ddl';
|
importMethod: ImportMethod;
|
||||||
setImportMethod: (method: 'query' | 'ddl') => void;
|
setImportMethod: (method: ImportMethod) => void;
|
||||||
showSSMSInfoDialog: boolean;
|
showSSMSInfoDialog: boolean;
|
||||||
setShowSSMSInfoDialog: (show: boolean) => void;
|
setShowSSMSInfoDialog: (show: boolean) => void;
|
||||||
}
|
}
|
||||||
@@ -115,7 +117,6 @@ export const InstructionsSection: React.FC<InstructionsSectionProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{DatabasesWithoutDDLInstructions.includes(databaseType) ? null : (
|
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<p className="text-sm leading-6 text-primary">
|
<p className="text-sm leading-6 text-primary">
|
||||||
How would you like to import?
|
How would you like to import?
|
||||||
@@ -125,9 +126,9 @@ export const InstructionsSection: React.FC<InstructionsSectionProps> = ({
|
|||||||
className="ml-1 flex-wrap justify-start gap-2"
|
className="ml-1 flex-wrap justify-start gap-2"
|
||||||
value={importMethod}
|
value={importMethod}
|
||||||
onValueChange={(value) => {
|
onValueChange={(value) => {
|
||||||
let selectedImportMethod: 'query' | 'ddl' = 'query';
|
let selectedImportMethod: ImportMethod = 'query';
|
||||||
if (value) {
|
if (value) {
|
||||||
selectedImportMethod = value as 'query' | 'ddl';
|
selectedImportMethod = value as ImportMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
setImportMethod(selectedImportMethod);
|
setImportMethod(selectedImportMethod);
|
||||||
@@ -144,19 +145,32 @@ export const InstructionsSection: React.FC<InstructionsSectionProps> = ({
|
|||||||
</Avatar>
|
</Avatar>
|
||||||
Smart Query
|
Smart Query
|
||||||
</ToggleGroupItem>
|
</ToggleGroupItem>
|
||||||
|
{!DatabasesWithoutDDLInstructions.includes(
|
||||||
|
databaseType
|
||||||
|
) && (
|
||||||
<ToggleGroupItem
|
<ToggleGroupItem
|
||||||
value="ddl"
|
value="ddl"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="h-6 gap-1 p-0 px-2 shadow-none data-[state=on]:bg-slate-200 dark:data-[state=on]:bg-slate-700"
|
className="h-6 gap-1 p-0 px-2 shadow-none data-[state=on]:bg-slate-200 dark:data-[state=on]:bg-slate-700"
|
||||||
>
|
>
|
||||||
<Avatar className="size-4 rounded-none">
|
<Avatar className="size-4 rounded-none">
|
||||||
<Code size={16} />
|
<FileCode size={16} />
|
||||||
</Avatar>
|
</Avatar>
|
||||||
SQL Script
|
SQL Script
|
||||||
</ToggleGroupItem>
|
</ToggleGroupItem>
|
||||||
|
)}
|
||||||
|
<ToggleGroupItem
|
||||||
|
value="dbml"
|
||||||
|
variant="outline"
|
||||||
|
className="h-6 gap-1 p-0 px-2 shadow-none data-[state=on]:bg-slate-200 dark:data-[state=on]:bg-slate-700"
|
||||||
|
>
|
||||||
|
<Avatar className="size-4 rounded-none">
|
||||||
|
<Code size={16} />
|
||||||
|
</Avatar>
|
||||||
|
DBML
|
||||||
|
</ToggleGroupItem>
|
||||||
</ToggleGroup>
|
</ToggleGroup>
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<div className="text-sm font-semibold">Instructions:</div>
|
<div className="text-sm font-semibold">Instructions:</div>
|
||||||
@@ -167,11 +181,16 @@ export const InstructionsSection: React.FC<InstructionsSectionProps> = ({
|
|||||||
showSSMSInfoDialog={showSSMSInfoDialog}
|
showSSMSInfoDialog={showSSMSInfoDialog}
|
||||||
setShowSSMSInfoDialog={setShowSSMSInfoDialog}
|
setShowSSMSInfoDialog={setShowSSMSInfoDialog}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : importMethod === 'ddl' ? (
|
||||||
<DDLInstructions
|
<DDLInstructions
|
||||||
databaseType={databaseType}
|
databaseType={databaseType}
|
||||||
databaseEdition={databaseEdition}
|
databaseEdition={databaseEdition}
|
||||||
/>
|
/>
|
||||||
|
) : (
|
||||||
|
<DBMLInstructions
|
||||||
|
databaseType={databaseType}
|
||||||
|
databaseEdition={databaseEdition}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import type { DatabaseType } from '@/lib/domain/database-type';
|
||||||
|
import type { DatabaseEdition } from '@/lib/domain/database-edition';
|
||||||
|
import { CodeSnippet } from '@/components/code-snippet/code-snippet';
|
||||||
|
import { setupDBMLLanguage } from '@/components/code-snippet/languages/dbml-language';
|
||||||
|
|
||||||
|
export interface DBMLInstructionsProps {
|
||||||
|
databaseType: DatabaseType;
|
||||||
|
databaseEdition?: DatabaseEdition;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DBMLInstructions: React.FC<DBMLInstructionsProps> = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="flex flex-col gap-1 text-sm text-primary">
|
||||||
|
<div>
|
||||||
|
Paste your DBML (Database Markup Language) schema definition
|
||||||
|
here →
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex h-64 flex-col gap-1 text-sm text-primary">
|
||||||
|
<h4 className="text-xs font-medium">Example:</h4>
|
||||||
|
<CodeSnippet
|
||||||
|
className="h-full"
|
||||||
|
allowCopy={false}
|
||||||
|
editorProps={{
|
||||||
|
beforeMount: setupDBMLLanguage,
|
||||||
|
}}
|
||||||
|
code={`Table users {
|
||||||
|
id int [pk]
|
||||||
|
username varchar
|
||||||
|
email varchar
|
||||||
|
}
|
||||||
|
|
||||||
|
Table posts {
|
||||||
|
id int [pk]
|
||||||
|
user_id int [ref: > users.id]
|
||||||
|
title varchar
|
||||||
|
content text
|
||||||
|
}`}
|
||||||
|
language={'dbml'}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -43,8 +43,8 @@ const DDLInstructionsMap: Record<DatabaseType, DDLInstruction[]> = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Execute the following command in your terminal:',
|
text: 'Execute the following command in your terminal:',
|
||||||
code: `sqlite3 <database_file_path>\n.dump > <output_file_path>`,
|
code: `sqlite3 <database_file_path>\n".schema" > <output_file_path>`,
|
||||||
example: `sqlite3 my_db.db\n.dump > schema_export.sql`,
|
example: `sqlite3 my_db.db\n".schema" > schema_export.sql`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Open the exported SQL file, copy its contents, and paste them here.',
|
text: 'Open the exported SQL file, copy its contents, and paste them here.',
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ export const SQLValidationStatus: React.FC<SQLValidationStatusProps> = ({
|
|||||||
|
|
||||||
{hasErrors ? (
|
{hasErrors ? (
|
||||||
<div className="rounded-md border border-red-200 bg-red-50 dark:border-red-800 dark:bg-red-950">
|
<div className="rounded-md border border-red-200 bg-red-50 dark:border-red-800 dark:bg-red-950">
|
||||||
<ScrollArea className="h-24">
|
<ScrollArea className="h-fit max-h-24">
|
||||||
<div className="space-y-3 p-3 pt-2 text-red-700 dark:text-red-300">
|
<div className="space-y-3 p-3 pt-2 text-red-700 dark:text-red-300">
|
||||||
{validation?.errors
|
{validation?.errors
|
||||||
.slice(0, 3)
|
.slice(0, 3)
|
||||||
@@ -137,7 +137,7 @@ export const SQLValidationStatus: React.FC<SQLValidationStatusProps> = ({
|
|||||||
|
|
||||||
{hasWarnings && !hasErrors ? (
|
{hasWarnings && !hasErrors ? (
|
||||||
<div className="rounded-md border border-sky-200 bg-sky-50 dark:border-sky-800 dark:bg-sky-950">
|
<div className="rounded-md border border-sky-200 bg-sky-50 dark:border-sky-800 dark:bg-sky-950">
|
||||||
<ScrollArea className="h-24">
|
<ScrollArea className="h-fit max-h-24">
|
||||||
<div className="space-y-3 p-3 pt-2 text-sky-700 dark:text-sky-300">
|
<div className="space-y-3 p-3 pt-2 text-sky-700 dark:text-sky-300">
|
||||||
<div className="flex items-start gap-2">
|
<div className="flex items-start gap-2">
|
||||||
<AlertTriangle className="mt-0.5 size-4 shrink-0 text-sky-700 dark:text-sky-300" />
|
<AlertTriangle className="mt-0.5 size-4 shrink-0 text-sky-700 dark:text-sky-300" />
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Dialog, DialogContent } from '@/components/dialog/dialog';
|
|||||||
import { DatabaseType } from '@/lib/domain/database-type';
|
import { DatabaseType } from '@/lib/domain/database-type';
|
||||||
import { useStorage } from '@/hooks/use-storage';
|
import { useStorage } from '@/hooks/use-storage';
|
||||||
import type { Diagram } from '@/lib/domain/diagram';
|
import type { Diagram } from '@/lib/domain/diagram';
|
||||||
import { loadFromDatabaseMetadata } from '@/lib/domain/diagram';
|
import { loadFromDatabaseMetadata } from '@/lib/data/import-metadata/import';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { useConfig } from '@/hooks/use-config';
|
import { useConfig } from '@/hooks/use-config';
|
||||||
import type { DatabaseMetadata } from '@/lib/data/import-metadata/metadata-types/database-metadata';
|
import type { DatabaseMetadata } from '@/lib/data/import-metadata/metadata-types/database-metadata';
|
||||||
@@ -22,6 +22,11 @@ import { sqlImportToDiagram } from '@/lib/data/sql-import';
|
|||||||
import type { SelectedTable } from '@/lib/data/import-metadata/filter-metadata';
|
import type { SelectedTable } from '@/lib/data/import-metadata/filter-metadata';
|
||||||
import { filterMetadataByTables } from '@/lib/data/import-metadata/filter-metadata';
|
import { filterMetadataByTables } from '@/lib/data/import-metadata/filter-metadata';
|
||||||
import { MAX_TABLES_WITHOUT_SHOWING_FILTER } from '../common/select-tables/constants';
|
import { MAX_TABLES_WITHOUT_SHOWING_FILTER } from '../common/select-tables/constants';
|
||||||
|
import {
|
||||||
|
defaultDBMLDiagramName,
|
||||||
|
importDBMLToDiagram,
|
||||||
|
} from '@/lib/dbml/dbml-import/dbml-import';
|
||||||
|
import type { ImportMethod } from '@/lib/import-method/import-method';
|
||||||
|
|
||||||
export interface CreateDiagramDialogProps extends BaseDialogProps {}
|
export interface CreateDiagramDialogProps extends BaseDialogProps {}
|
||||||
|
|
||||||
@@ -30,11 +35,11 @@ export const CreateDiagramDialog: React.FC<CreateDiagramDialogProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { diagramId } = useChartDB();
|
const { diagramId } = useChartDB();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [importMethod, setImportMethod] = useState<'query' | 'ddl'>('query');
|
const [importMethod, setImportMethod] = useState<ImportMethod>('query');
|
||||||
const [databaseType, setDatabaseType] = useState<DatabaseType>(
|
const [databaseType, setDatabaseType] = useState<DatabaseType>(
|
||||||
DatabaseType.GENERIC
|
DatabaseType.GENERIC
|
||||||
);
|
);
|
||||||
const { closeCreateDiagramDialog, openImportDBMLDialog } = useDialog();
|
const { closeCreateDiagramDialog } = useDialog();
|
||||||
const { updateConfig } = useConfig();
|
const { updateConfig } = useConfig();
|
||||||
const [scriptResult, setScriptResult] = useState('');
|
const [scriptResult, setScriptResult] = useState('');
|
||||||
const [databaseEdition, setDatabaseEdition] = useState<
|
const [databaseEdition, setDatabaseEdition] = useState<
|
||||||
@@ -89,6 +94,14 @@ export const CreateDiagramDialog: React.FC<CreateDiagramDialogProps> = ({
|
|||||||
sourceDatabaseType: databaseType,
|
sourceDatabaseType: databaseType,
|
||||||
targetDatabaseType: databaseType,
|
targetDatabaseType: databaseType,
|
||||||
});
|
});
|
||||||
|
} else if (importMethod === 'dbml') {
|
||||||
|
diagram = await importDBMLToDiagram(scriptResult, {
|
||||||
|
databaseType,
|
||||||
|
});
|
||||||
|
// Update the diagram name if it's the default
|
||||||
|
if (diagram.name === defaultDBMLDiagramName) {
|
||||||
|
diagram.name = `Diagram ${diagramNumber}`;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let metadata: DatabaseMetadata | undefined = databaseMetadata;
|
let metadata: DatabaseMetadata | undefined = databaseMetadata;
|
||||||
|
|
||||||
@@ -152,10 +165,6 @@ export const CreateDiagramDialog: React.FC<CreateDiagramDialogProps> = ({
|
|||||||
await updateConfig({ config: { defaultDiagramId: diagram.id } });
|
await updateConfig({ config: { defaultDiagramId: diagram.id } });
|
||||||
closeCreateDiagramDialog();
|
closeCreateDiagramDialog();
|
||||||
navigate(`/diagrams/${diagram.id}`);
|
navigate(`/diagrams/${diagram.id}`);
|
||||||
setTimeout(
|
|
||||||
() => openImportDBMLDialog({ withCreateEmptyDiagram: true }),
|
|
||||||
700
|
|
||||||
);
|
|
||||||
}, [
|
}, [
|
||||||
databaseType,
|
databaseType,
|
||||||
addDiagram,
|
addDiagram,
|
||||||
@@ -164,14 +173,13 @@ export const CreateDiagramDialog: React.FC<CreateDiagramDialogProps> = ({
|
|||||||
navigate,
|
navigate,
|
||||||
updateConfig,
|
updateConfig,
|
||||||
diagramNumber,
|
diagramNumber,
|
||||||
openImportDBMLDialog,
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const importNewDiagramOrFilterTables = useCallback(async () => {
|
const importNewDiagramOrFilterTables = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
setIsParsingMetadata(true);
|
setIsParsingMetadata(true);
|
||||||
|
|
||||||
if (importMethod === 'ddl') {
|
if (importMethod === 'ddl' || importMethod === 'dbml') {
|
||||||
await importNewDiagram();
|
await importNewDiagram();
|
||||||
} else {
|
} else {
|
||||||
// Parse metadata asynchronously to avoid blocking the UI
|
// Parse metadata asynchronously to avoid blocking the UI
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ export const SelectDatabase: React.FC<SelectDatabaseProps> = ({
|
|||||||
type="button"
|
type="button"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={createNewDiagram}
|
onClick={createNewDiagram}
|
||||||
|
disabled={databaseType === DatabaseType.GENERIC}
|
||||||
>
|
>
|
||||||
{t('new_diagram_dialog.empty_diagram')}
|
{t('new_diagram_dialog.empty_diagram')}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -218,8 +218,14 @@ export const CreateRelationshipDialog: React.FC<
|
|||||||
closeCreateRelationshipDialog();
|
closeCreateRelationshipDialog();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
modal={false}
|
||||||
|
>
|
||||||
|
<DialogContent
|
||||||
|
className="flex flex-col overflow-y-auto"
|
||||||
|
showClose
|
||||||
|
forceOverlay
|
||||||
|
onInteractOutside={(e) => e.preventDefault()}
|
||||||
>
|
>
|
||||||
<DialogContent className="flex flex-col overflow-y-auto" showClose>
|
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>
|
<DialogTitle>
|
||||||
{t('create_relationship_dialog.title')}
|
{t('create_relationship_dialog.title')}
|
||||||
|
|||||||
@@ -17,15 +17,21 @@ import { useDialog } from '@/hooks/use-dialog';
|
|||||||
import {
|
import {
|
||||||
exportBaseSQL,
|
exportBaseSQL,
|
||||||
exportSQL,
|
exportSQL,
|
||||||
} from '@/lib/data/export-metadata/export-sql-script';
|
} from '@/lib/data/sql-export/export-sql-script';
|
||||||
import { databaseTypeToLabelMap } from '@/lib/databases';
|
import { databaseTypeToLabelMap } from '@/lib/databases';
|
||||||
import { DatabaseType } from '@/lib/domain/database-type';
|
import { DatabaseType } from '@/lib/domain/database-type';
|
||||||
import { shouldShowTablesBySchemaFilter } from '@/lib/domain/db-table';
|
|
||||||
import { Annoyed, Sparkles } from 'lucide-react';
|
import { Annoyed, Sparkles } from 'lucide-react';
|
||||||
import React, { useCallback, useEffect, useRef } from 'react';
|
import React, { useCallback, useEffect, useRef } from 'react';
|
||||||
import { Trans, useTranslation } from 'react-i18next';
|
import { Trans, useTranslation } from 'react-i18next';
|
||||||
import type { BaseDialogProps } from '../common/base-dialog-props';
|
import type { BaseDialogProps } from '../common/base-dialog-props';
|
||||||
import type { Diagram } from '@/lib/domain/diagram';
|
import type { Diagram } from '@/lib/domain/diagram';
|
||||||
|
import { useDiagramFilter } from '@/context/diagram-filter-context/use-diagram-filter';
|
||||||
|
import {
|
||||||
|
filterDependency,
|
||||||
|
filterRelationship,
|
||||||
|
filterTable,
|
||||||
|
} from '@/lib/domain/diagram-filter/filter';
|
||||||
|
import { defaultSchemas } from '@/lib/data/default-schemas';
|
||||||
|
|
||||||
export interface ExportSQLDialogProps extends BaseDialogProps {
|
export interface ExportSQLDialogProps extends BaseDialogProps {
|
||||||
targetDatabaseType: DatabaseType;
|
targetDatabaseType: DatabaseType;
|
||||||
@@ -36,7 +42,8 @@ export const ExportSQLDialog: React.FC<ExportSQLDialogProps> = ({
|
|||||||
targetDatabaseType,
|
targetDatabaseType,
|
||||||
}) => {
|
}) => {
|
||||||
const { closeExportSQLDialog } = useDialog();
|
const { closeExportSQLDialog } = useDialog();
|
||||||
const { currentDiagram, filteredSchemas } = useChartDB();
|
const { currentDiagram } = useChartDB();
|
||||||
|
const { filter } = useDiagramFilter();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [script, setScript] = React.useState<string>();
|
const [script, setScript] = React.useState<string>();
|
||||||
const [error, setError] = React.useState<boolean>(false);
|
const [error, setError] = React.useState<boolean>(false);
|
||||||
@@ -48,7 +55,16 @@ export const ExportSQLDialog: React.FC<ExportSQLDialogProps> = ({
|
|||||||
const filteredDiagram: Diagram = {
|
const filteredDiagram: Diagram = {
|
||||||
...currentDiagram,
|
...currentDiagram,
|
||||||
tables: currentDiagram.tables?.filter((table) =>
|
tables: currentDiagram.tables?.filter((table) =>
|
||||||
shouldShowTablesBySchemaFilter(table, filteredSchemas)
|
filterTable({
|
||||||
|
table: {
|
||||||
|
id: table.id,
|
||||||
|
schema: table.schema,
|
||||||
|
},
|
||||||
|
filter,
|
||||||
|
options: {
|
||||||
|
defaultSchema: defaultSchemas[targetDatabaseType],
|
||||||
|
},
|
||||||
|
})
|
||||||
),
|
),
|
||||||
relationships: currentDiagram.relationships?.filter((rel) => {
|
relationships: currentDiagram.relationships?.filter((rel) => {
|
||||||
const sourceTable = currentDiagram.tables?.find(
|
const sourceTable = currentDiagram.tables?.find(
|
||||||
@@ -60,11 +76,20 @@ export const ExportSQLDialog: React.FC<ExportSQLDialogProps> = ({
|
|||||||
return (
|
return (
|
||||||
sourceTable &&
|
sourceTable &&
|
||||||
targetTable &&
|
targetTable &&
|
||||||
shouldShowTablesBySchemaFilter(
|
filterRelationship({
|
||||||
sourceTable,
|
tableA: {
|
||||||
filteredSchemas
|
id: sourceTable.id,
|
||||||
) &&
|
schema: sourceTable.schema,
|
||||||
shouldShowTablesBySchemaFilter(targetTable, filteredSchemas)
|
},
|
||||||
|
tableB: {
|
||||||
|
id: targetTable.id,
|
||||||
|
schema: targetTable.schema,
|
||||||
|
},
|
||||||
|
filter,
|
||||||
|
options: {
|
||||||
|
defaultSchema: defaultSchemas[targetDatabaseType],
|
||||||
|
},
|
||||||
|
})
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
dependencies: currentDiagram.dependencies?.filter((dep) => {
|
dependencies: currentDiagram.dependencies?.filter((dep) => {
|
||||||
@@ -77,11 +102,20 @@ export const ExportSQLDialog: React.FC<ExportSQLDialogProps> = ({
|
|||||||
return (
|
return (
|
||||||
table &&
|
table &&
|
||||||
dependentTable &&
|
dependentTable &&
|
||||||
shouldShowTablesBySchemaFilter(table, filteredSchemas) &&
|
filterDependency({
|
||||||
shouldShowTablesBySchemaFilter(
|
tableA: {
|
||||||
dependentTable,
|
id: table.id,
|
||||||
filteredSchemas
|
schema: table.schema,
|
||||||
)
|
},
|
||||||
|
tableB: {
|
||||||
|
id: dependentTable.id,
|
||||||
|
schema: dependentTable.schema,
|
||||||
|
},
|
||||||
|
filter,
|
||||||
|
options: {
|
||||||
|
defaultSchema: defaultSchemas[targetDatabaseType],
|
||||||
|
},
|
||||||
|
})
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
@@ -101,7 +135,7 @@ export const ExportSQLDialog: React.FC<ExportSQLDialogProps> = ({
|
|||||||
signal: abortControllerRef.current?.signal,
|
signal: abortControllerRef.current?.signal,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [targetDatabaseType, currentDiagram, filteredSchemas]);
|
}, [targetDatabaseType, currentDiagram, filter]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!dialog.open) {
|
if (!dialog.open) {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import type { DatabaseEdition } from '@/lib/domain/database-edition';
|
|||||||
import type { DatabaseMetadata } from '@/lib/data/import-metadata/metadata-types/database-metadata';
|
import type { DatabaseMetadata } from '@/lib/data/import-metadata/metadata-types/database-metadata';
|
||||||
import { loadDatabaseMetadata } from '@/lib/data/import-metadata/metadata-types/database-metadata';
|
import { loadDatabaseMetadata } from '@/lib/data/import-metadata/metadata-types/database-metadata';
|
||||||
import type { Diagram } from '@/lib/domain/diagram';
|
import type { Diagram } from '@/lib/domain/diagram';
|
||||||
import { loadFromDatabaseMetadata } from '@/lib/domain/diagram';
|
import { loadFromDatabaseMetadata } from '@/lib/data/import-metadata/import';
|
||||||
import { useChartDB } from '@/hooks/use-chartdb';
|
import { useChartDB } from '@/hooks/use-chartdb';
|
||||||
import { useRedoUndoStack } from '@/hooks/use-redo-undo-stack';
|
import { useRedoUndoStack } from '@/hooks/use-redo-undo-stack';
|
||||||
import { Trans, useTranslation } from 'react-i18next';
|
import { Trans, useTranslation } from 'react-i18next';
|
||||||
@@ -15,6 +15,8 @@ import { useReactFlow } from '@xyflow/react';
|
|||||||
import type { BaseDialogProps } from '../common/base-dialog-props';
|
import type { BaseDialogProps } from '../common/base-dialog-props';
|
||||||
import { useAlert } from '@/context/alert-context/alert-context';
|
import { useAlert } from '@/context/alert-context/alert-context';
|
||||||
import { sqlImportToDiagram } from '@/lib/data/sql-import';
|
import { sqlImportToDiagram } from '@/lib/data/sql-import';
|
||||||
|
import { importDBMLToDiagram } from '@/lib/dbml/dbml-import/dbml-import';
|
||||||
|
import type { ImportMethod } from '@/lib/import-method/import-method';
|
||||||
|
|
||||||
export interface ImportDatabaseDialogProps extends BaseDialogProps {
|
export interface ImportDatabaseDialogProps extends BaseDialogProps {
|
||||||
databaseType: DatabaseType;
|
databaseType: DatabaseType;
|
||||||
@@ -24,7 +26,7 @@ export const ImportDatabaseDialog: React.FC<ImportDatabaseDialogProps> = ({
|
|||||||
dialog,
|
dialog,
|
||||||
databaseType,
|
databaseType,
|
||||||
}) => {
|
}) => {
|
||||||
const [importMethod, setImportMethod] = useState<'query' | 'ddl'>('query');
|
const [importMethod, setImportMethod] = useState<ImportMethod>('query');
|
||||||
const { closeImportDatabaseDialog } = useDialog();
|
const { closeImportDatabaseDialog } = useDialog();
|
||||||
const { showAlert } = useAlert();
|
const { showAlert } = useAlert();
|
||||||
const {
|
const {
|
||||||
@@ -65,6 +67,10 @@ export const ImportDatabaseDialog: React.FC<ImportDatabaseDialogProps> = ({
|
|||||||
sourceDatabaseType: databaseType,
|
sourceDatabaseType: databaseType,
|
||||||
targetDatabaseType: databaseType,
|
targetDatabaseType: databaseType,
|
||||||
});
|
});
|
||||||
|
} else if (importMethod === 'dbml') {
|
||||||
|
diagram = await importDBMLToDiagram(scriptResult, {
|
||||||
|
databaseType,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
const databaseMetadata: DatabaseMetadata =
|
const databaseMetadata: DatabaseMetadata =
|
||||||
loadDatabaseMetadata(scriptResult);
|
loadDatabaseMetadata(scriptResult);
|
||||||
|
|||||||
@@ -1,359 +0,0 @@
|
|||||||
import React, {
|
|
||||||
useCallback,
|
|
||||||
useEffect,
|
|
||||||
useState,
|
|
||||||
Suspense,
|
|
||||||
useRef,
|
|
||||||
} from 'react';
|
|
||||||
import type * as monaco from 'monaco-editor';
|
|
||||||
import { useDialog } from '@/hooks/use-dialog';
|
|
||||||
import {
|
|
||||||
Dialog,
|
|
||||||
DialogClose,
|
|
||||||
DialogContent,
|
|
||||||
DialogDescription,
|
|
||||||
DialogFooter,
|
|
||||||
DialogHeader,
|
|
||||||
DialogInternalContent,
|
|
||||||
DialogTitle,
|
|
||||||
} from '@/components/dialog/dialog';
|
|
||||||
import { Button } from '@/components/button/button';
|
|
||||||
import type { BaseDialogProps } from '../common/base-dialog-props';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { Editor } from '@/components/code-snippet/code-snippet';
|
|
||||||
import { useTheme } from '@/hooks/use-theme';
|
|
||||||
import { AlertCircle } from 'lucide-react';
|
|
||||||
import {
|
|
||||||
importDBMLToDiagram,
|
|
||||||
sanitizeDBML,
|
|
||||||
preprocessDBML,
|
|
||||||
} from '@/lib/dbml/dbml-import/dbml-import';
|
|
||||||
import { useChartDB } from '@/hooks/use-chartdb';
|
|
||||||
import { Parser } from '@dbml/core';
|
|
||||||
import { useCanvas } from '@/hooks/use-canvas';
|
|
||||||
import { setupDBMLLanguage } from '@/components/code-snippet/languages/dbml-language';
|
|
||||||
import type { DBTable } from '@/lib/domain/db-table';
|
|
||||||
import { useToast } from '@/components/toast/use-toast';
|
|
||||||
import { Spinner } from '@/components/spinner/spinner';
|
|
||||||
import { debounce } from '@/lib/utils';
|
|
||||||
import { parseDBMLError } from '@/lib/dbml/dbml-import/dbml-import-error';
|
|
||||||
import {
|
|
||||||
clearErrorHighlight,
|
|
||||||
highlightErrorLine,
|
|
||||||
} from '@/components/code-snippet/dbml/utils';
|
|
||||||
|
|
||||||
export interface ImportDBMLDialogProps extends BaseDialogProps {
|
|
||||||
withCreateEmptyDiagram?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ImportDBMLDialog: React.FC<ImportDBMLDialogProps> = ({
|
|
||||||
dialog,
|
|
||||||
withCreateEmptyDiagram,
|
|
||||||
}) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const initialDBML = `// Use DBML to define your database structure
|
|
||||||
// Simple Blog System with Comments Example
|
|
||||||
|
|
||||||
Table users {
|
|
||||||
id integer [primary key]
|
|
||||||
name varchar
|
|
||||||
email varchar
|
|
||||||
}
|
|
||||||
|
|
||||||
Table posts {
|
|
||||||
id integer [primary key]
|
|
||||||
title varchar
|
|
||||||
content text
|
|
||||||
user_id integer
|
|
||||||
created_at timestamp
|
|
||||||
}
|
|
||||||
|
|
||||||
Table comments {
|
|
||||||
id integer [primary key]
|
|
||||||
content text
|
|
||||||
post_id integer
|
|
||||||
user_id integer
|
|
||||||
created_at timestamp
|
|
||||||
}
|
|
||||||
|
|
||||||
// Relationships
|
|
||||||
Ref: posts.user_id > users.id // Each post belongs to one user
|
|
||||||
Ref: comments.post_id > posts.id // Each comment belongs to one post
|
|
||||||
Ref: comments.user_id > users.id // Each comment is written by one user`;
|
|
||||||
|
|
||||||
const [dbmlContent, setDBMLContent] = useState<string>(initialDBML);
|
|
||||||
const { closeImportDBMLDialog } = useDialog();
|
|
||||||
const [errorMessage, setErrorMessage] = useState<string | undefined>();
|
|
||||||
const { effectiveTheme } = useTheme();
|
|
||||||
const { toast } = useToast();
|
|
||||||
const {
|
|
||||||
addTables,
|
|
||||||
addRelationships,
|
|
||||||
tables,
|
|
||||||
relationships,
|
|
||||||
removeTables,
|
|
||||||
removeRelationships,
|
|
||||||
} = useChartDB();
|
|
||||||
const { reorderTables } = useCanvas();
|
|
||||||
const [reorder, setReorder] = useState(false);
|
|
||||||
const editorRef = useRef<monaco.editor.IStandaloneCodeEditor>();
|
|
||||||
const decorationsCollection =
|
|
||||||
useRef<monaco.editor.IEditorDecorationsCollection>();
|
|
||||||
|
|
||||||
const handleEditorDidMount = (
|
|
||||||
editor: monaco.editor.IStandaloneCodeEditor
|
|
||||||
) => {
|
|
||||||
editorRef.current = editor;
|
|
||||||
decorationsCollection.current = editor.createDecorationsCollection();
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (reorder) {
|
|
||||||
reorderTables({
|
|
||||||
updateHistory: false,
|
|
||||||
});
|
|
||||||
setReorder(false);
|
|
||||||
}
|
|
||||||
}, [reorder, reorderTables]);
|
|
||||||
|
|
||||||
const clearDecorations = useCallback(() => {
|
|
||||||
clearErrorHighlight(decorationsCollection.current);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const validateDBML = useCallback(
|
|
||||||
async (content: string) => {
|
|
||||||
// Clear previous errors
|
|
||||||
setErrorMessage(undefined);
|
|
||||||
clearDecorations();
|
|
||||||
|
|
||||||
if (!content.trim()) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const preprocessedContent = preprocessDBML(content);
|
|
||||||
const sanitizedContent = sanitizeDBML(preprocessedContent);
|
|
||||||
const parser = new Parser();
|
|
||||||
parser.parse(sanitizedContent, 'dbml');
|
|
||||||
} catch (e) {
|
|
||||||
const parsedError = parseDBMLError(e);
|
|
||||||
if (parsedError) {
|
|
||||||
setErrorMessage(
|
|
||||||
t('import_dbml_dialog.error.description') +
|
|
||||||
` (1 error found - in line ${parsedError.line})`
|
|
||||||
);
|
|
||||||
highlightErrorLine({
|
|
||||||
error: parsedError,
|
|
||||||
model: editorRef.current?.getModel(),
|
|
||||||
editorDecorationsCollection:
|
|
||||||
decorationsCollection.current,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
setErrorMessage(
|
|
||||||
e instanceof Error ? e.message : JSON.stringify(e)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[clearDecorations, t]
|
|
||||||
);
|
|
||||||
|
|
||||||
const debouncedValidateRef = useRef<((value: string) => void) | null>(null);
|
|
||||||
|
|
||||||
// Set up debounced validation
|
|
||||||
useEffect(() => {
|
|
||||||
debouncedValidateRef.current = debounce((value: string) => {
|
|
||||||
validateDBML(value);
|
|
||||||
}, 500);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
debouncedValidateRef.current = null;
|
|
||||||
};
|
|
||||||
}, [validateDBML]);
|
|
||||||
|
|
||||||
// Trigger validation when content changes
|
|
||||||
useEffect(() => {
|
|
||||||
if (debouncedValidateRef.current) {
|
|
||||||
debouncedValidateRef.current(dbmlContent);
|
|
||||||
}
|
|
||||||
}, [dbmlContent]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!dialog.open) {
|
|
||||||
setErrorMessage(undefined);
|
|
||||||
clearDecorations();
|
|
||||||
setDBMLContent(initialDBML);
|
|
||||||
}
|
|
||||||
}, [dialog.open, initialDBML, clearDecorations]);
|
|
||||||
|
|
||||||
const handleImport = useCallback(async () => {
|
|
||||||
if (!dbmlContent.trim() || errorMessage) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const importedDiagram = await importDBMLToDiagram(dbmlContent);
|
|
||||||
const tableIdsToRemove = tables
|
|
||||||
.filter((table) =>
|
|
||||||
importedDiagram.tables?.some(
|
|
||||||
(t: DBTable) =>
|
|
||||||
t.name === table.name && t.schema === table.schema
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.map((table) => table.id);
|
|
||||||
// Find relationships that need to be removed
|
|
||||||
const relationshipIdsToRemove = relationships
|
|
||||||
.filter((relationship) => {
|
|
||||||
const sourceTable = tables.find(
|
|
||||||
(table: DBTable) =>
|
|
||||||
table.id === relationship.sourceTableId
|
|
||||||
);
|
|
||||||
const targetTable = tables.find(
|
|
||||||
(table: DBTable) =>
|
|
||||||
table.id === relationship.targetTableId
|
|
||||||
);
|
|
||||||
if (!sourceTable || !targetTable) return true;
|
|
||||||
const replacementSourceTable = importedDiagram.tables?.find(
|
|
||||||
(table: DBTable) =>
|
|
||||||
table.name === sourceTable.name &&
|
|
||||||
table.schema === sourceTable.schema
|
|
||||||
);
|
|
||||||
const replacementTargetTable = importedDiagram.tables?.find(
|
|
||||||
(table: DBTable) =>
|
|
||||||
table.name === targetTable.name &&
|
|
||||||
table.schema === targetTable.schema
|
|
||||||
);
|
|
||||||
return replacementSourceTable || replacementTargetTable;
|
|
||||||
})
|
|
||||||
.map((relationship) => relationship.id);
|
|
||||||
|
|
||||||
// Remove existing items
|
|
||||||
await Promise.all([
|
|
||||||
removeTables(tableIdsToRemove, { updateHistory: false }),
|
|
||||||
removeRelationships(relationshipIdsToRemove, {
|
|
||||||
updateHistory: false,
|
|
||||||
}),
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Add new items
|
|
||||||
await Promise.all([
|
|
||||||
addTables(importedDiagram.tables ?? [], {
|
|
||||||
updateHistory: false,
|
|
||||||
}),
|
|
||||||
addRelationships(importedDiagram.relationships ?? [], {
|
|
||||||
updateHistory: false,
|
|
||||||
}),
|
|
||||||
]);
|
|
||||||
setReorder(true);
|
|
||||||
closeImportDBMLDialog();
|
|
||||||
} catch (e) {
|
|
||||||
toast({
|
|
||||||
title: t('import_dbml_dialog.error.title'),
|
|
||||||
variant: 'destructive',
|
|
||||||
description: (
|
|
||||||
<>
|
|
||||||
<div>{t('import_dbml_dialog.error.description')}</div>
|
|
||||||
{e instanceof Error ? e.message : JSON.stringify(e)}
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [
|
|
||||||
dbmlContent,
|
|
||||||
closeImportDBMLDialog,
|
|
||||||
tables,
|
|
||||||
relationships,
|
|
||||||
removeTables,
|
|
||||||
removeRelationships,
|
|
||||||
addTables,
|
|
||||||
addRelationships,
|
|
||||||
errorMessage,
|
|
||||||
toast,
|
|
||||||
setReorder,
|
|
||||||
t,
|
|
||||||
]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dialog
|
|
||||||
{...dialog}
|
|
||||||
onOpenChange={(open) => {
|
|
||||||
if (!open) {
|
|
||||||
closeImportDBMLDialog();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<DialogContent
|
|
||||||
className="flex h-[80vh] max-h-screen w-full flex-col md:max-w-[900px]"
|
|
||||||
showClose
|
|
||||||
>
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle>
|
|
||||||
{withCreateEmptyDiagram
|
|
||||||
? t('import_dbml_dialog.example_title')
|
|
||||||
: t('import_dbml_dialog.title')}
|
|
||||||
</DialogTitle>
|
|
||||||
<DialogDescription>
|
|
||||||
{t('import_dbml_dialog.description')}
|
|
||||||
</DialogDescription>
|
|
||||||
</DialogHeader>
|
|
||||||
<DialogInternalContent>
|
|
||||||
<Suspense fallback={<Spinner />}>
|
|
||||||
<Editor
|
|
||||||
value={dbmlContent}
|
|
||||||
onChange={(value) => setDBMLContent(value || '')}
|
|
||||||
language="dbml"
|
|
||||||
onMount={handleEditorDidMount}
|
|
||||||
theme={
|
|
||||||
effectiveTheme === 'dark'
|
|
||||||
? 'dbml-dark'
|
|
||||||
: 'dbml-light'
|
|
||||||
}
|
|
||||||
beforeMount={setupDBMLLanguage}
|
|
||||||
options={{
|
|
||||||
minimap: { enabled: false },
|
|
||||||
scrollBeyondLastLine: false,
|
|
||||||
automaticLayout: true,
|
|
||||||
glyphMargin: true,
|
|
||||||
lineNumbers: 'on',
|
|
||||||
scrollbar: {
|
|
||||||
vertical: 'visible',
|
|
||||||
horizontal: 'visible',
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
className="size-full"
|
|
||||||
/>
|
|
||||||
</Suspense>
|
|
||||||
</DialogInternalContent>
|
|
||||||
<DialogFooter>
|
|
||||||
<div className="flex w-full items-center justify-between">
|
|
||||||
<div className="flex items-center gap-4">
|
|
||||||
<DialogClose asChild>
|
|
||||||
<Button variant="secondary">
|
|
||||||
{withCreateEmptyDiagram
|
|
||||||
? t('import_dbml_dialog.skip_and_empty')
|
|
||||||
: t('import_dbml_dialog.cancel')}
|
|
||||||
</Button>
|
|
||||||
</DialogClose>
|
|
||||||
{errorMessage ? (
|
|
||||||
<div className="flex items-center gap-1">
|
|
||||||
<AlertCircle className="size-4 text-destructive" />
|
|
||||||
|
|
||||||
<span className="text-xs text-destructive">
|
|
||||||
{errorMessage ||
|
|
||||||
t(
|
|
||||||
'import_dbml_dialog.error.description'
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
<Button
|
|
||||||
onClick={handleImport}
|
|
||||||
disabled={!dbmlContent.trim() || !!errorMessage}
|
|
||||||
>
|
|
||||||
{withCreateEmptyDiagram
|
|
||||||
? t('import_dbml_dialog.show_example')
|
|
||||||
: t('import_dbml_dialog.import')}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</DialogFooter>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
import React, { useCallback } from 'react';
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuSeparator,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from '@/components/dropdown-menu/dropdown-menu';
|
||||||
|
import { Button } from '@/components/button/button';
|
||||||
|
import { Ellipsis, Layers2, SquareArrowOutUpRight, Trash2 } from 'lucide-react';
|
||||||
|
import { useChartDB } from '@/hooks/use-chartdb';
|
||||||
|
import type { Diagram } from '@/lib/domain';
|
||||||
|
import { useStorage } from '@/hooks/use-storage';
|
||||||
|
import { cloneDiagram } from '@/lib/clone';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
interface DiagramRowActionsMenuProps {
|
||||||
|
diagram: Diagram;
|
||||||
|
onOpen: () => void;
|
||||||
|
refetch: () => void;
|
||||||
|
numberOfDiagrams: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DiagramRowActionsMenu: React.FC<DiagramRowActionsMenuProps> = ({
|
||||||
|
diagram,
|
||||||
|
onOpen,
|
||||||
|
refetch,
|
||||||
|
numberOfDiagrams,
|
||||||
|
}) => {
|
||||||
|
const { diagramId } = useChartDB();
|
||||||
|
const { deleteDiagram, addDiagram } = useStorage();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const onDelete = useCallback(async () => {
|
||||||
|
deleteDiagram(diagram.id);
|
||||||
|
refetch();
|
||||||
|
|
||||||
|
if (diagram.id === diagramId || numberOfDiagrams <= 1) {
|
||||||
|
window.location.href = '/';
|
||||||
|
}
|
||||||
|
}, [deleteDiagram, diagram.id, diagramId, refetch, numberOfDiagrams]);
|
||||||
|
|
||||||
|
const onDuplicate = useCallback(async () => {
|
||||||
|
const duplicatedDiagram = cloneDiagram(diagram);
|
||||||
|
|
||||||
|
const diagramToAdd = duplicatedDiagram.diagram;
|
||||||
|
|
||||||
|
if (!diagramToAdd) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
diagramToAdd.name = `${diagram.name} (Copy)`;
|
||||||
|
|
||||||
|
addDiagram({ diagram: diagramToAdd });
|
||||||
|
refetch();
|
||||||
|
}, [addDiagram, refetch, diagram]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
className="size-8 p-0"
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
<Ellipsis className="size-4" />
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="end">
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={onOpen}
|
||||||
|
className="flex justify-between gap-4"
|
||||||
|
>
|
||||||
|
{t('open_diagram_dialog.diagram_actions.open')}
|
||||||
|
<SquareArrowOutUpRight className="size-3.5" />
|
||||||
|
</DropdownMenuItem>
|
||||||
|
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={onDuplicate}
|
||||||
|
className="flex justify-between gap-4"
|
||||||
|
>
|
||||||
|
{t('open_diagram_dialog.diagram_actions.duplicate')}
|
||||||
|
<Layers2 className="size-3.5" />
|
||||||
|
</DropdownMenuItem>
|
||||||
|
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={onDelete}
|
||||||
|
className="flex justify-between gap-4 text-red-700"
|
||||||
|
>
|
||||||
|
{t('open_diagram_dialog.diagram_actions.delete')}
|
||||||
|
<Trash2 className="size-3.5 text-red-700" />
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -27,6 +27,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import type { BaseDialogProps } from '../common/base-dialog-props';
|
import type { BaseDialogProps } from '../common/base-dialog-props';
|
||||||
import { useDebounce } from '@/hooks/use-debounce';
|
import { useDebounce } from '@/hooks/use-debounce';
|
||||||
|
import { DiagramRowActionsMenu } from './diagram-row-actions-menu/diagram-row-actions-menu';
|
||||||
|
|
||||||
export interface OpenDiagramDialogProps extends BaseDialogProps {
|
export interface OpenDiagramDialogProps extends BaseDialogProps {
|
||||||
canClose?: boolean;
|
canClose?: boolean;
|
||||||
@@ -46,21 +47,22 @@ export const OpenDiagramDialog: React.FC<OpenDiagramDialogProps> = ({
|
|||||||
string | undefined
|
string | undefined
|
||||||
>();
|
>();
|
||||||
|
|
||||||
useEffect(() => {
|
const fetchDiagrams = useCallback(async () => {
|
||||||
setSelectedDiagramId(undefined);
|
|
||||||
}, [dialog.open]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const fetchDiagrams = async () => {
|
|
||||||
const diagrams = await listDiagrams({ includeTables: true });
|
const diagrams = await listDiagrams({ includeTables: true });
|
||||||
setDiagrams(
|
setDiagrams(
|
||||||
diagrams.sort(
|
diagrams.sort(
|
||||||
(a, b) => b.updatedAt.getTime() - a.updatedAt.getTime()
|
(a, b) => b.updatedAt.getTime() - a.updatedAt.getTime()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
};
|
}, [listDiagrams]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!dialog.open) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setSelectedDiagramId(undefined);
|
||||||
fetchDiagrams();
|
fetchDiagrams();
|
||||||
}, [listDiagrams, setDiagrams, dialog.open]);
|
}, [dialog.open, fetchDiagrams]);
|
||||||
|
|
||||||
const openDiagram = useCallback(
|
const openDiagram = useCallback(
|
||||||
(diagramId: string) => {
|
(diagramId: string) => {
|
||||||
@@ -166,6 +168,7 @@ export const OpenDiagramDialog: React.FC<OpenDiagramDialogProps> = ({
|
|||||||
'open_diagram_dialog.table_columns.tables_count'
|
'open_diagram_dialog.table_columns.tables_count'
|
||||||
)}
|
)}
|
||||||
</TableHead>
|
</TableHead>
|
||||||
|
<TableHead />
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
@@ -221,6 +224,19 @@ export const OpenDiagramDialog: React.FC<OpenDiagramDialogProps> = ({
|
|||||||
<TableCell className="text-center">
|
<TableCell className="text-center">
|
||||||
{diagram.tables?.length}
|
{diagram.tables?.length}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
<TableCell className="items-center p-0 pr-1 text-right">
|
||||||
|
<DiagramRowActionsMenu
|
||||||
|
diagram={diagram}
|
||||||
|
onOpen={() => {
|
||||||
|
openDiagram(diagram.id);
|
||||||
|
closeOpenDiagramDialog();
|
||||||
|
}}
|
||||||
|
numberOfDiagrams={
|
||||||
|
diagrams.length
|
||||||
|
}
|
||||||
|
refetch={fetchDiagrams}
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
))}
|
))}
|
||||||
</TableBody>
|
</TableBody>
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ export const TableSchemaDialog: React.FC<TableSchemaDialogProps> = ({
|
|||||||
allowSchemaCreation = false,
|
allowSchemaCreation = false,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { databaseType, filteredSchemas, filterSchemas } = useChartDB();
|
const { databaseType } = useChartDB();
|
||||||
const [selectedSchemaId, setSelectedSchemaId] = useState<string>(
|
const [selectedSchemaId, setSelectedSchemaId] = useState<string>(
|
||||||
table?.schema
|
table?.schema
|
||||||
? schemaNameToSchemaId(table.schema)
|
? schemaNameToSchemaId(table.schema)
|
||||||
@@ -93,7 +93,6 @@ export const TableSchemaDialog: React.FC<TableSchemaDialogProps> = ({
|
|||||||
const { closeTableSchemaDialog } = useDialog();
|
const { closeTableSchemaDialog } = useDialog();
|
||||||
|
|
||||||
const handleConfirm = useCallback(() => {
|
const handleConfirm = useCallback(() => {
|
||||||
let createdSchemaId: string;
|
|
||||||
if (isCreatingNew && newSchemaName.trim()) {
|
if (isCreatingNew && newSchemaName.trim()) {
|
||||||
const newSchema: DBSchema = {
|
const newSchema: DBSchema = {
|
||||||
id: schemaNameToSchemaId(newSchemaName.trim()),
|
id: schemaNameToSchemaId(newSchemaName.trim()),
|
||||||
@@ -101,30 +100,14 @@ export const TableSchemaDialog: React.FC<TableSchemaDialogProps> = ({
|
|||||||
tableCount: 0,
|
tableCount: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
createdSchemaId = newSchema.id;
|
|
||||||
|
|
||||||
onConfirm({ schema: newSchema });
|
onConfirm({ schema: newSchema });
|
||||||
} else {
|
} else {
|
||||||
const schema = schemas.find((s) => s.id === selectedSchemaId);
|
const schema = schemas.find((s) => s.id === selectedSchemaId);
|
||||||
if (!schema) return;
|
if (!schema) return;
|
||||||
|
|
||||||
createdSchemaId = schema.id;
|
|
||||||
onConfirm({ schema });
|
onConfirm({ schema });
|
||||||
}
|
}
|
||||||
|
}, [onConfirm, selectedSchemaId, schemas, isCreatingNew, newSchemaName]);
|
||||||
filterSchemas([
|
|
||||||
...(filteredSchemas ?? schemas.map((s) => s.id)),
|
|
||||||
createdSchemaId,
|
|
||||||
]);
|
|
||||||
}, [
|
|
||||||
onConfirm,
|
|
||||||
selectedSchemaId,
|
|
||||||
schemas,
|
|
||||||
isCreatingNew,
|
|
||||||
newSchemaName,
|
|
||||||
filteredSchemas,
|
|
||||||
filterSchemas,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const schemaOptions: SelectBoxOption[] = useMemo(
|
const schemaOptions: SelectBoxOption[] = useMemo(
|
||||||
() =>
|
() =>
|
||||||
|
|||||||
142
src/hooks/use-focus-on.ts
Normal file
142
src/hooks/use-focus-on.ts
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
import { useCallback } from 'react';
|
||||||
|
import { useReactFlow } from '@xyflow/react';
|
||||||
|
import { useLayout } from '@/hooks/use-layout';
|
||||||
|
import { useBreakpoint } from '@/hooks/use-breakpoint';
|
||||||
|
|
||||||
|
interface FocusOptions {
|
||||||
|
select?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useFocusOn = () => {
|
||||||
|
const { fitView, setNodes, setEdges } = useReactFlow();
|
||||||
|
const { hideSidePanel } = useLayout();
|
||||||
|
const { isMd: isDesktop } = useBreakpoint('md');
|
||||||
|
|
||||||
|
const focusOnArea = useCallback(
|
||||||
|
(areaId: string, options: FocusOptions = {}) => {
|
||||||
|
const { select = true } = options;
|
||||||
|
|
||||||
|
if (select) {
|
||||||
|
setNodes((nodes) =>
|
||||||
|
nodes.map((node) =>
|
||||||
|
node.id === areaId
|
||||||
|
? {
|
||||||
|
...node,
|
||||||
|
selected: true,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
...node,
|
||||||
|
selected: false,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fitView({
|
||||||
|
duration: 500,
|
||||||
|
maxZoom: 1,
|
||||||
|
minZoom: 1,
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
id: areaId,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!isDesktop) {
|
||||||
|
hideSidePanel();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[fitView, setNodes, hideSidePanel, isDesktop]
|
||||||
|
);
|
||||||
|
|
||||||
|
const focusOnTable = useCallback(
|
||||||
|
(tableId: string, options: FocusOptions = {}) => {
|
||||||
|
const { select = true } = options;
|
||||||
|
|
||||||
|
if (select) {
|
||||||
|
setNodes((nodes) =>
|
||||||
|
nodes.map((node) =>
|
||||||
|
node.id === tableId
|
||||||
|
? {
|
||||||
|
...node,
|
||||||
|
selected: true,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
...node,
|
||||||
|
selected: false,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fitView({
|
||||||
|
duration: 500,
|
||||||
|
maxZoom: 1,
|
||||||
|
minZoom: 1,
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
id: tableId,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!isDesktop) {
|
||||||
|
hideSidePanel();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[fitView, setNodes, hideSidePanel, isDesktop]
|
||||||
|
);
|
||||||
|
|
||||||
|
const focusOnRelationship = useCallback(
|
||||||
|
(
|
||||||
|
relationshipId: string,
|
||||||
|
sourceTableId: string,
|
||||||
|
targetTableId: string,
|
||||||
|
options: FocusOptions = {}
|
||||||
|
) => {
|
||||||
|
const { select = true } = options;
|
||||||
|
|
||||||
|
if (select) {
|
||||||
|
setEdges((edges) =>
|
||||||
|
edges.map((edge) =>
|
||||||
|
edge.id === relationshipId
|
||||||
|
? {
|
||||||
|
...edge,
|
||||||
|
selected: true,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
...edge,
|
||||||
|
selected: false,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fitView({
|
||||||
|
duration: 500,
|
||||||
|
maxZoom: 1,
|
||||||
|
minZoom: 1,
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
id: sourceTableId,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: targetTableId,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!isDesktop) {
|
||||||
|
hideSidePanel();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[fitView, setEdges, hideSidePanel, isDesktop]
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
focusOnArea,
|
||||||
|
focusOnTable,
|
||||||
|
focusOnRelationship,
|
||||||
|
};
|
||||||
|
};
|
||||||
379
src/hooks/use-update-table-field.ts
Normal file
379
src/hooks/use-update-table-field.ts
Normal file
@@ -0,0 +1,379 @@
|
|||||||
|
import { useCallback, useMemo, useState, useEffect, useRef } from 'react';
|
||||||
|
import { useChartDB } from './use-chartdb';
|
||||||
|
import { useDebounce } from './use-debounce-v2';
|
||||||
|
import type { DatabaseType, DBField, DBTable } from '@/lib/domain';
|
||||||
|
import type {
|
||||||
|
SelectBoxOption,
|
||||||
|
SelectBoxProps,
|
||||||
|
} from '@/components/select-box/select-box';
|
||||||
|
import {
|
||||||
|
dataTypeDataToDataType,
|
||||||
|
sortedDataTypeMap,
|
||||||
|
supportsArrayDataType,
|
||||||
|
autoIncrementAlwaysOn,
|
||||||
|
requiresNotNull,
|
||||||
|
} from '@/lib/data/data-types/data-types';
|
||||||
|
import { generateDBFieldSuffix } from '@/lib/domain/db-field';
|
||||||
|
import type { DataTypeData } from '@/lib/data/data-types/data-types';
|
||||||
|
|
||||||
|
const generateFieldRegexPatterns = (
|
||||||
|
dataType: DataTypeData,
|
||||||
|
databaseType: DatabaseType
|
||||||
|
): {
|
||||||
|
regex?: string;
|
||||||
|
extractRegex?: RegExp;
|
||||||
|
} => {
|
||||||
|
const typeName = dataType.name;
|
||||||
|
const supportsArrays = supportsArrayDataType(dataType.id, databaseType);
|
||||||
|
const arrayPattern = supportsArrays ? '(\\[\\])?' : '';
|
||||||
|
|
||||||
|
if (!dataType.fieldAttributes) {
|
||||||
|
// For types without field attributes, support plain type + optional array notation
|
||||||
|
return {
|
||||||
|
regex: `^${typeName}${arrayPattern}$`,
|
||||||
|
extractRegex: new RegExp(`^${typeName}${arrayPattern}$`),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const fieldAttributes = dataType.fieldAttributes;
|
||||||
|
|
||||||
|
if (fieldAttributes.hasCharMaxLength) {
|
||||||
|
if (fieldAttributes.hasCharMaxLengthOption) {
|
||||||
|
return {
|
||||||
|
regex: `^${typeName}\\((\\d+|[mM][aA][xX])\\)${arrayPattern}$`,
|
||||||
|
extractRegex: supportsArrays
|
||||||
|
? /\((\d+|max)\)(\[\])?/i
|
||||||
|
: /\((\d+|max)\)/i,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
regex: `^${typeName}\\(\\d+\\)${arrayPattern}$`,
|
||||||
|
extractRegex: supportsArrays ? /\((\d+)\)(\[\])?/ : /\((\d+)\)/,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fieldAttributes.precision && fieldAttributes.scale) {
|
||||||
|
return {
|
||||||
|
regex: `^${typeName}\\s*\\(\\s*\\d+\\s*(?:,\\s*\\d+\\s*)?\\)${arrayPattern}$`,
|
||||||
|
extractRegex: new RegExp(
|
||||||
|
`${typeName}\\s*\\(\\s*(\\d+)\\s*(?:,\\s*(\\d+)\\s*)?\\)${arrayPattern}`
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fieldAttributes.precision) {
|
||||||
|
return {
|
||||||
|
regex: `^${typeName}\\s*\\(\\s*\\d+\\s*\\)${arrayPattern}$`,
|
||||||
|
extractRegex: supportsArrays ? /\((\d+)\)(\[\])?/ : /\((\d+)\)/,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return { regex: undefined, extractRegex: undefined };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useUpdateTableField = (
|
||||||
|
table: DBTable,
|
||||||
|
field: DBField,
|
||||||
|
customUpdateField?: (attrs: Partial<DBField>) => void
|
||||||
|
) => {
|
||||||
|
const {
|
||||||
|
databaseType,
|
||||||
|
customTypes,
|
||||||
|
updateField: chartDBUpdateField,
|
||||||
|
removeField: chartDBRemoveField,
|
||||||
|
} = useChartDB();
|
||||||
|
|
||||||
|
// Local state for responsive UI
|
||||||
|
const [localFieldName, setLocalFieldName] = useState(field.name);
|
||||||
|
const [localNullable, setLocalNullable] = useState(field.nullable);
|
||||||
|
const [localPrimaryKey, setLocalPrimaryKey] = useState(field.primaryKey);
|
||||||
|
|
||||||
|
const lastFieldNameRef = useRef<string>(field.name);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (localFieldName === lastFieldNameRef.current) {
|
||||||
|
lastFieldNameRef.current = field.name;
|
||||||
|
setLocalFieldName(field.name);
|
||||||
|
}
|
||||||
|
}, [field.name, localFieldName]);
|
||||||
|
|
||||||
|
// Update local state when field properties change externally
|
||||||
|
useEffect(() => {
|
||||||
|
setLocalNullable(field.nullable);
|
||||||
|
setLocalPrimaryKey(field.primaryKey);
|
||||||
|
}, [field.nullable, field.primaryKey]);
|
||||||
|
|
||||||
|
// Use custom updateField if provided, otherwise use the chartDB one
|
||||||
|
const updateField = useMemo(
|
||||||
|
() =>
|
||||||
|
customUpdateField
|
||||||
|
? (
|
||||||
|
_tableId: string,
|
||||||
|
_fieldId: string,
|
||||||
|
attrs: Partial<DBField>
|
||||||
|
) => customUpdateField(attrs)
|
||||||
|
: chartDBUpdateField,
|
||||||
|
[customUpdateField, chartDBUpdateField]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Calculate primary key fields for validation
|
||||||
|
const primaryKeyFields = useMemo(() => {
|
||||||
|
return table.fields.filter((f) => f.primaryKey);
|
||||||
|
}, [table.fields]);
|
||||||
|
|
||||||
|
const primaryKeyCount = useMemo(
|
||||||
|
() => primaryKeyFields.length,
|
||||||
|
[primaryKeyFields.length]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Generate data type options for select box
|
||||||
|
const dataFieldOptions = useMemo(() => {
|
||||||
|
const standardTypes: SelectBoxOption[] = sortedDataTypeMap[
|
||||||
|
databaseType
|
||||||
|
].map((type) => {
|
||||||
|
const regexPatterns = generateFieldRegexPatterns(
|
||||||
|
type,
|
||||||
|
databaseType
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
label: type.name,
|
||||||
|
value: type.id,
|
||||||
|
regex: regexPatterns.regex,
|
||||||
|
extractRegex: regexPatterns.extractRegex,
|
||||||
|
group: customTypes?.length ? 'Standard Types' : undefined,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!customTypes?.length) {
|
||||||
|
return standardTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add custom types as options
|
||||||
|
const customTypeOptions: SelectBoxOption[] = customTypes.map(
|
||||||
|
(type) => ({
|
||||||
|
label: type.name,
|
||||||
|
value: type.name,
|
||||||
|
description:
|
||||||
|
type.kind === 'enum' ? `${type.values?.join(' | ')}` : '',
|
||||||
|
group: 'Custom Types',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return [...standardTypes, ...customTypeOptions];
|
||||||
|
}, [databaseType, customTypes]);
|
||||||
|
|
||||||
|
// Handle data type change
|
||||||
|
const handleDataTypeChange = useCallback<
|
||||||
|
NonNullable<SelectBoxProps['onChange']>
|
||||||
|
>(
|
||||||
|
(value, regexMatches) => {
|
||||||
|
const dataType = sortedDataTypeMap[databaseType].find(
|
||||||
|
(v) => v.id === value
|
||||||
|
) ?? {
|
||||||
|
id: value as string,
|
||||||
|
name: value as string,
|
||||||
|
};
|
||||||
|
|
||||||
|
let characterMaximumLength: string | undefined = undefined;
|
||||||
|
let precision: number | undefined = undefined;
|
||||||
|
let scale: number | undefined = undefined;
|
||||||
|
let isArray: boolean | undefined = undefined;
|
||||||
|
|
||||||
|
if (regexMatches?.length) {
|
||||||
|
// Check if the last captured group is the array indicator []
|
||||||
|
const lastMatch = regexMatches[regexMatches.length - 1];
|
||||||
|
const hasArrayIndicator = lastMatch === '[]';
|
||||||
|
|
||||||
|
if (dataType?.fieldAttributes?.hasCharMaxLength) {
|
||||||
|
characterMaximumLength = regexMatches[1]?.toLowerCase();
|
||||||
|
} else if (
|
||||||
|
dataType?.fieldAttributes?.precision &&
|
||||||
|
dataType?.fieldAttributes?.scale
|
||||||
|
) {
|
||||||
|
precision = parseInt(regexMatches[1]);
|
||||||
|
scale = regexMatches[2]
|
||||||
|
? parseInt(regexMatches[2])
|
||||||
|
: undefined;
|
||||||
|
} else if (dataType?.fieldAttributes?.precision) {
|
||||||
|
precision = parseInt(regexMatches[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set isArray if the array indicator was found and the type supports arrays
|
||||||
|
if (hasArrayIndicator) {
|
||||||
|
const typeId = value as string;
|
||||||
|
if (supportsArrayDataType(typeId, databaseType)) {
|
||||||
|
isArray = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Explicitly set to false/undefined if no array indicator
|
||||||
|
isArray = undefined;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (
|
||||||
|
dataType?.fieldAttributes?.hasCharMaxLength &&
|
||||||
|
field.characterMaximumLength
|
||||||
|
) {
|
||||||
|
characterMaximumLength = field.characterMaximumLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dataType?.fieldAttributes?.precision && field.precision) {
|
||||||
|
precision = field.precision;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dataType?.fieldAttributes?.scale && field.scale) {
|
||||||
|
scale = field.scale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const newTypeName = dataType?.name ?? (value as string);
|
||||||
|
const typeRequiresNotNull = requiresNotNull(newTypeName);
|
||||||
|
const shouldForceIncrement = autoIncrementAlwaysOn(newTypeName);
|
||||||
|
|
||||||
|
updateField(table.id, field.id, {
|
||||||
|
characterMaximumLength,
|
||||||
|
precision,
|
||||||
|
scale,
|
||||||
|
isArray,
|
||||||
|
...(typeRequiresNotNull ? { nullable: false } : {}),
|
||||||
|
increment: shouldForceIncrement ? true : undefined,
|
||||||
|
default: undefined,
|
||||||
|
type: dataTypeDataToDataType(
|
||||||
|
dataType ?? {
|
||||||
|
id: value as string,
|
||||||
|
name: value as string,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[
|
||||||
|
updateField,
|
||||||
|
databaseType,
|
||||||
|
field.characterMaximumLength,
|
||||||
|
field.precision,
|
||||||
|
field.scale,
|
||||||
|
field.id,
|
||||||
|
table.id,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Debounced update for field name
|
||||||
|
const debouncedNameUpdate = useDebounce(
|
||||||
|
useCallback(
|
||||||
|
(value: string) => {
|
||||||
|
if (value.trim() !== field.name) {
|
||||||
|
updateField(table.id, field.id, { name: value });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[updateField, table.id, field.id, field.name]
|
||||||
|
),
|
||||||
|
300 // 300ms debounce for text input
|
||||||
|
);
|
||||||
|
|
||||||
|
// Debounced update for nullable toggle
|
||||||
|
const debouncedNullableUpdate = useDebounce(
|
||||||
|
useCallback(
|
||||||
|
(value: boolean) => {
|
||||||
|
const updates: Partial<DBField> = { nullable: value };
|
||||||
|
|
||||||
|
// If setting to nullable, clear increment (auto-increment requires NOT NULL)
|
||||||
|
if (value && field.increment) {
|
||||||
|
updates.increment = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateField(table.id, field.id, updates);
|
||||||
|
},
|
||||||
|
[updateField, table.id, field.id, field.increment]
|
||||||
|
),
|
||||||
|
100 // 100ms debounce for toggle
|
||||||
|
);
|
||||||
|
|
||||||
|
// Debounced update for primary key toggle
|
||||||
|
const debouncedPrimaryKeyUpdate = useDebounce(
|
||||||
|
useCallback(
|
||||||
|
(value: boolean, primaryKeyCount: number) => {
|
||||||
|
if (value) {
|
||||||
|
// When setting as primary key
|
||||||
|
const updates: Partial<DBField> = {
|
||||||
|
primaryKey: true,
|
||||||
|
};
|
||||||
|
// Only auto-set unique if this will be the only primary key
|
||||||
|
if (primaryKeyCount === 0) {
|
||||||
|
updates.unique = true;
|
||||||
|
}
|
||||||
|
updateField(table.id, field.id, updates);
|
||||||
|
} else {
|
||||||
|
// When removing primary key
|
||||||
|
updateField(table.id, field.id, {
|
||||||
|
primaryKey: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[updateField, table.id, field.id]
|
||||||
|
),
|
||||||
|
100 // 100ms debounce for toggle
|
||||||
|
);
|
||||||
|
|
||||||
|
// Handle primary key toggle with optimistic update
|
||||||
|
const handlePrimaryKeyToggle = useCallback(
|
||||||
|
(value: boolean) => {
|
||||||
|
setLocalPrimaryKey(value);
|
||||||
|
debouncedPrimaryKeyUpdate(value, primaryKeyCount);
|
||||||
|
},
|
||||||
|
[primaryKeyCount, debouncedPrimaryKeyUpdate]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Handle nullable toggle with optimistic update
|
||||||
|
const handleNullableToggle = useCallback(
|
||||||
|
(value: boolean) => {
|
||||||
|
setLocalNullable(value);
|
||||||
|
debouncedNullableUpdate(value);
|
||||||
|
},
|
||||||
|
[debouncedNullableUpdate]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Handle name change with optimistic update
|
||||||
|
const handleNameChange = useCallback(
|
||||||
|
(value: string) => {
|
||||||
|
setLocalFieldName(value);
|
||||||
|
debouncedNameUpdate(value);
|
||||||
|
},
|
||||||
|
[debouncedNameUpdate]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Utility function to generate field suffix for display
|
||||||
|
const generateFieldSuffix = useCallback(
|
||||||
|
(typeId?: string) => {
|
||||||
|
return generateDBFieldSuffix(
|
||||||
|
{
|
||||||
|
...field,
|
||||||
|
isArray: field.isArray && typeId === field.type.id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
databaseType,
|
||||||
|
forceExtended: true,
|
||||||
|
typeId,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[field, databaseType]
|
||||||
|
);
|
||||||
|
|
||||||
|
const removeField = useCallback(() => {
|
||||||
|
chartDBRemoveField(table.id, field.id);
|
||||||
|
}, [chartDBRemoveField, table.id, field.id]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
dataFieldOptions,
|
||||||
|
handleDataTypeChange,
|
||||||
|
handlePrimaryKeyToggle,
|
||||||
|
handleNullableToggle,
|
||||||
|
handleNameChange,
|
||||||
|
generateFieldSuffix,
|
||||||
|
primaryKeyCount,
|
||||||
|
fieldName: localFieldName,
|
||||||
|
nullable: localNullable,
|
||||||
|
primaryKey: localPrimaryKey,
|
||||||
|
removeField,
|
||||||
|
};
|
||||||
|
};
|
||||||
42
src/hooks/use-update-table.ts
Normal file
42
src/hooks/use-update-table.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import { useCallback, useState, useEffect } from 'react';
|
||||||
|
import { useChartDB } from './use-chartdb';
|
||||||
|
import { useDebounce } from './use-debounce-v2';
|
||||||
|
import type { DBTable } from '@/lib/domain';
|
||||||
|
|
||||||
|
// Hook for updating table properties with debouncing for performance
|
||||||
|
export const useUpdateTable = (table: DBTable) => {
|
||||||
|
const { updateTable: chartDBUpdateTable } = useChartDB();
|
||||||
|
const [localTableName, setLocalTableName] = useState(table.name);
|
||||||
|
|
||||||
|
// Debounced update function
|
||||||
|
const debouncedUpdate = useDebounce(
|
||||||
|
useCallback(
|
||||||
|
(value: string) => {
|
||||||
|
if (value.trim() && value.trim() !== table.name) {
|
||||||
|
chartDBUpdateTable(table.id, { name: value.trim() });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[chartDBUpdateTable, table.id, table.name]
|
||||||
|
),
|
||||||
|
1000 // 1000ms debounce
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update local state immediately for responsive UI
|
||||||
|
const handleTableNameChange = useCallback(
|
||||||
|
(value: string) => {
|
||||||
|
setLocalTableName(value);
|
||||||
|
debouncedUpdate(value);
|
||||||
|
},
|
||||||
|
[debouncedUpdate]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update local state when table name changes externally
|
||||||
|
useEffect(() => {
|
||||||
|
setLocalTableName(table.name);
|
||||||
|
}, [table.name]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
tableName: localTableName,
|
||||||
|
handleTableNameChange,
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
|||||||
|
|
||||||
export const ar: LanguageTranslation = {
|
export const ar: LanguageTranslation = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: 'جديد',
|
||||||
|
browse: 'تصفح',
|
||||||
|
tables: 'الجداول',
|
||||||
|
refs: 'المراجع',
|
||||||
|
areas: 'المناطق',
|
||||||
|
dependencies: 'التبعيات',
|
||||||
|
custom_types: 'الأنواع المخصصة',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: 'ملف',
|
actions: 'الإجراءات',
|
||||||
new: 'جديد',
|
new: 'جديد...',
|
||||||
open: 'فتح',
|
browse: 'تصفح...',
|
||||||
save: 'حفظ',
|
save: 'حفظ',
|
||||||
import: 'استيراد قاعدة بيانات',
|
import: 'استيراد قاعدة بيانات',
|
||||||
export_sql: 'SQL تصدير',
|
export_sql: 'SQL تصدير',
|
||||||
export_as: 'تصدير كـ',
|
export_as: 'تصدير كـ',
|
||||||
delete_diagram: 'حذف الرسم البياني',
|
delete_diagram: 'حذف',
|
||||||
exit: 'خروج',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: 'تحرير',
|
edit: 'تحرير',
|
||||||
@@ -29,6 +37,7 @@ export const ar: LanguageTranslation = {
|
|||||||
hide_field_attributes: 'إخفاء خصائص الحقل',
|
hide_field_attributes: 'إخفاء خصائص الحقل',
|
||||||
show_field_attributes: 'إظهار خصائص الحقل',
|
show_field_attributes: 'إظهار خصائص الحقل',
|
||||||
zoom_on_scroll: 'تكبير/تصغير عند التمرير',
|
zoom_on_scroll: 'تكبير/تصغير عند التمرير',
|
||||||
|
show_views: 'عروض قاعدة البيانات',
|
||||||
theme: 'المظهر',
|
theme: 'المظهر',
|
||||||
show_dependencies: 'إظهار الاعتمادات',
|
show_dependencies: 'إظهار الاعتمادات',
|
||||||
hide_dependencies: 'إخفاء الاعتمادات',
|
hide_dependencies: 'إخفاء الاعتمادات',
|
||||||
@@ -65,22 +74,13 @@ export const ar: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: 'إعادة ترتيب الرسم البياني',
|
title: 'ترتيب تلقائي للرسم البياني',
|
||||||
description:
|
description:
|
||||||
'هذا الإجراء سيقوم بإعادة ترتيب الجداول في المخطط بشكل تلقائي. هل تريد المتابعة؟',
|
'هذا الإجراء سيقوم بإعادة ترتيب الجداول في المخطط بشكل تلقائي. هل تريد المتابعة؟',
|
||||||
reorder: 'إعادة ترتيب',
|
reorder: 'ترتيب تلقائي',
|
||||||
cancel: 'إلغاء',
|
cancel: 'إلغاء',
|
||||||
},
|
},
|
||||||
|
|
||||||
multiple_schemas_alert: {
|
|
||||||
title: 'مخططات متعددة',
|
|
||||||
description:
|
|
||||||
'{{formattedSchemas}} :مخططات في هذا الرسم البياني. يتم حاليا عرض {{schemasCount}} هناك',
|
|
||||||
// TODO: Translate
|
|
||||||
show_me: 'Show me',
|
|
||||||
none: 'لا شيء',
|
|
||||||
},
|
|
||||||
|
|
||||||
copy_to_clipboard_toast: {
|
copy_to_clipboard_toast: {
|
||||||
unsupported: {
|
unsupported: {
|
||||||
title: 'فشل النسخ',
|
title: 'فشل النسخ',
|
||||||
@@ -115,14 +115,11 @@ export const ar: LanguageTranslation = {
|
|||||||
copied: '!تم النسخ',
|
copied: '!تم النسخ',
|
||||||
|
|
||||||
side_panel: {
|
side_panel: {
|
||||||
schema: ':المخطط',
|
|
||||||
filter_by_schema: 'تصفية حسب المخطط',
|
|
||||||
search_schema: '...بحث في المخطط',
|
|
||||||
no_schemas_found: '.لم يتم العثور على مخططات',
|
|
||||||
view_all_options: '...عرض جميع الخيارات',
|
view_all_options: '...عرض جميع الخيارات',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: 'الجداول',
|
tables: 'الجداول',
|
||||||
add_table: 'إضافة جدول',
|
add_table: 'إضافة جدول',
|
||||||
|
add_view: 'إضافة عرض',
|
||||||
filter: 'تصفية',
|
filter: 'تصفية',
|
||||||
collapse: 'طي الكل',
|
collapse: 'طي الكل',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
@@ -148,6 +145,7 @@ export const ar: LanguageTranslation = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: 'خصائص الحقل',
|
title: 'خصائص الحقل',
|
||||||
unique: 'فريد',
|
unique: 'فريد',
|
||||||
|
auto_increment: 'زيادة تلقائية',
|
||||||
comments: 'تعليقات',
|
comments: 'تعليقات',
|
||||||
no_comments: 'لا يوجد تعليقات',
|
no_comments: 'لا يوجد تعليقات',
|
||||||
delete_field: 'حذف الحقل',
|
delete_field: 'حذف الحقل',
|
||||||
@@ -162,6 +160,7 @@ export const ar: LanguageTranslation = {
|
|||||||
title: 'خصائص الفهرس',
|
title: 'خصائص الفهرس',
|
||||||
name: 'الإسم',
|
name: 'الإسم',
|
||||||
unique: 'فريد',
|
unique: 'فريد',
|
||||||
|
index_type: 'نوع الفهرس',
|
||||||
delete_index: 'حذف الفهرس',
|
delete_index: 'حذف الفهرس',
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -178,12 +177,15 @@ export const ar: LanguageTranslation = {
|
|||||||
description: 'أنشئ جدولاً للبدء',
|
description: 'أنشئ جدولاً للبدء',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: 'العلاقات',
|
refs: 'المراجع',
|
||||||
filter: 'تصفية',
|
filter: 'تصفية',
|
||||||
add_relationship: 'إضافة علاقة',
|
|
||||||
collapse: 'طي الكل',
|
collapse: 'طي الكل',
|
||||||
|
add_relationship: 'إضافة علاقة',
|
||||||
|
relationships: 'العلاقات',
|
||||||
|
dependencies: 'الاعتمادات',
|
||||||
relationship: {
|
relationship: {
|
||||||
|
relationship: 'العلاقة',
|
||||||
primary: 'الجدول الأساسي',
|
primary: 'الجدول الأساسي',
|
||||||
foreign: 'الجدول المرتبط',
|
foreign: 'الجدول المرتبط',
|
||||||
cardinality: 'الكاردينالية',
|
cardinality: 'الكاردينالية',
|
||||||
@@ -193,16 +195,8 @@ export const ar: LanguageTranslation = {
|
|||||||
delete_relationship: 'حذف',
|
delete_relationship: 'حذف',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
|
||||||
title: 'لا توجد علاقات',
|
|
||||||
description: 'إنشئ علاقة لربط الجداول',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dependencies_section: {
|
|
||||||
dependencies: 'الاعتمادات',
|
|
||||||
filter: 'تصفية',
|
|
||||||
collapse: 'طي الكل',
|
|
||||||
dependency: {
|
dependency: {
|
||||||
|
dependency: 'الاعتماد',
|
||||||
table: 'الجدول',
|
table: 'الجدول',
|
||||||
dependent_table: 'عرض الاعتمادات',
|
dependent_table: 'عرض الاعتمادات',
|
||||||
delete_dependency: 'حذف',
|
delete_dependency: 'حذف',
|
||||||
@@ -212,8 +206,8 @@ export const ar: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: 'لا توجد اعتمادات',
|
title: 'لا توجد علاقات',
|
||||||
description: 'إنشاء اعتماد للبدء',
|
description: 'إنشاء علاقة للبدء',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -254,6 +248,7 @@ export const ar: LanguageTranslation = {
|
|||||||
enum_values: 'Enum Values',
|
enum_values: 'Enum Values',
|
||||||
composite_fields: 'Fields',
|
composite_fields: 'Fields',
|
||||||
no_fields: 'No fields defined',
|
no_fields: 'No fields defined',
|
||||||
|
no_values: 'لم يتم تحديد قيم التعداد',
|
||||||
field_name_placeholder: 'Field name',
|
field_name_placeholder: 'Field name',
|
||||||
field_type_placeholder: 'Select type',
|
field_type_placeholder: 'Select type',
|
||||||
add_field: 'Add Field',
|
add_field: 'Add Field',
|
||||||
@@ -276,7 +271,7 @@ export const ar: LanguageTranslation = {
|
|||||||
show_all: 'عرض الكل',
|
show_all: 'عرض الكل',
|
||||||
undo: 'تراجع',
|
undo: 'تراجع',
|
||||||
redo: 'إعادة',
|
redo: 'إعادة',
|
||||||
reorder_diagram: 'إعادة ترتيب الرسم البياني',
|
reorder_diagram: 'ترتيب تلقائي للرسم البياني',
|
||||||
highlight_overlapping_tables: 'تمييز الجداول المتداخلة',
|
highlight_overlapping_tables: 'تمييز الجداول المتداخلة',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
filter: 'Filter Tables',
|
filter: 'Filter Tables',
|
||||||
@@ -313,13 +308,13 @@ export const ar: LanguageTranslation = {
|
|||||||
cancel: 'إلغاء',
|
cancel: 'إلغاء',
|
||||||
import_from_file: 'استيراد من ملف',
|
import_from_file: 'استيراد من ملف',
|
||||||
back: 'رجوع',
|
back: 'رجوع',
|
||||||
empty_diagram: 'مخطط فارغ',
|
empty_diagram: 'قاعدة بيانات فارغة',
|
||||||
continue: 'متابعة',
|
continue: 'متابعة',
|
||||||
import: 'استيراد',
|
import: 'استيراد',
|
||||||
},
|
},
|
||||||
|
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: 'فتح مخطط',
|
title: 'فتح قاعدة بيانات',
|
||||||
description: 'اختر مخططًا لفتحه من القائمة ادناه',
|
description: 'اختر مخططًا لفتحه من القائمة ادناه',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
name: 'الإسم',
|
name: 'الإسم',
|
||||||
@@ -329,6 +324,12 @@ export const ar: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
cancel: 'إلغاء',
|
cancel: 'إلغاء',
|
||||||
open: 'فتح',
|
open: 'فتح',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: 'فتح',
|
||||||
|
duplicate: 'تكرار',
|
||||||
|
delete: 'حذف',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -474,6 +475,7 @@ export const ar: LanguageTranslation = {
|
|||||||
|
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: 'جدول جديد',
|
new_table: 'جدول جديد',
|
||||||
|
new_view: 'عرض جديد',
|
||||||
new_relationship: 'علاقة جديدة',
|
new_relationship: 'علاقة جديدة',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
new_area: 'New Area',
|
new_area: 'New Area',
|
||||||
@@ -495,6 +497,8 @@ export const ar: LanguageTranslation = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: 'اللغة',
|
change_language: 'اللغة',
|
||||||
},
|
},
|
||||||
|
on: 'تشغيل',
|
||||||
|
off: 'إيقاف',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
|||||||
|
|
||||||
export const bn: LanguageTranslation = {
|
export const bn: LanguageTranslation = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: 'নতুন',
|
||||||
|
browse: 'ব্রাউজ',
|
||||||
|
tables: 'টেবিল',
|
||||||
|
refs: 'রেফস',
|
||||||
|
areas: 'এলাকা',
|
||||||
|
dependencies: 'নির্ভরতা',
|
||||||
|
custom_types: 'কাস্টম টাইপ',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: 'ফাইল',
|
actions: 'কার্য',
|
||||||
new: 'নতুন',
|
new: 'নতুন...',
|
||||||
open: 'খুলুন',
|
browse: 'ব্রাউজ করুন...',
|
||||||
save: 'সংরক্ষণ করুন',
|
save: 'সংরক্ষণ করুন',
|
||||||
import: 'ডাটাবেস আমদানি করুন',
|
import: 'ডাটাবেস আমদানি করুন',
|
||||||
export_sql: 'SQL রপ্তানি করুন',
|
export_sql: 'SQL রপ্তানি করুন',
|
||||||
export_as: 'রূপে রপ্তানি করুন',
|
export_as: 'রূপে রপ্তানি করুন',
|
||||||
delete_diagram: 'ডায়াগ্রাম মুছুন',
|
delete_diagram: 'মুছুন',
|
||||||
exit: 'প্রস্থান করুন',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: 'সম্পাদনা',
|
edit: 'সম্পাদনা',
|
||||||
@@ -29,6 +37,7 @@ export const bn: LanguageTranslation = {
|
|||||||
hide_field_attributes: 'ফিল্ড অ্যাট্রিবিউট লুকান',
|
hide_field_attributes: 'ফিল্ড অ্যাট্রিবিউট লুকান',
|
||||||
show_field_attributes: 'ফিল্ড অ্যাট্রিবিউট দেখান',
|
show_field_attributes: 'ফিল্ড অ্যাট্রিবিউট দেখান',
|
||||||
zoom_on_scroll: 'স্ক্রলে জুম করুন',
|
zoom_on_scroll: 'স্ক্রলে জুম করুন',
|
||||||
|
show_views: 'ডাটাবেস ভিউ',
|
||||||
theme: 'থিম',
|
theme: 'থিম',
|
||||||
show_dependencies: 'নির্ভরতাগুলি দেখান',
|
show_dependencies: 'নির্ভরতাগুলি দেখান',
|
||||||
hide_dependencies: 'নির্ভরতাগুলি লুকান',
|
hide_dependencies: 'নির্ভরতাগুলি লুকান',
|
||||||
@@ -66,22 +75,13 @@ export const bn: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: 'ডায়াগ্রাম পুনর্বিন্যাস করুন',
|
title: 'স্বয়ংক্রিয় ডায়াগ্রাম সাজান',
|
||||||
description:
|
description:
|
||||||
'এই কাজটি ডায়াগ্রামের সমস্ত টেবিল পুনর্বিন্যাস করবে। আপনি কি চালিয়ে যেতে চান?',
|
'এই কাজটি ডায়াগ্রামের সমস্ত টেবিল পুনর্বিন্যাস করবে। আপনি কি চালিয়ে যেতে চান?',
|
||||||
reorder: 'পুনর্বিন্যাস করুন',
|
reorder: 'স্বয়ংক্রিয় সাজান',
|
||||||
cancel: 'বাতিল করুন',
|
cancel: 'বাতিল করুন',
|
||||||
},
|
},
|
||||||
|
|
||||||
multiple_schemas_alert: {
|
|
||||||
title: 'বহু স্কিমা',
|
|
||||||
description:
|
|
||||||
'{{schemasCount}} স্কিমা এই ডায়াগ্রামে রয়েছে। বর্তমানে প্রদর্শিত: {{formattedSchemas}}।',
|
|
||||||
// TODO: Translate
|
|
||||||
show_me: 'Show me',
|
|
||||||
none: 'কিছুই না',
|
|
||||||
},
|
|
||||||
|
|
||||||
copy_to_clipboard_toast: {
|
copy_to_clipboard_toast: {
|
||||||
unsupported: {
|
unsupported: {
|
||||||
title: 'কপি ব্যর্থ হয়েছে',
|
title: 'কপি ব্যর্থ হয়েছে',
|
||||||
@@ -116,14 +116,11 @@ export const bn: LanguageTranslation = {
|
|||||||
copied: 'অনুলিপি সম্পন্ন!',
|
copied: 'অনুলিপি সম্পন্ন!',
|
||||||
|
|
||||||
side_panel: {
|
side_panel: {
|
||||||
schema: 'স্কিমা:',
|
|
||||||
filter_by_schema: 'স্কিমা দ্বারা ফিল্টার করুন',
|
|
||||||
search_schema: 'স্কিমা খুঁজুন...',
|
|
||||||
no_schemas_found: 'কোনো স্কিমা পাওয়া যায়নি।',
|
|
||||||
view_all_options: 'সমস্ত বিকল্প দেখুন...',
|
view_all_options: 'সমস্ত বিকল্প দেখুন...',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: 'টেবিল',
|
tables: 'টেবিল',
|
||||||
add_table: 'টেবিল যোগ করুন',
|
add_table: 'টেবিল যোগ করুন',
|
||||||
|
add_view: 'ভিউ যোগ করুন',
|
||||||
filter: 'ফিল্টার',
|
filter: 'ফিল্টার',
|
||||||
collapse: 'সব ভাঁজ করুন',
|
collapse: 'সব ভাঁজ করুন',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
@@ -149,6 +146,7 @@ export const bn: LanguageTranslation = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: 'ফিল্ড কর্ম',
|
title: 'ফিল্ড কর্ম',
|
||||||
unique: 'অদ্বিতীয়',
|
unique: 'অদ্বিতীয়',
|
||||||
|
auto_increment: 'স্বয়ংক্রিয় বৃদ্ধি',
|
||||||
comments: 'মন্তব্য',
|
comments: 'মন্তব্য',
|
||||||
no_comments: 'কোনো মন্তব্য নেই',
|
no_comments: 'কোনো মন্তব্য নেই',
|
||||||
delete_field: 'ফিল্ড মুছুন',
|
delete_field: 'ফিল্ড মুছুন',
|
||||||
@@ -164,6 +162,7 @@ export const bn: LanguageTranslation = {
|
|||||||
title: 'ইনডেক্স কর্ম',
|
title: 'ইনডেক্স কর্ম',
|
||||||
name: 'নাম',
|
name: 'নাম',
|
||||||
unique: 'অদ্বিতীয়',
|
unique: 'অদ্বিতীয়',
|
||||||
|
index_type: 'ইনডেক্স ধরন',
|
||||||
delete_index: 'ইনডেক্স মুছুন',
|
delete_index: 'ইনডেক্স মুছুন',
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -180,14 +179,17 @@ export const bn: LanguageTranslation = {
|
|||||||
description: 'শুরু করতে একটি টেবিল তৈরি করুন',
|
description: 'শুরু করতে একটি টেবিল তৈরি করুন',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: 'সম্পর্ক',
|
refs: 'রেফস',
|
||||||
filter: 'ফিল্টার',
|
filter: 'ফিল্টার',
|
||||||
add_relationship: 'সম্পর্ক যোগ করুন',
|
|
||||||
collapse: 'সব ভাঁজ করুন',
|
collapse: 'সব ভাঁজ করুন',
|
||||||
|
add_relationship: 'সম্পর্ক যোগ করুন',
|
||||||
|
relationships: 'সম্পর্ক',
|
||||||
|
dependencies: 'নির্ভরতাগুলি',
|
||||||
relationship: {
|
relationship: {
|
||||||
|
relationship: 'সম্পর্ক',
|
||||||
primary: 'প্রাথমিক টেবিল',
|
primary: 'প্রাথমিক টেবিল',
|
||||||
foreign: 'বিদেশি টেবিল',
|
foreign: 'রেফারেন্স করা টেবিল',
|
||||||
cardinality: 'কার্ডিনালিটি',
|
cardinality: 'কার্ডিনালিটি',
|
||||||
delete_relationship: 'মুছুন',
|
delete_relationship: 'মুছুন',
|
||||||
relationship_actions: {
|
relationship_actions: {
|
||||||
@@ -195,27 +197,19 @@ export const bn: LanguageTranslation = {
|
|||||||
delete_relationship: 'মুছুন',
|
delete_relationship: 'মুছুন',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
|
||||||
title: 'কোনো সম্পর্ক নেই',
|
|
||||||
description: 'টেবিল সংযোগ করতে একটি সম্পর্ক তৈরি করুন',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dependencies_section: {
|
|
||||||
dependencies: 'নির্ভরতাগুলি',
|
|
||||||
filter: 'ফিল্টার',
|
|
||||||
collapse: 'ভাঁজ করুন',
|
|
||||||
dependency: {
|
dependency: {
|
||||||
|
dependency: 'নির্ভরতা',
|
||||||
table: 'টেবিল',
|
table: 'টেবিল',
|
||||||
dependent_table: 'নির্ভরশীল টেবিল',
|
dependent_table: 'নির্ভরশীল ভিউ',
|
||||||
delete_dependency: 'নির্ভরতা মুছুন',
|
delete_dependency: 'মুছুন',
|
||||||
dependency_actions: {
|
dependency_actions: {
|
||||||
title: 'কর্ম',
|
title: 'কর্ম',
|
||||||
delete_dependency: 'নির্ভরতা মুছুন',
|
delete_dependency: 'মুছুন',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: 'কোনো নির্ভরতাগুলি নেই',
|
title: 'কোনো সম্পর্ক নেই',
|
||||||
description: 'এই অংশে কোনো নির্ভরতা উপলব্ধ নেই।',
|
description: 'শুরু করতে একটি সম্পর্ক তৈরি করুন',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -255,6 +249,7 @@ export const bn: LanguageTranslation = {
|
|||||||
enum_values: 'Enum Values',
|
enum_values: 'Enum Values',
|
||||||
composite_fields: 'Fields',
|
composite_fields: 'Fields',
|
||||||
no_fields: 'No fields defined',
|
no_fields: 'No fields defined',
|
||||||
|
no_values: 'কোন enum মান সংজ্ঞায়িত নেই',
|
||||||
field_name_placeholder: 'Field name',
|
field_name_placeholder: 'Field name',
|
||||||
field_type_placeholder: 'Select type',
|
field_type_placeholder: 'Select type',
|
||||||
add_field: 'Add Field',
|
add_field: 'Add Field',
|
||||||
@@ -277,7 +272,7 @@ export const bn: LanguageTranslation = {
|
|||||||
show_all: 'সব দেখান',
|
show_all: 'সব দেখান',
|
||||||
undo: 'পূর্বাবস্থায় ফিরুন',
|
undo: 'পূর্বাবস্থায় ফিরুন',
|
||||||
redo: 'পুনরায় করুন',
|
redo: 'পুনরায় করুন',
|
||||||
reorder_diagram: 'ডায়াগ্রাম পুনর্বিন্যাস করুন',
|
reorder_diagram: 'স্বয়ংক্রিয় ডায়াগ্রাম সাজান',
|
||||||
highlight_overlapping_tables: 'ওভারল্যাপিং টেবিল হাইলাইট করুন',
|
highlight_overlapping_tables: 'ওভারল্যাপিং টেবিল হাইলাইট করুন',
|
||||||
|
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
@@ -315,13 +310,13 @@ export const bn: LanguageTranslation = {
|
|||||||
cancel: 'বাতিল করুন',
|
cancel: 'বাতিল করুন',
|
||||||
back: 'ফিরে যান',
|
back: 'ফিরে যান',
|
||||||
import_from_file: 'ফাইল থেকে আমদানি করুন',
|
import_from_file: 'ফাইল থেকে আমদানি করুন',
|
||||||
empty_diagram: 'ফাঁকা চিত্র',
|
empty_diagram: 'খালি ডাটাবেস',
|
||||||
continue: 'চালিয়ে যান',
|
continue: 'চালিয়ে যান',
|
||||||
import: 'আমদানি করুন',
|
import: 'আমদানি করুন',
|
||||||
},
|
},
|
||||||
|
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: 'চিত্র খুলুন',
|
title: 'ডেটাবেস খুলুন',
|
||||||
description: 'নিচের তালিকা থেকে একটি চিত্র নির্বাচন করুন।',
|
description: 'নিচের তালিকা থেকে একটি চিত্র নির্বাচন করুন।',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
name: 'নাম',
|
name: 'নাম',
|
||||||
@@ -331,6 +326,12 @@ export const bn: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
cancel: 'বাতিল করুন',
|
cancel: 'বাতিল করুন',
|
||||||
open: 'খুলুন',
|
open: 'খুলুন',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: 'খুলুন',
|
||||||
|
duplicate: 'ডুপ্লিকেট',
|
||||||
|
delete: 'মুছুন',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -479,6 +480,7 @@ export const bn: LanguageTranslation = {
|
|||||||
|
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: 'নতুন টেবিল',
|
new_table: 'নতুন টেবিল',
|
||||||
|
new_view: 'নতুন ভিউ',
|
||||||
new_relationship: 'নতুন সম্পর্ক',
|
new_relationship: 'নতুন সম্পর্ক',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
new_area: 'New Area',
|
new_area: 'New Area',
|
||||||
@@ -500,6 +502,9 @@ export const bn: LanguageTranslation = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: 'ভাষা পরিবর্তন করুন',
|
change_language: 'ভাষা পরিবর্তন করুন',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: 'চালু',
|
||||||
|
off: 'বন্ধ',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
|||||||
|
|
||||||
export const de: LanguageTranslation = {
|
export const de: LanguageTranslation = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: 'Neu',
|
||||||
|
browse: 'Durchsuchen',
|
||||||
|
tables: 'Tabellen',
|
||||||
|
refs: 'Refs',
|
||||||
|
areas: 'Bereiche',
|
||||||
|
dependencies: 'Abhängigkeiten',
|
||||||
|
custom_types: 'Benutzerdefinierte Typen',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: 'Datei',
|
actions: 'Aktionen',
|
||||||
new: 'Neu',
|
new: 'Neu...',
|
||||||
open: 'Öffnen',
|
browse: 'Durchsuchen...',
|
||||||
save: 'Speichern',
|
save: 'Speichern',
|
||||||
import: 'Datenbank importieren',
|
import: 'Datenbank importieren',
|
||||||
export_sql: 'SQL exportieren',
|
export_sql: 'SQL exportieren',
|
||||||
export_as: 'Exportieren als',
|
export_as: 'Exportieren als',
|
||||||
delete_diagram: 'Diagramm löschen',
|
delete_diagram: 'Löschen',
|
||||||
exit: 'Beenden',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: 'Bearbeiten',
|
edit: 'Bearbeiten',
|
||||||
@@ -29,6 +37,7 @@ export const de: LanguageTranslation = {
|
|||||||
hide_field_attributes: 'Feldattribute ausblenden',
|
hide_field_attributes: 'Feldattribute ausblenden',
|
||||||
show_field_attributes: 'Feldattribute anzeigen',
|
show_field_attributes: 'Feldattribute anzeigen',
|
||||||
zoom_on_scroll: 'Zoom beim Scrollen',
|
zoom_on_scroll: 'Zoom beim Scrollen',
|
||||||
|
show_views: 'Datenbankansichten',
|
||||||
theme: 'Stil',
|
theme: 'Stil',
|
||||||
show_dependencies: 'Abhängigkeiten anzeigen',
|
show_dependencies: 'Abhängigkeiten anzeigen',
|
||||||
hide_dependencies: 'Abhängigkeiten ausblenden',
|
hide_dependencies: 'Abhängigkeiten ausblenden',
|
||||||
@@ -66,22 +75,13 @@ export const de: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: 'Diagramm neu anordnen',
|
title: 'Diagramm automatisch anordnen',
|
||||||
description:
|
description:
|
||||||
'Diese Aktion wird alle Tabellen im Diagramm neu anordnen. Möchten Sie fortfahren?',
|
'Diese Aktion wird alle Tabellen im Diagramm neu anordnen. Möchten Sie fortfahren?',
|
||||||
reorder: 'Neu anordnen',
|
reorder: 'Automatisch anordnen',
|
||||||
cancel: 'Abbrechen',
|
cancel: 'Abbrechen',
|
||||||
},
|
},
|
||||||
|
|
||||||
multiple_schemas_alert: {
|
|
||||||
title: 'Mehrere Schemas',
|
|
||||||
description:
|
|
||||||
'{{schemasCount}} Schemas in diesem Diagramm. Derzeit angezeigt: {{formattedSchemas}}.',
|
|
||||||
// TODO: Translate
|
|
||||||
show_me: 'Show me',
|
|
||||||
none: 'Keine',
|
|
||||||
},
|
|
||||||
|
|
||||||
copy_to_clipboard_toast: {
|
copy_to_clipboard_toast: {
|
||||||
unsupported: {
|
unsupported: {
|
||||||
title: 'Kopieren fehlgeschlagen',
|
title: 'Kopieren fehlgeschlagen',
|
||||||
@@ -117,14 +117,11 @@ export const de: LanguageTranslation = {
|
|||||||
copied: 'Kopiert!',
|
copied: 'Kopiert!',
|
||||||
|
|
||||||
side_panel: {
|
side_panel: {
|
||||||
schema: 'Schema:',
|
|
||||||
filter_by_schema: 'Nach Schema filtern',
|
|
||||||
search_schema: 'Schema suchen...',
|
|
||||||
no_schemas_found: 'Keine Schemas gefunden.',
|
|
||||||
view_all_options: 'Alle Optionen anzeigen...',
|
view_all_options: 'Alle Optionen anzeigen...',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: 'Tabellen',
|
tables: 'Tabellen',
|
||||||
add_table: 'Tabelle hinzufügen',
|
add_table: 'Tabelle hinzufügen',
|
||||||
|
add_view: 'Ansicht hinzufügen',
|
||||||
filter: 'Filter',
|
filter: 'Filter',
|
||||||
collapse: 'Alle einklappen',
|
collapse: 'Alle einklappen',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
@@ -150,6 +147,7 @@ export const de: LanguageTranslation = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: 'Feldattribute',
|
title: 'Feldattribute',
|
||||||
unique: 'Eindeutig',
|
unique: 'Eindeutig',
|
||||||
|
auto_increment: 'Automatisch hochzählen',
|
||||||
comments: 'Kommentare',
|
comments: 'Kommentare',
|
||||||
no_comments: 'Keine Kommentare',
|
no_comments: 'Keine Kommentare',
|
||||||
delete_field: 'Feld löschen',
|
delete_field: 'Feld löschen',
|
||||||
@@ -165,6 +163,7 @@ export const de: LanguageTranslation = {
|
|||||||
title: 'Indexattribute',
|
title: 'Indexattribute',
|
||||||
name: 'Name',
|
name: 'Name',
|
||||||
unique: 'Eindeutig',
|
unique: 'Eindeutig',
|
||||||
|
index_type: 'Indextyp',
|
||||||
delete_index: 'Index löschen',
|
delete_index: 'Index löschen',
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -181,32 +180,26 @@ export const de: LanguageTranslation = {
|
|||||||
description: 'Erstellen Sie eine Tabelle, um zu beginnen',
|
description: 'Erstellen Sie eine Tabelle, um zu beginnen',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: 'Beziehungen',
|
refs: 'Refs',
|
||||||
filter: 'Filter',
|
filter: 'Filter',
|
||||||
add_relationship: 'Beziehung hinzufügen',
|
|
||||||
collapse: 'Alle einklappen',
|
collapse: 'Alle einklappen',
|
||||||
|
add_relationship: 'Beziehung hinzufügen',
|
||||||
|
relationships: 'Beziehungen',
|
||||||
|
dependencies: 'Abhängigkeiten',
|
||||||
relationship: {
|
relationship: {
|
||||||
|
relationship: 'Beziehung',
|
||||||
primary: 'Primäre Tabelle',
|
primary: 'Primäre Tabelle',
|
||||||
foreign: 'Referenzierte Tabelle',
|
foreign: 'Referenzierte Tabelle',
|
||||||
cardinality: 'Kardinalität',
|
cardinality: 'Kardinalität',
|
||||||
delete_relationship: 'Beziehung löschen',
|
delete_relationship: 'Löschen',
|
||||||
relationship_actions: {
|
relationship_actions: {
|
||||||
title: 'Aktionen',
|
title: 'Aktionen',
|
||||||
delete_relationship: 'Beziehung löschen',
|
delete_relationship: 'Löschen',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
|
||||||
title: 'Keine Beziehungen',
|
|
||||||
description:
|
|
||||||
'Erstellen Sie eine Beziehung, um Tabellen zu verbinden',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dependencies_section: {
|
|
||||||
dependencies: 'Abhängigkeiten',
|
|
||||||
filter: 'Filter',
|
|
||||||
collapse: 'Alle einklappen',
|
|
||||||
dependency: {
|
dependency: {
|
||||||
|
dependency: 'Abhängigkeit',
|
||||||
table: 'Tabelle',
|
table: 'Tabelle',
|
||||||
dependent_table: 'Abhängige Ansicht',
|
dependent_table: 'Abhängige Ansicht',
|
||||||
delete_dependency: 'Löschen',
|
delete_dependency: 'Löschen',
|
||||||
@@ -216,8 +209,8 @@ export const de: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: 'Keine Abhängigkeiten',
|
title: 'Keine Beziehungen',
|
||||||
description: 'Erstellen Sie eine Ansicht, um zu beginnen',
|
description: 'Erstellen Sie eine Beziehung, um zu beginnen',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -257,6 +250,7 @@ export const de: LanguageTranslation = {
|
|||||||
enum_values: 'Enum Values',
|
enum_values: 'Enum Values',
|
||||||
composite_fields: 'Fields',
|
composite_fields: 'Fields',
|
||||||
no_fields: 'No fields defined',
|
no_fields: 'No fields defined',
|
||||||
|
no_values: 'Keine Enum-Werte definiert',
|
||||||
field_name_placeholder: 'Field name',
|
field_name_placeholder: 'Field name',
|
||||||
field_type_placeholder: 'Select type',
|
field_type_placeholder: 'Select type',
|
||||||
add_field: 'Add Field',
|
add_field: 'Add Field',
|
||||||
@@ -279,7 +273,7 @@ export const de: LanguageTranslation = {
|
|||||||
show_all: 'Alle anzeigen',
|
show_all: 'Alle anzeigen',
|
||||||
undo: 'Rückgängig',
|
undo: 'Rückgängig',
|
||||||
redo: 'Wiederholen',
|
redo: 'Wiederholen',
|
||||||
reorder_diagram: 'Diagramm neu anordnen',
|
reorder_diagram: 'Diagramm automatisch anordnen',
|
||||||
|
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||||
@@ -319,13 +313,13 @@ export const de: LanguageTranslation = {
|
|||||||
back: 'Zurück',
|
back: 'Zurück',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
import_from_file: 'Import from File',
|
import_from_file: 'Import from File',
|
||||||
empty_diagram: 'Leeres Diagramm',
|
empty_diagram: 'Leere Datenbank',
|
||||||
continue: 'Weiter',
|
continue: 'Weiter',
|
||||||
import: 'Importieren',
|
import: 'Importieren',
|
||||||
},
|
},
|
||||||
|
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: 'Diagramm öffnen',
|
title: 'Datenbank öffnen',
|
||||||
description: 'Wählen Sie ein Diagramm aus der Liste unten aus.',
|
description: 'Wählen Sie ein Diagramm aus der Liste unten aus.',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
name: 'Name',
|
name: 'Name',
|
||||||
@@ -335,6 +329,12 @@ export const de: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
cancel: 'Abbrechen',
|
cancel: 'Abbrechen',
|
||||||
open: 'Öffnen',
|
open: 'Öffnen',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: 'Öffnen',
|
||||||
|
duplicate: 'Duplizieren',
|
||||||
|
delete: 'Löschen',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -483,6 +483,7 @@ export const de: LanguageTranslation = {
|
|||||||
|
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: 'Neue Tabelle',
|
new_table: 'Neue Tabelle',
|
||||||
|
new_view: 'Neue Ansicht',
|
||||||
new_relationship: 'Neue Beziehung',
|
new_relationship: 'Neue Beziehung',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
new_area: 'New Area',
|
new_area: 'New Area',
|
||||||
@@ -505,6 +506,9 @@ export const de: LanguageTranslation = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: 'Sprache',
|
change_language: 'Sprache',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: 'Ein',
|
||||||
|
off: 'Aus',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata } from '../types';
|
|||||||
|
|
||||||
export const en = {
|
export const en = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: 'New',
|
||||||
|
browse: 'Browse',
|
||||||
|
tables: 'Tables',
|
||||||
|
refs: 'Refs',
|
||||||
|
areas: 'Areas',
|
||||||
|
dependencies: 'Dependencies',
|
||||||
|
custom_types: 'Custom Types',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: 'File',
|
actions: 'Actions',
|
||||||
new: 'New',
|
new: 'New...',
|
||||||
open: 'Open',
|
browse: 'Browse...',
|
||||||
save: 'Save',
|
save: 'Save',
|
||||||
import: 'Import',
|
import: 'Import',
|
||||||
export_sql: 'Export SQL',
|
export_sql: 'Export SQL',
|
||||||
export_as: 'Export as',
|
export_as: 'Export as',
|
||||||
delete_diagram: 'Delete Diagram',
|
delete_diagram: 'Delete',
|
||||||
exit: 'Exit',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: 'Edit',
|
edit: 'Edit',
|
||||||
@@ -29,6 +37,7 @@ export const en = {
|
|||||||
hide_field_attributes: 'Hide Field Attributes',
|
hide_field_attributes: 'Hide Field Attributes',
|
||||||
show_field_attributes: 'Show Field Attributes',
|
show_field_attributes: 'Show Field Attributes',
|
||||||
zoom_on_scroll: 'Zoom on Scroll',
|
zoom_on_scroll: 'Zoom on Scroll',
|
||||||
|
show_views: 'Database Views',
|
||||||
theme: 'Theme',
|
theme: 'Theme',
|
||||||
show_dependencies: 'Show Dependencies',
|
show_dependencies: 'Show Dependencies',
|
||||||
hide_dependencies: 'Hide Dependencies',
|
hide_dependencies: 'Hide Dependencies',
|
||||||
@@ -64,21 +73,13 @@ export const en = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: 'Reorder Diagram',
|
title: 'Auto Arrange Diagram',
|
||||||
description:
|
description:
|
||||||
'This action will rearrange all tables in the diagram. Do you want to continue?',
|
'This action will rearrange all tables in the diagram. Do you want to continue?',
|
||||||
reorder: 'Reorder',
|
reorder: 'Auto Arrange',
|
||||||
cancel: 'Cancel',
|
cancel: 'Cancel',
|
||||||
},
|
},
|
||||||
|
|
||||||
multiple_schemas_alert: {
|
|
||||||
title: 'Multiple Schemas',
|
|
||||||
description:
|
|
||||||
'{{schemasCount}} schemas in this diagram. Currently displaying: {{formattedSchemas}}.',
|
|
||||||
show_me: 'Show me',
|
|
||||||
none: 'none',
|
|
||||||
},
|
|
||||||
|
|
||||||
copy_to_clipboard_toast: {
|
copy_to_clipboard_toast: {
|
||||||
unsupported: {
|
unsupported: {
|
||||||
title: 'Copy failed',
|
title: 'Copy failed',
|
||||||
@@ -113,14 +114,11 @@ export const en = {
|
|||||||
copied: 'Copied!',
|
copied: 'Copied!',
|
||||||
|
|
||||||
side_panel: {
|
side_panel: {
|
||||||
schema: 'Schema:',
|
|
||||||
filter_by_schema: 'Filter by schema',
|
|
||||||
search_schema: 'Search schema...',
|
|
||||||
no_schemas_found: 'No schemas found.',
|
|
||||||
view_all_options: 'View all Options...',
|
view_all_options: 'View all Options...',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: 'Tables',
|
tables: 'Tables',
|
||||||
add_table: 'Add Table',
|
add_table: 'Add Table',
|
||||||
|
add_view: 'Add View',
|
||||||
filter: 'Filter',
|
filter: 'Filter',
|
||||||
collapse: 'Collapse All',
|
collapse: 'Collapse All',
|
||||||
clear: 'Clear Filter',
|
clear: 'Clear Filter',
|
||||||
@@ -144,6 +142,7 @@ export const en = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: 'Field Attributes',
|
title: 'Field Attributes',
|
||||||
unique: 'Unique',
|
unique: 'Unique',
|
||||||
|
auto_increment: 'Auto Increment',
|
||||||
character_length: 'Max Length',
|
character_length: 'Max Length',
|
||||||
precision: 'Precision',
|
precision: 'Precision',
|
||||||
scale: 'Scale',
|
scale: 'Scale',
|
||||||
@@ -157,6 +156,7 @@ export const en = {
|
|||||||
title: 'Index Attributes',
|
title: 'Index Attributes',
|
||||||
name: 'Name',
|
name: 'Name',
|
||||||
unique: 'Unique',
|
unique: 'Unique',
|
||||||
|
index_type: 'Index Type',
|
||||||
delete_index: 'Delete Index',
|
delete_index: 'Delete Index',
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -173,12 +173,15 @@ export const en = {
|
|||||||
description: 'Create a table to get started',
|
description: 'Create a table to get started',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: 'Relationships',
|
refs: 'Refs',
|
||||||
filter: 'Filter',
|
filter: 'Filter',
|
||||||
add_relationship: 'Add Relationship',
|
|
||||||
collapse: 'Collapse All',
|
collapse: 'Collapse All',
|
||||||
|
add_relationship: 'Add Relationship',
|
||||||
|
relationships: 'Relationships',
|
||||||
|
dependencies: 'Dependencies',
|
||||||
relationship: {
|
relationship: {
|
||||||
|
relationship: 'Relationship',
|
||||||
primary: 'Primary Table',
|
primary: 'Primary Table',
|
||||||
foreign: 'Referenced Table',
|
foreign: 'Referenced Table',
|
||||||
cardinality: 'Cardinality',
|
cardinality: 'Cardinality',
|
||||||
@@ -188,16 +191,8 @@ export const en = {
|
|||||||
delete_relationship: 'Delete',
|
delete_relationship: 'Delete',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
|
||||||
title: 'No relationships',
|
|
||||||
description: 'Create a relationship to connect tables',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dependencies_section: {
|
|
||||||
dependencies: 'Dependencies',
|
|
||||||
filter: 'Filter',
|
|
||||||
collapse: 'Collapse All',
|
|
||||||
dependency: {
|
dependency: {
|
||||||
|
dependency: 'Dependency',
|
||||||
table: 'Table',
|
table: 'Table',
|
||||||
dependent_table: 'Dependent View',
|
dependent_table: 'Dependent View',
|
||||||
delete_dependency: 'Delete',
|
delete_dependency: 'Delete',
|
||||||
@@ -207,8 +202,8 @@ export const en = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: 'No dependencies',
|
title: 'No relationships',
|
||||||
description: 'Create a view to get started',
|
description: 'Create a relationship to get started',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -247,6 +242,7 @@ export const en = {
|
|||||||
enum_values: 'Enum Values',
|
enum_values: 'Enum Values',
|
||||||
composite_fields: 'Fields',
|
composite_fields: 'Fields',
|
||||||
no_fields: 'No fields defined',
|
no_fields: 'No fields defined',
|
||||||
|
no_values: 'No enum values defined',
|
||||||
field_name_placeholder: 'Field name',
|
field_name_placeholder: 'Field name',
|
||||||
field_type_placeholder: 'Select type',
|
field_type_placeholder: 'Select type',
|
||||||
add_field: 'Add Field',
|
add_field: 'Add Field',
|
||||||
@@ -269,7 +265,7 @@ export const en = {
|
|||||||
show_all: 'Show All',
|
show_all: 'Show All',
|
||||||
undo: 'Undo',
|
undo: 'Undo',
|
||||||
redo: 'Redo',
|
redo: 'Redo',
|
||||||
reorder_diagram: 'Reorder Diagram',
|
reorder_diagram: 'Auto Arrange Diagram',
|
||||||
highlight_overlapping_tables: 'Highlight Overlapping Tables',
|
highlight_overlapping_tables: 'Highlight Overlapping Tables',
|
||||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||||
custom_type_highlight_tooltip:
|
custom_type_highlight_tooltip:
|
||||||
@@ -305,13 +301,13 @@ export const en = {
|
|||||||
cancel: 'Cancel',
|
cancel: 'Cancel',
|
||||||
import_from_file: 'Import from File',
|
import_from_file: 'Import from File',
|
||||||
back: 'Back',
|
back: 'Back',
|
||||||
empty_diagram: 'Empty diagram',
|
empty_diagram: 'Empty database',
|
||||||
continue: 'Continue',
|
continue: 'Continue',
|
||||||
import: 'Import',
|
import: 'Import',
|
||||||
},
|
},
|
||||||
|
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: 'Open Diagram',
|
title: 'Open Database',
|
||||||
description: 'Select a diagram to open from the list below.',
|
description: 'Select a diagram to open from the list below.',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
name: 'Name',
|
name: 'Name',
|
||||||
@@ -321,6 +317,12 @@ export const en = {
|
|||||||
},
|
},
|
||||||
cancel: 'Cancel',
|
cancel: 'Cancel',
|
||||||
open: 'Open',
|
open: 'Open',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: 'Open',
|
||||||
|
duplicate: 'Duplicate',
|
||||||
|
delete: 'Delete',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -468,6 +470,7 @@ export const en = {
|
|||||||
|
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: 'New Table',
|
new_table: 'New Table',
|
||||||
|
new_view: 'New View',
|
||||||
new_relationship: 'New Relationship',
|
new_relationship: 'New Relationship',
|
||||||
new_area: 'New Area',
|
new_area: 'New Area',
|
||||||
},
|
},
|
||||||
@@ -488,6 +491,9 @@ export const en = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: 'Language',
|
change_language: 'Language',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: 'On',
|
||||||
|
off: 'Off',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
|||||||
|
|
||||||
export const es: LanguageTranslation = {
|
export const es: LanguageTranslation = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: 'Nuevo',
|
||||||
|
browse: 'Examinar',
|
||||||
|
tables: 'Tablas',
|
||||||
|
refs: 'Refs',
|
||||||
|
areas: 'Áreas',
|
||||||
|
dependencies: 'Dependencias',
|
||||||
|
custom_types: 'Tipos Personalizados',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: 'Archivo',
|
actions: 'Acciones',
|
||||||
new: 'Nuevo',
|
new: 'Nuevo...',
|
||||||
open: 'Abrir',
|
browse: 'Examinar...',
|
||||||
save: 'Guardar',
|
save: 'Guardar',
|
||||||
import: 'Importar Base de Datos',
|
import: 'Importar Base de Datos',
|
||||||
export_sql: 'Exportar SQL',
|
export_sql: 'Exportar SQL',
|
||||||
export_as: 'Exportar como',
|
export_as: 'Exportar como',
|
||||||
delete_diagram: 'Eliminar Diagrama',
|
delete_diagram: 'Eliminar',
|
||||||
exit: 'Salir',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: 'Editar',
|
edit: 'Editar',
|
||||||
@@ -29,6 +37,7 @@ export const es: LanguageTranslation = {
|
|||||||
show_sidebar: 'Mostrar Barra Lateral',
|
show_sidebar: 'Mostrar Barra Lateral',
|
||||||
hide_sidebar: 'Ocultar Barra Lateral',
|
hide_sidebar: 'Ocultar Barra Lateral',
|
||||||
zoom_on_scroll: 'Zoom al Desplazarse',
|
zoom_on_scroll: 'Zoom al Desplazarse',
|
||||||
|
show_views: 'Vistas de Base de Datos',
|
||||||
theme: 'Tema',
|
theme: 'Tema',
|
||||||
show_dependencies: 'Mostrar dependencias',
|
show_dependencies: 'Mostrar dependencias',
|
||||||
hide_dependencies: 'Ocultar dependencias',
|
hide_dependencies: 'Ocultar dependencias',
|
||||||
@@ -65,10 +74,10 @@ export const es: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: 'Reordenar Diagrama',
|
title: 'Organizar Diagrama Automáticamente',
|
||||||
description:
|
description:
|
||||||
'Esta acción reorganizará todas las tablas en el diagrama. ¿Deseas continuar?',
|
'Esta acción reorganizará todas las tablas en el diagrama. ¿Deseas continuar?',
|
||||||
reorder: 'Reordenar',
|
reorder: 'Organizar Automáticamente',
|
||||||
cancel: 'Cancelar',
|
cancel: 'Cancelar',
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -106,14 +115,11 @@ export const es: LanguageTranslation = {
|
|||||||
copied: 'Copied!',
|
copied: 'Copied!',
|
||||||
|
|
||||||
side_panel: {
|
side_panel: {
|
||||||
schema: 'Esquema:',
|
|
||||||
filter_by_schema: 'Filtrar por esquema',
|
|
||||||
search_schema: 'Buscar esquema...',
|
|
||||||
no_schemas_found: 'No se encontraron esquemas.',
|
|
||||||
view_all_options: 'Ver todas las opciones...',
|
view_all_options: 'Ver todas las opciones...',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: 'Tablas',
|
tables: 'Tablas',
|
||||||
add_table: 'Agregar Tabla',
|
add_table: 'Agregar Tabla',
|
||||||
|
add_view: 'Agregar Vista',
|
||||||
filter: 'Filtrar',
|
filter: 'Filtrar',
|
||||||
collapse: 'Colapsar Todo',
|
collapse: 'Colapsar Todo',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
@@ -139,6 +145,7 @@ export const es: LanguageTranslation = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: 'Atributos del Campo',
|
title: 'Atributos del Campo',
|
||||||
unique: 'Único',
|
unique: 'Único',
|
||||||
|
auto_increment: 'Autoincremento',
|
||||||
comments: 'Comentarios',
|
comments: 'Comentarios',
|
||||||
no_comments: 'Sin comentarios',
|
no_comments: 'Sin comentarios',
|
||||||
delete_field: 'Eliminar Campo',
|
delete_field: 'Eliminar Campo',
|
||||||
@@ -154,6 +161,7 @@ export const es: LanguageTranslation = {
|
|||||||
title: 'Atributos del Índice',
|
title: 'Atributos del Índice',
|
||||||
name: 'Nombre',
|
name: 'Nombre',
|
||||||
unique: 'Único',
|
unique: 'Único',
|
||||||
|
index_type: 'Tipo de Índice',
|
||||||
delete_index: 'Eliminar Índice',
|
delete_index: 'Eliminar Índice',
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -170,14 +178,17 @@ export const es: LanguageTranslation = {
|
|||||||
description: 'Crea una tabla para comenzar',
|
description: 'Crea una tabla para comenzar',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: 'Relaciones',
|
refs: 'Refs',
|
||||||
add_relationship: 'Agregar Relación',
|
|
||||||
filter: 'Filtrar',
|
filter: 'Filtrar',
|
||||||
collapse: 'Colapsar Todo',
|
collapse: 'Colapsar Todo',
|
||||||
|
add_relationship: 'Agregar Relación',
|
||||||
|
relationships: 'Relaciones',
|
||||||
|
dependencies: 'Dependencias',
|
||||||
relationship: {
|
relationship: {
|
||||||
primary: 'Primaria',
|
relationship: 'Relación',
|
||||||
foreign: 'Foránea',
|
primary: 'Tabla Primaria',
|
||||||
|
foreign: 'Tabla Referenciada',
|
||||||
cardinality: 'Cardinalidad',
|
cardinality: 'Cardinalidad',
|
||||||
delete_relationship: 'Eliminar',
|
delete_relationship: 'Eliminar',
|
||||||
relationship_actions: {
|
relationship_actions: {
|
||||||
@@ -185,18 +196,10 @@ export const es: LanguageTranslation = {
|
|||||||
delete_relationship: 'Eliminar',
|
delete_relationship: 'Eliminar',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
|
||||||
title: 'No hay relaciones',
|
|
||||||
description: 'Crea una relación para conectar tablas',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dependencies_section: {
|
|
||||||
dependencies: 'Dependencias',
|
|
||||||
filter: 'Filtro',
|
|
||||||
collapse: 'Colapsar todo',
|
|
||||||
dependency: {
|
dependency: {
|
||||||
|
dependency: 'Dependencia',
|
||||||
table: 'Tabla',
|
table: 'Tabla',
|
||||||
dependent_table: 'Vista dependiente',
|
dependent_table: 'Vista Dependiente',
|
||||||
delete_dependency: 'Eliminar',
|
delete_dependency: 'Eliminar',
|
||||||
dependency_actions: {
|
dependency_actions: {
|
||||||
title: 'Acciones',
|
title: 'Acciones',
|
||||||
@@ -204,8 +207,8 @@ export const es: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: 'Sin dependencias',
|
title: 'Sin relaciones',
|
||||||
description: 'Crea una vista para comenzar',
|
description: 'Crea una relación para comenzar',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -245,6 +248,7 @@ export const es: LanguageTranslation = {
|
|||||||
enum_values: 'Enum Values',
|
enum_values: 'Enum Values',
|
||||||
composite_fields: 'Fields',
|
composite_fields: 'Fields',
|
||||||
no_fields: 'No fields defined',
|
no_fields: 'No fields defined',
|
||||||
|
no_values: 'No hay valores de enum definidos',
|
||||||
field_name_placeholder: 'Field name',
|
field_name_placeholder: 'Field name',
|
||||||
field_type_placeholder: 'Select type',
|
field_type_placeholder: 'Select type',
|
||||||
add_field: 'Add Field',
|
add_field: 'Add Field',
|
||||||
@@ -267,7 +271,7 @@ export const es: LanguageTranslation = {
|
|||||||
show_all: 'Mostrar Todo',
|
show_all: 'Mostrar Todo',
|
||||||
undo: 'Deshacer',
|
undo: 'Deshacer',
|
||||||
redo: 'Rehacer',
|
redo: 'Rehacer',
|
||||||
reorder_diagram: 'Reordenar Diagrama',
|
reorder_diagram: 'Organizar Diagrama Automáticamente',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||||
custom_type_highlight_tooltip:
|
custom_type_highlight_tooltip:
|
||||||
@@ -306,13 +310,13 @@ export const es: LanguageTranslation = {
|
|||||||
back: 'Atrás',
|
back: 'Atrás',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
import_from_file: 'Import from File',
|
import_from_file: 'Import from File',
|
||||||
empty_diagram: 'Diagrama vacío',
|
empty_diagram: 'Base de datos vacía',
|
||||||
continue: 'Continuar',
|
continue: 'Continuar',
|
||||||
import: 'Importar',
|
import: 'Importar',
|
||||||
},
|
},
|
||||||
|
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: 'Abrir Diagrama',
|
title: 'Abrir Base de Datos',
|
||||||
description:
|
description:
|
||||||
'Selecciona un diagrama para abrir de la lista a continuación.',
|
'Selecciona un diagrama para abrir de la lista a continuación.',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
@@ -323,6 +327,12 @@ export const es: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
cancel: 'Cancelar',
|
cancel: 'Cancelar',
|
||||||
open: 'Abrir',
|
open: 'Abrir',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: 'Abrir',
|
||||||
|
duplicate: 'Duplicar',
|
||||||
|
delete: 'Eliminar',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -424,14 +434,6 @@ export const es: LanguageTranslation = {
|
|||||||
confirm: '¡Claro!',
|
confirm: '¡Claro!',
|
||||||
},
|
},
|
||||||
|
|
||||||
multiple_schemas_alert: {
|
|
||||||
title: 'Múltiples Esquemas',
|
|
||||||
description:
|
|
||||||
'{{schemasCount}} esquemas en este diagrama. Actualmente mostrando: {{formattedSchemas}}.',
|
|
||||||
// TODO: Translate
|
|
||||||
show_me: 'Show me',
|
|
||||||
none: 'nada',
|
|
||||||
},
|
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
export_diagram_dialog: {
|
export_diagram_dialog: {
|
||||||
title: 'Export Diagram',
|
title: 'Export Diagram',
|
||||||
@@ -480,6 +482,7 @@ export const es: LanguageTranslation = {
|
|||||||
|
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: 'Nueva Tabla',
|
new_table: 'Nueva Tabla',
|
||||||
|
new_view: 'Nueva Vista',
|
||||||
new_relationship: 'Nueva Relación',
|
new_relationship: 'Nueva Relación',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
new_area: 'New Area',
|
new_area: 'New Area',
|
||||||
@@ -502,6 +505,9 @@ export const es: LanguageTranslation = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: 'Idioma',
|
change_language: 'Idioma',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: 'Encendido',
|
||||||
|
off: 'Apagado',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
|||||||
|
|
||||||
export const fr: LanguageTranslation = {
|
export const fr: LanguageTranslation = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: 'Nouveau',
|
||||||
|
browse: 'Parcourir',
|
||||||
|
tables: 'Tables',
|
||||||
|
refs: 'Refs',
|
||||||
|
areas: 'Zones',
|
||||||
|
dependencies: 'Dépendances',
|
||||||
|
custom_types: 'Types Personnalisés',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: 'Fichier',
|
actions: 'Actions',
|
||||||
new: 'Nouveau',
|
new: 'Nouveau...',
|
||||||
open: 'Ouvrir',
|
browse: 'Parcourir...',
|
||||||
save: 'Enregistrer',
|
save: 'Enregistrer',
|
||||||
import: 'Importer Base de Données',
|
import: 'Importer Base de Données',
|
||||||
export_sql: 'Exporter SQL',
|
export_sql: 'Exporter SQL',
|
||||||
export_as: 'Exporter en tant que',
|
export_as: 'Exporter en tant que',
|
||||||
delete_diagram: 'Supprimer le Diagramme',
|
delete_diagram: 'Supprimer',
|
||||||
exit: 'Quitter',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: 'Édition',
|
edit: 'Édition',
|
||||||
@@ -29,6 +37,7 @@ export const fr: LanguageTranslation = {
|
|||||||
hide_field_attributes: 'Masquer les Attributs de Champ',
|
hide_field_attributes: 'Masquer les Attributs de Champ',
|
||||||
show_field_attributes: 'Afficher les Attributs de Champ',
|
show_field_attributes: 'Afficher les Attributs de Champ',
|
||||||
zoom_on_scroll: 'Zoom sur le Défilement',
|
zoom_on_scroll: 'Zoom sur le Défilement',
|
||||||
|
show_views: 'Vues de Base de Données',
|
||||||
theme: 'Thème',
|
theme: 'Thème',
|
||||||
show_dependencies: 'Afficher les Dépendances',
|
show_dependencies: 'Afficher les Dépendances',
|
||||||
hide_dependencies: 'Masquer les Dépendances',
|
hide_dependencies: 'Masquer les Dépendances',
|
||||||
@@ -64,10 +73,10 @@ export const fr: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: 'Réorganiser le Diagramme',
|
title: 'Organiser Automatiquement le Diagramme',
|
||||||
description:
|
description:
|
||||||
'Cette action réorganisera toutes les tables dans le diagramme. Voulez-vous continuer ?',
|
'Cette action réorganisera toutes les tables dans le diagramme. Voulez-vous continuer ?',
|
||||||
reorder: 'Réorganiser',
|
reorder: 'Organiser Automatiquement',
|
||||||
cancel: 'Annuler',
|
cancel: 'Annuler',
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -105,14 +114,11 @@ export const fr: LanguageTranslation = {
|
|||||||
copied: 'Copié !',
|
copied: 'Copié !',
|
||||||
|
|
||||||
side_panel: {
|
side_panel: {
|
||||||
schema: 'Schéma:',
|
|
||||||
filter_by_schema: 'Filtrer par schéma',
|
|
||||||
search_schema: 'Rechercher un schéma...',
|
|
||||||
no_schemas_found: 'Aucun schéma trouvé.',
|
|
||||||
view_all_options: 'Voir toutes les Options...',
|
view_all_options: 'Voir toutes les Options...',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: 'Tables',
|
tables: 'Tables',
|
||||||
add_table: 'Ajouter une Table',
|
add_table: 'Ajouter une Table',
|
||||||
|
add_view: 'Ajouter une Vue',
|
||||||
filter: 'Filtrer',
|
filter: 'Filtrer',
|
||||||
collapse: 'Réduire Tout',
|
collapse: 'Réduire Tout',
|
||||||
clear: 'Effacer le Filtre',
|
clear: 'Effacer le Filtre',
|
||||||
@@ -137,6 +143,7 @@ export const fr: LanguageTranslation = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: 'Attributs du Champ',
|
title: 'Attributs du Champ',
|
||||||
unique: 'Unique',
|
unique: 'Unique',
|
||||||
|
auto_increment: 'Auto-incrément',
|
||||||
comments: 'Commentaires',
|
comments: 'Commentaires',
|
||||||
no_comments: 'Pas de commentaires',
|
no_comments: 'Pas de commentaires',
|
||||||
delete_field: 'Supprimer le Champ',
|
delete_field: 'Supprimer le Champ',
|
||||||
@@ -152,6 +159,7 @@ export const fr: LanguageTranslation = {
|
|||||||
title: "Attributs de l'Index",
|
title: "Attributs de l'Index",
|
||||||
name: 'Nom',
|
name: 'Nom',
|
||||||
unique: 'Unique',
|
unique: 'Unique',
|
||||||
|
index_type: "Type d'index",
|
||||||
delete_index: "Supprimer l'Index",
|
delete_index: "Supprimer l'Index",
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -168,12 +176,15 @@ export const fr: LanguageTranslation = {
|
|||||||
description: 'Créez une table pour commencer',
|
description: 'Créez une table pour commencer',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: 'Relations',
|
refs: 'Refs',
|
||||||
filter: 'Filtrer',
|
filter: 'Filtrer',
|
||||||
add_relationship: 'Ajouter une Relation',
|
|
||||||
collapse: 'Réduire Tout',
|
collapse: 'Réduire Tout',
|
||||||
|
add_relationship: 'Ajouter une Relation',
|
||||||
|
relationships: 'Relations',
|
||||||
|
dependencies: 'Dépendances',
|
||||||
relationship: {
|
relationship: {
|
||||||
|
relationship: 'Relation',
|
||||||
primary: 'Table Principale',
|
primary: 'Table Principale',
|
||||||
foreign: 'Table Référencée',
|
foreign: 'Table Référencée',
|
||||||
cardinality: 'Cardinalité',
|
cardinality: 'Cardinalité',
|
||||||
@@ -183,16 +194,8 @@ export const fr: LanguageTranslation = {
|
|||||||
delete_relationship: 'Supprimer',
|
delete_relationship: 'Supprimer',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
|
||||||
title: 'Aucune relation',
|
|
||||||
description: 'Créez une relation pour connecter les tables',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dependencies_section: {
|
|
||||||
dependencies: 'Dépendances',
|
|
||||||
filter: 'Filtrer',
|
|
||||||
collapse: 'Réduire Tout',
|
|
||||||
dependency: {
|
dependency: {
|
||||||
|
dependency: 'Dépendance',
|
||||||
table: 'Table',
|
table: 'Table',
|
||||||
dependent_table: 'Vue Dépendante',
|
dependent_table: 'Vue Dépendante',
|
||||||
delete_dependency: 'Supprimer',
|
delete_dependency: 'Supprimer',
|
||||||
@@ -202,8 +205,8 @@ export const fr: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: 'Aucune dépendance',
|
title: 'Aucune relation',
|
||||||
description: 'Créez une vue pour commencer',
|
description: 'Créez une relation pour commencer',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -243,6 +246,7 @@ export const fr: LanguageTranslation = {
|
|||||||
enum_values: 'Enum Values',
|
enum_values: 'Enum Values',
|
||||||
composite_fields: 'Fields',
|
composite_fields: 'Fields',
|
||||||
no_fields: 'No fields defined',
|
no_fields: 'No fields defined',
|
||||||
|
no_values: "Aucune valeur d'énumération définie",
|
||||||
field_name_placeholder: 'Field name',
|
field_name_placeholder: 'Field name',
|
||||||
field_type_placeholder: 'Select type',
|
field_type_placeholder: 'Select type',
|
||||||
add_field: 'Add Field',
|
add_field: 'Add Field',
|
||||||
@@ -265,7 +269,7 @@ export const fr: LanguageTranslation = {
|
|||||||
show_all: 'Afficher Tout',
|
show_all: 'Afficher Tout',
|
||||||
undo: 'Annuler',
|
undo: 'Annuler',
|
||||||
redo: 'Rétablir',
|
redo: 'Rétablir',
|
||||||
reorder_diagram: 'Réorganiser le Diagramme',
|
reorder_diagram: 'Organiser Automatiquement le Diagramme',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||||
custom_type_highlight_tooltip:
|
custom_type_highlight_tooltip:
|
||||||
@@ -303,13 +307,13 @@ export const fr: LanguageTranslation = {
|
|||||||
cancel: 'Annuler',
|
cancel: 'Annuler',
|
||||||
back: 'Retour',
|
back: 'Retour',
|
||||||
import_from_file: "Importer à partir d'un fichier",
|
import_from_file: "Importer à partir d'un fichier",
|
||||||
empty_diagram: 'Diagramme vide',
|
empty_diagram: 'Base de données vide',
|
||||||
continue: 'Continuer',
|
continue: 'Continuer',
|
||||||
import: 'Importer',
|
import: 'Importer',
|
||||||
},
|
},
|
||||||
|
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: 'Ouvrir Diagramme',
|
title: 'Ouvrir Base de Données',
|
||||||
description:
|
description:
|
||||||
'Sélectionnez un diagramme à ouvrir dans la liste ci-dessous.',
|
'Sélectionnez un diagramme à ouvrir dans la liste ci-dessous.',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
@@ -320,6 +324,12 @@ export const fr: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
cancel: 'Annuler',
|
cancel: 'Annuler',
|
||||||
open: 'Ouvrir',
|
open: 'Ouvrir',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: 'Ouvrir',
|
||||||
|
duplicate: 'Dupliquer',
|
||||||
|
delete: 'Supprimer',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -357,15 +367,6 @@ export const fr: LanguageTranslation = {
|
|||||||
transparent_description: 'Remove background color from image.',
|
transparent_description: 'Remove background color from image.',
|
||||||
},
|
},
|
||||||
|
|
||||||
multiple_schemas_alert: {
|
|
||||||
title: 'Schémas Multiples',
|
|
||||||
description:
|
|
||||||
'{{schemasCount}} schémas dans ce diagramme. Actuellement affiché(s) : {{formattedSchemas}}.',
|
|
||||||
// TODO: Translate
|
|
||||||
show_me: 'Show me',
|
|
||||||
none: 'Aucun',
|
|
||||||
},
|
|
||||||
|
|
||||||
new_table_schema_dialog: {
|
new_table_schema_dialog: {
|
||||||
title: 'Sélectionner un Schéma',
|
title: 'Sélectionner un Schéma',
|
||||||
description:
|
description:
|
||||||
@@ -477,6 +478,7 @@ export const fr: LanguageTranslation = {
|
|||||||
|
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: 'Nouvelle Table',
|
new_table: 'Nouvelle Table',
|
||||||
|
new_view: 'Nouvelle Vue',
|
||||||
new_relationship: 'Nouvelle Relation',
|
new_relationship: 'Nouvelle Relation',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
new_area: 'New Area',
|
new_area: 'New Area',
|
||||||
@@ -499,6 +501,9 @@ export const fr: LanguageTranslation = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: 'Langue',
|
change_language: 'Langue',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: 'Activé',
|
||||||
|
off: 'Désactivé',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
|||||||
|
|
||||||
export const gu: LanguageTranslation = {
|
export const gu: LanguageTranslation = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: 'નવું',
|
||||||
|
browse: 'બ્રાઉજ',
|
||||||
|
tables: 'ટેબલો',
|
||||||
|
refs: 'રેફ્સ',
|
||||||
|
areas: 'ક્ષેત્રો',
|
||||||
|
dependencies: 'નિર્ભરતાઓ',
|
||||||
|
custom_types: 'કસ્ટમ ટાઇપ',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: 'ફાઇલ',
|
actions: 'ક્રિયાઓ',
|
||||||
new: 'નવું',
|
new: 'નવું...',
|
||||||
open: 'ખોલો',
|
browse: 'બ્રાઉજ કરો...',
|
||||||
save: 'સાચવો',
|
save: 'સાચવો',
|
||||||
import: 'ડેટાબેસ આયાત કરો',
|
import: 'ડેટાબેસ આયાત કરો',
|
||||||
export_sql: 'SQL નિકાસ કરો',
|
export_sql: 'SQL નિકાસ કરો',
|
||||||
export_as: 'રૂપે નિકાસ કરો',
|
export_as: 'રૂપે નિકાસ કરો',
|
||||||
delete_diagram: 'ડાયાગ્રામ કાઢી નાખો',
|
delete_diagram: 'કાઢી નાખો',
|
||||||
exit: 'બહાર જાઓ',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: 'ફેરફાર',
|
edit: 'ફેરફાર',
|
||||||
@@ -29,6 +37,7 @@ export const gu: LanguageTranslation = {
|
|||||||
hide_field_attributes: 'ફીલ્ડ અટ્રિબ્યુટ્સ છુપાવો',
|
hide_field_attributes: 'ફીલ્ડ અટ્રિબ્યુટ્સ છુપાવો',
|
||||||
show_field_attributes: 'ફીલ્ડ અટ્રિબ્યુટ્સ બતાવો',
|
show_field_attributes: 'ફીલ્ડ અટ્રિબ્યુટ્સ બતાવો',
|
||||||
zoom_on_scroll: 'સ્ક્રોલ પર ઝૂમ કરો',
|
zoom_on_scroll: 'સ્ક્રોલ પર ઝૂમ કરો',
|
||||||
|
show_views: 'ડેટાબેઝ વ્યૂઝ',
|
||||||
theme: 'થિમ',
|
theme: 'થિમ',
|
||||||
show_dependencies: 'નિર્ભરતાઓ બતાવો',
|
show_dependencies: 'નિર્ભરતાઓ બતાવો',
|
||||||
hide_dependencies: 'નિર્ભરતાઓ છુપાવો',
|
hide_dependencies: 'નિર્ભરતાઓ છુપાવો',
|
||||||
@@ -66,22 +75,13 @@ export const gu: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: 'ડાયાગ્રામ ફરી વ્યવસ્થિત કરો',
|
title: 'ડાયાગ્રામ ઑટોમેટિક ગોઠવો',
|
||||||
description:
|
description:
|
||||||
'આ ક્રિયા ડાયાગ્રામમાં બધી ટેબલ્સને ફરીથી વ્યવસ્થિત કરશે. શું તમે ચાલુ રાખવા માંગો છો?',
|
'આ ક્રિયા ડાયાગ્રામમાં બધી ટેબલ્સને ફરીથી વ્યવસ્થિત કરશે. શું તમે ચાલુ રાખવા માંગો છો?',
|
||||||
reorder: 'ફરી વ્યવસ્થિત કરો',
|
reorder: 'ઑટોમેટિક ગોઠવો',
|
||||||
cancel: 'રદ કરો',
|
cancel: 'રદ કરો',
|
||||||
},
|
},
|
||||||
|
|
||||||
multiple_schemas_alert: {
|
|
||||||
title: 'કઈંક વધારે સ્કીમા',
|
|
||||||
description:
|
|
||||||
'{{schemasCount}} સ્કીમા આ ડાયાગ્રામમાં છે. હાલમાં દર્શાવેલ છે: {{formattedSchemas}}.',
|
|
||||||
// TODO: Translate
|
|
||||||
show_me: 'Show me',
|
|
||||||
none: 'કઈ નહીં',
|
|
||||||
},
|
|
||||||
|
|
||||||
copy_to_clipboard_toast: {
|
copy_to_clipboard_toast: {
|
||||||
unsupported: {
|
unsupported: {
|
||||||
title: 'નકલ નિષ્ફળ',
|
title: 'નકલ નિષ્ફળ',
|
||||||
@@ -116,14 +116,11 @@ export const gu: LanguageTranslation = {
|
|||||||
copied: 'નકલ થયું!',
|
copied: 'નકલ થયું!',
|
||||||
|
|
||||||
side_panel: {
|
side_panel: {
|
||||||
schema: 'સ્કીમા:',
|
|
||||||
filter_by_schema: 'સ્કીમા દ્વારા ફિલ્ટર કરો',
|
|
||||||
search_schema: 'સ્કીમા શોધો...',
|
|
||||||
no_schemas_found: 'કોઈ સ્કીમા મળ્યા નથી.',
|
|
||||||
view_all_options: 'બધા વિકલ્પો જુઓ...',
|
view_all_options: 'બધા વિકલ્પો જુઓ...',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: 'ટેબલ્સ',
|
tables: 'ટેબલ્સ',
|
||||||
add_table: 'ટેબલ ઉમેરો',
|
add_table: 'ટેબલ ઉમેરો',
|
||||||
|
add_view: 'વ્યૂ ઉમેરો',
|
||||||
filter: 'ફિલ્ટર',
|
filter: 'ફિલ્ટર',
|
||||||
collapse: 'બધાને સકુચિત કરો',
|
collapse: 'બધાને સકુચિત કરો',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
@@ -150,6 +147,7 @@ export const gu: LanguageTranslation = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: 'ફીલ્ડ લક્ષણો',
|
title: 'ફીલ્ડ લક્ષણો',
|
||||||
unique: 'અદ્વિતીય',
|
unique: 'અદ્વિતીય',
|
||||||
|
auto_increment: 'ઑટો ઇન્ક્રિમેન્ટ',
|
||||||
comments: 'ટિપ્પણીઓ',
|
comments: 'ટિપ્પણીઓ',
|
||||||
no_comments: 'કોઈ ટિપ્પણીઓ નથી',
|
no_comments: 'કોઈ ટિપ્પણીઓ નથી',
|
||||||
delete_field: 'ફીલ્ડ કાઢી નાખો',
|
delete_field: 'ફીલ્ડ કાઢી નાખો',
|
||||||
@@ -165,6 +163,7 @@ export const gu: LanguageTranslation = {
|
|||||||
title: 'ઇન્ડેક્સ લક્ષણો',
|
title: 'ઇન્ડેક્સ લક્ષણો',
|
||||||
name: 'નામ',
|
name: 'નામ',
|
||||||
unique: 'અદ્વિતીય',
|
unique: 'અદ્વિતીય',
|
||||||
|
index_type: 'ઇન્ડેક્સ પ્રકાર',
|
||||||
delete_index: 'ઇન્ડેક્સ કાઢી નાખો',
|
delete_index: 'ઇન્ડેક્સ કાઢી નાખો',
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -181,14 +180,17 @@ export const gu: LanguageTranslation = {
|
|||||||
description: 'શરૂ કરવા માટે એક ટેબલ બનાવો',
|
description: 'શરૂ કરવા માટે એક ટેબલ બનાવો',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: 'સંબંધો',
|
refs: 'રેફ્સ',
|
||||||
filter: 'ફિલ્ટર',
|
filter: 'ફિલ્ટર',
|
||||||
add_relationship: 'સંબંધ ઉમેરો',
|
|
||||||
collapse: 'બધાને સકુચિત કરો',
|
collapse: 'બધાને સકુચિત કરો',
|
||||||
|
add_relationship: 'સંબંધ ઉમેરો',
|
||||||
|
relationships: 'સંબંધો',
|
||||||
|
dependencies: 'નિર્ભરતાઓ',
|
||||||
relationship: {
|
relationship: {
|
||||||
|
relationship: 'સંબંધ',
|
||||||
primary: 'પ્રાથમિક ટેબલ',
|
primary: 'પ્રાથમિક ટેબલ',
|
||||||
foreign: 'સંદર્ભ ટેબલ',
|
foreign: 'સંદર્ભિત ટેબલ',
|
||||||
cardinality: 'કાર્ડિનાલિટી',
|
cardinality: 'કાર્ડિનાલિટી',
|
||||||
delete_relationship: 'કાઢી નાખો',
|
delete_relationship: 'કાઢી નાખો',
|
||||||
relationship_actions: {
|
relationship_actions: {
|
||||||
@@ -196,27 +198,19 @@ export const gu: LanguageTranslation = {
|
|||||||
delete_relationship: 'કાઢી નાખો',
|
delete_relationship: 'કાઢી નાખો',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
|
||||||
title: 'કોઈ સંબંધો નથી',
|
|
||||||
description: 'ટેબલ્સ કનેક્ટ કરવા માટે એક સંબંધ બનાવો',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dependencies_section: {
|
|
||||||
dependencies: 'નિર્ભરતાઓ',
|
|
||||||
filter: 'ફિલ્ટર',
|
|
||||||
collapse: 'સિકોડો',
|
|
||||||
dependency: {
|
dependency: {
|
||||||
|
dependency: 'નિર્ભરતા',
|
||||||
table: 'ટેબલ',
|
table: 'ટેબલ',
|
||||||
dependent_table: 'આધાર રાખેલું ટેબલ',
|
dependent_table: 'નિર્ભરશીલ વ્યૂ',
|
||||||
delete_dependency: 'નિર્ભરતા કાઢી નાખો',
|
delete_dependency: 'કાઢી નાખો',
|
||||||
dependency_actions: {
|
dependency_actions: {
|
||||||
title: 'ક્રિયાઓ',
|
title: 'ક્રિયાઓ',
|
||||||
delete_dependency: 'નિર્ભરતા કાઢી નાખો',
|
delete_dependency: 'કાઢી નાખો',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: 'કોઈ નિર્ભરતાઓ નથી',
|
title: 'કોઈ સંબંધો નથી',
|
||||||
description: 'આ વિભાગમાં કોઈ નિર્ભરતા ઉપલબ્ધ નથી.',
|
description: 'શરૂ કરવા માટે એક સંબંધ બનાવો',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -256,6 +250,7 @@ export const gu: LanguageTranslation = {
|
|||||||
enum_values: 'Enum Values',
|
enum_values: 'Enum Values',
|
||||||
composite_fields: 'Fields',
|
composite_fields: 'Fields',
|
||||||
no_fields: 'No fields defined',
|
no_fields: 'No fields defined',
|
||||||
|
no_values: 'કોઈ enum મૂલ્યો વ્યાખ્યાયિત નથી',
|
||||||
field_name_placeholder: 'Field name',
|
field_name_placeholder: 'Field name',
|
||||||
field_type_placeholder: 'Select type',
|
field_type_placeholder: 'Select type',
|
||||||
add_field: 'Add Field',
|
add_field: 'Add Field',
|
||||||
@@ -278,7 +273,7 @@ export const gu: LanguageTranslation = {
|
|||||||
show_all: 'બધું બતાવો',
|
show_all: 'બધું બતાવો',
|
||||||
undo: 'અનડુ',
|
undo: 'અનડુ',
|
||||||
redo: 'રીડુ',
|
redo: 'રીડુ',
|
||||||
reorder_diagram: 'ડાયાગ્રામ ફરીથી વ્યવસ્થિત કરો',
|
reorder_diagram: 'ડાયાગ્રામ ઑટોમેટિક ગોઠવો',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||||
custom_type_highlight_tooltip:
|
custom_type_highlight_tooltip:
|
||||||
@@ -315,13 +310,13 @@ export const gu: LanguageTranslation = {
|
|||||||
cancel: 'રદ કરો',
|
cancel: 'રદ કરો',
|
||||||
back: 'પાછા',
|
back: 'પાછા',
|
||||||
import_from_file: 'ફાઇલમાંથી આયાત કરો',
|
import_from_file: 'ફાઇલમાંથી આયાત કરો',
|
||||||
empty_diagram: 'ખાલી ડાયાગ્રામ',
|
empty_diagram: 'ખાલી ડેટાબેસ',
|
||||||
continue: 'ચાલુ રાખો',
|
continue: 'ચાલુ રાખો',
|
||||||
import: 'આયાત કરો',
|
import: 'આયાત કરો',
|
||||||
},
|
},
|
||||||
|
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: 'ડાયાગ્રામ ખોલો',
|
title: 'ડેટાબેસ ખોલો',
|
||||||
description: 'નીચેની યાદીમાંથી એક ડાયાગ્રામ પસંદ કરો.',
|
description: 'નીચેની યાદીમાંથી એક ડાયાગ્રામ પસંદ કરો.',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
name: 'નામ',
|
name: 'નામ',
|
||||||
@@ -331,6 +326,12 @@ export const gu: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
cancel: 'રદ કરો',
|
cancel: 'રદ કરો',
|
||||||
open: 'ખોલો',
|
open: 'ખોલો',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: 'ખોલો',
|
||||||
|
duplicate: 'ડુપ્લિકેટ',
|
||||||
|
delete: 'કાઢી નાખો',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -480,6 +481,7 @@ export const gu: LanguageTranslation = {
|
|||||||
|
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: 'નવું ટેબલ',
|
new_table: 'નવું ટેબલ',
|
||||||
|
new_view: 'નવું વ્યૂ',
|
||||||
new_relationship: 'નવો સંબંધ',
|
new_relationship: 'નવો સંબંધ',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
new_area: 'New Area',
|
new_area: 'New Area',
|
||||||
@@ -501,6 +503,9 @@ export const gu: LanguageTranslation = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: 'ભાષા બદલો',
|
change_language: 'ભાષા બદલો',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: 'ચાલુ',
|
||||||
|
off: 'બંધ',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
|||||||
|
|
||||||
export const hi: LanguageTranslation = {
|
export const hi: LanguageTranslation = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: 'नया',
|
||||||
|
browse: 'ब्राउज़',
|
||||||
|
tables: 'टेबल',
|
||||||
|
refs: 'रेफ्स',
|
||||||
|
areas: 'क्षेत्र',
|
||||||
|
dependencies: 'निर्भरताएं',
|
||||||
|
custom_types: 'कस्टम टाइप',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: 'फ़ाइल',
|
actions: 'कार्य',
|
||||||
new: 'नया',
|
new: 'नया...',
|
||||||
open: 'खोलें',
|
browse: 'ब्राउज़ करें...',
|
||||||
save: 'सहेजें',
|
save: 'सहेजें',
|
||||||
import: 'डेटाबेस आयात करें',
|
import: 'डेटाबेस आयात करें',
|
||||||
export_sql: 'SQL निर्यात करें',
|
export_sql: 'SQL निर्यात करें',
|
||||||
export_as: 'के रूप में निर्यात करें',
|
export_as: 'के रूप में निर्यात करें',
|
||||||
delete_diagram: 'आरेख हटाएँ',
|
delete_diagram: 'हटाएँ',
|
||||||
exit: 'बाहर जाएँ',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: 'संपादित करें',
|
edit: 'संपादित करें',
|
||||||
@@ -29,6 +37,7 @@ export const hi: LanguageTranslation = {
|
|||||||
hide_field_attributes: 'फ़ील्ड विशेषताएँ छिपाएँ',
|
hide_field_attributes: 'फ़ील्ड विशेषताएँ छिपाएँ',
|
||||||
show_field_attributes: 'फ़ील्ड विशेषताएँ दिखाएँ',
|
show_field_attributes: 'फ़ील्ड विशेषताएँ दिखाएँ',
|
||||||
zoom_on_scroll: 'स्क्रॉल पर ज़ूम',
|
zoom_on_scroll: 'स्क्रॉल पर ज़ूम',
|
||||||
|
show_views: 'डेटाबेस व्यू',
|
||||||
theme: 'थीम',
|
theme: 'थीम',
|
||||||
show_dependencies: 'निर्भरता दिखाएँ',
|
show_dependencies: 'निर्भरता दिखाएँ',
|
||||||
hide_dependencies: 'निर्भरता छिपाएँ',
|
hide_dependencies: 'निर्भरता छिपाएँ',
|
||||||
@@ -65,22 +74,13 @@ export const hi: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: 'आरेख पुनः व्यवस्थित करें',
|
title: 'आरेख स्वचालित व्यवस्थित करें',
|
||||||
description:
|
description:
|
||||||
'यह क्रिया आरेख में सभी तालिकाओं को पुनः व्यवस्थित कर देगी। क्या आप जारी रखना चाहते हैं?',
|
'यह क्रिया आरेख में सभी तालिकाओं को पुनः व्यवस्थित कर देगी। क्या आप जारी रखना चाहते हैं?',
|
||||||
reorder: 'पुनः व्यवस्थित करें',
|
reorder: 'स्वचालित व्यवस्थित करें',
|
||||||
cancel: 'रद्द करें',
|
cancel: 'रद्द करें',
|
||||||
},
|
},
|
||||||
|
|
||||||
multiple_schemas_alert: {
|
|
||||||
title: 'एकाधिक स्कीमा',
|
|
||||||
description:
|
|
||||||
'{{schemasCount}} स्कीमा इस आरेख में हैं। वर्तमान में प्रदर्शित: {{formattedSchemas}}।',
|
|
||||||
// TODO: Translate
|
|
||||||
show_me: 'Show me',
|
|
||||||
none: 'कोई नहीं',
|
|
||||||
},
|
|
||||||
|
|
||||||
copy_to_clipboard_toast: {
|
copy_to_clipboard_toast: {
|
||||||
unsupported: {
|
unsupported: {
|
||||||
title: 'कॉपी असफल',
|
title: 'कॉपी असफल',
|
||||||
@@ -116,14 +116,11 @@ export const hi: LanguageTranslation = {
|
|||||||
copied: 'Copied!',
|
copied: 'Copied!',
|
||||||
|
|
||||||
side_panel: {
|
side_panel: {
|
||||||
schema: 'स्कीमा:',
|
|
||||||
filter_by_schema: 'स्कीमा द्वारा फ़िल्टर करें',
|
|
||||||
search_schema: 'स्कीमा खोजें...',
|
|
||||||
no_schemas_found: 'कोई स्कीमा नहीं मिला।',
|
|
||||||
view_all_options: 'सभी विकल्प देखें...',
|
view_all_options: 'सभी विकल्प देखें...',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: 'तालिकाएँ',
|
tables: 'तालिकाएँ',
|
||||||
add_table: 'तालिका जोड़ें',
|
add_table: 'तालिका जोड़ें',
|
||||||
|
add_view: 'व्यू जोड़ें',
|
||||||
filter: 'फ़िल्टर',
|
filter: 'फ़िल्टर',
|
||||||
collapse: 'सभी को संक्षिप्त करें',
|
collapse: 'सभी को संक्षिप्त करें',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
@@ -149,6 +146,7 @@ export const hi: LanguageTranslation = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: 'फ़ील्ड विशेषताएँ',
|
title: 'फ़ील्ड विशेषताएँ',
|
||||||
unique: 'अद्वितीय',
|
unique: 'अद्वितीय',
|
||||||
|
auto_increment: 'ऑटो इंक्रीमेंट',
|
||||||
comments: 'टिप्पणियाँ',
|
comments: 'टिप्पणियाँ',
|
||||||
no_comments: 'कोई टिप्पणी नहीं',
|
no_comments: 'कोई टिप्पणी नहीं',
|
||||||
delete_field: 'फ़ील्ड हटाएँ',
|
delete_field: 'फ़ील्ड हटाएँ',
|
||||||
@@ -164,6 +162,7 @@ export const hi: LanguageTranslation = {
|
|||||||
title: 'सूचकांक विशेषताएँ',
|
title: 'सूचकांक विशेषताएँ',
|
||||||
name: 'नाम',
|
name: 'नाम',
|
||||||
unique: 'अद्वितीय',
|
unique: 'अद्वितीय',
|
||||||
|
index_type: 'इंडेक्स प्रकार',
|
||||||
delete_index: 'सूचकांक हटाएँ',
|
delete_index: 'सूचकांक हटाएँ',
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -180,12 +179,15 @@ export const hi: LanguageTranslation = {
|
|||||||
description: 'शुरू करने के लिए एक तालिका बनाएँ',
|
description: 'शुरू करने के लिए एक तालिका बनाएँ',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: 'संबंध',
|
refs: 'रेफ्स',
|
||||||
filter: 'फ़िल्टर',
|
filter: 'फ़िल्टर',
|
||||||
add_relationship: 'संबंध जोड़ें',
|
|
||||||
collapse: 'सभी को संक्षिप्त करें',
|
collapse: 'सभी को संक्षिप्त करें',
|
||||||
|
add_relationship: 'संबंध जोड़ें',
|
||||||
|
relationships: 'संबंध',
|
||||||
|
dependencies: 'निर्भरताएँ',
|
||||||
relationship: {
|
relationship: {
|
||||||
|
relationship: 'संबंध',
|
||||||
primary: 'प्राथमिक तालिका',
|
primary: 'प्राथमिक तालिका',
|
||||||
foreign: 'संदर्भित तालिका',
|
foreign: 'संदर्भित तालिका',
|
||||||
cardinality: 'कार्डिनैलिटी',
|
cardinality: 'कार्डिनैलिटी',
|
||||||
@@ -195,28 +197,19 @@ export const hi: LanguageTranslation = {
|
|||||||
delete_relationship: 'हटाएँ',
|
delete_relationship: 'हटाएँ',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
dependency: {
|
||||||
|
dependency: 'निर्भरता',
|
||||||
|
table: 'तालिका',
|
||||||
|
dependent_table: 'आश्रित दृश्य',
|
||||||
|
delete_dependency: 'हटाएँ',
|
||||||
|
dependency_actions: {
|
||||||
|
title: 'क्रियाएँ',
|
||||||
|
delete_dependency: 'हटाएँ',
|
||||||
|
},
|
||||||
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: 'कोई संबंध नहीं',
|
title: 'कोई संबंध नहीं',
|
||||||
description:
|
description: 'शुरू करने के लिए एक संबंध बनाएँ',
|
||||||
'तालिकाओं को कनेक्ट करने के लिए एक संबंध बनाएँ',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dependencies_section: {
|
|
||||||
dependencies: 'निर्भरताएँ',
|
|
||||||
filter: 'फ़िल्टर',
|
|
||||||
collapse: 'सिकोड़ें',
|
|
||||||
dependency: {
|
|
||||||
table: 'तालिका',
|
|
||||||
dependent_table: 'आश्रित तालिका',
|
|
||||||
delete_dependency: 'निर्भरता हटाएँ',
|
|
||||||
dependency_actions: {
|
|
||||||
title: 'कार्रवाइयाँ',
|
|
||||||
delete_dependency: 'निर्भरता हटाएँ',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
empty_state: {
|
|
||||||
title: 'कोई निर्भरता नहीं',
|
|
||||||
description: 'इस अनुभाग में कोई निर्भरता उपलब्ध नहीं है।',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -256,6 +249,7 @@ export const hi: LanguageTranslation = {
|
|||||||
enum_values: 'Enum Values',
|
enum_values: 'Enum Values',
|
||||||
composite_fields: 'Fields',
|
composite_fields: 'Fields',
|
||||||
no_fields: 'No fields defined',
|
no_fields: 'No fields defined',
|
||||||
|
no_values: 'कोई enum मान परिभाषित नहीं',
|
||||||
field_name_placeholder: 'Field name',
|
field_name_placeholder: 'Field name',
|
||||||
field_type_placeholder: 'Select type',
|
field_type_placeholder: 'Select type',
|
||||||
add_field: 'Add Field',
|
add_field: 'Add Field',
|
||||||
@@ -278,7 +272,7 @@ export const hi: LanguageTranslation = {
|
|||||||
show_all: 'सभी दिखाएँ',
|
show_all: 'सभी दिखाएँ',
|
||||||
undo: 'पूर्ववत करें',
|
undo: 'पूर्ववत करें',
|
||||||
redo: 'पुनः करें',
|
redo: 'पुनः करें',
|
||||||
reorder_diagram: 'आरेख पुनः व्यवस्थित करें',
|
reorder_diagram: 'आरेख स्वचालित व्यवस्थित करें',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||||
custom_type_highlight_tooltip:
|
custom_type_highlight_tooltip:
|
||||||
@@ -318,13 +312,13 @@ export const hi: LanguageTranslation = {
|
|||||||
back: 'वापस',
|
back: 'वापस',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
import_from_file: 'Import from File',
|
import_from_file: 'Import from File',
|
||||||
empty_diagram: 'खाली आरेख',
|
empty_diagram: 'खाली डेटाबेस',
|
||||||
continue: 'जारी रखें',
|
continue: 'जारी रखें',
|
||||||
import: 'आयात करें',
|
import: 'आयात करें',
|
||||||
},
|
},
|
||||||
|
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: 'आरेख खोलें',
|
title: 'डेटाबेस खोलें',
|
||||||
description: 'नीचे दी गई सूची से एक आरेख चुनें।',
|
description: 'नीचे दी गई सूची से एक आरेख चुनें।',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
name: 'नाम',
|
name: 'नाम',
|
||||||
@@ -334,6 +328,12 @@ export const hi: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
cancel: 'रद्द करें',
|
cancel: 'रद्द करें',
|
||||||
open: 'खोलें',
|
open: 'खोलें',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: 'खोलें',
|
||||||
|
duplicate: 'डुप्लिकेट',
|
||||||
|
delete: 'हटाएं',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -483,6 +483,7 @@ export const hi: LanguageTranslation = {
|
|||||||
|
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: 'नई तालिका',
|
new_table: 'नई तालिका',
|
||||||
|
new_view: 'नया व्यू',
|
||||||
new_relationship: 'नया संबंध',
|
new_relationship: 'नया संबंध',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
new_area: 'New Area',
|
new_area: 'New Area',
|
||||||
@@ -505,6 +506,9 @@ export const hi: LanguageTranslation = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: 'भाषा बदलें',
|
change_language: 'भाषा बदलें',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: 'चालू',
|
||||||
|
off: 'बंद',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
|||||||
|
|
||||||
export const hr: LanguageTranslation = {
|
export const hr: LanguageTranslation = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: 'Novi',
|
||||||
|
browse: 'Pregledaj',
|
||||||
|
tables: 'Tablice',
|
||||||
|
refs: 'Refs',
|
||||||
|
areas: 'Područja',
|
||||||
|
dependencies: 'Ovisnosti',
|
||||||
|
custom_types: 'Prilagođeni Tipovi',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: 'Datoteka',
|
actions: 'Akcije',
|
||||||
new: 'Nova',
|
new: 'Novi...',
|
||||||
open: 'Otvori',
|
browse: 'Pregledaj...',
|
||||||
save: 'Spremi',
|
save: 'Spremi',
|
||||||
import: 'Uvezi',
|
import: 'Uvezi',
|
||||||
export_sql: 'Izvezi SQL',
|
export_sql: 'Izvezi SQL',
|
||||||
export_as: 'Izvezi kao',
|
export_as: 'Izvezi kao',
|
||||||
delete_diagram: 'Izbriši dijagram',
|
delete_diagram: 'Izbriši',
|
||||||
exit: 'Izađi',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: 'Uredi',
|
edit: 'Uredi',
|
||||||
@@ -29,6 +37,7 @@ export const hr: LanguageTranslation = {
|
|||||||
hide_field_attributes: 'Sakrij atribute polja',
|
hide_field_attributes: 'Sakrij atribute polja',
|
||||||
show_field_attributes: 'Prikaži atribute polja',
|
show_field_attributes: 'Prikaži atribute polja',
|
||||||
zoom_on_scroll: 'Zumiranje pri skrolanju',
|
zoom_on_scroll: 'Zumiranje pri skrolanju',
|
||||||
|
show_views: 'Pogledi Baze Podataka',
|
||||||
theme: 'Tema',
|
theme: 'Tema',
|
||||||
show_dependencies: 'Prikaži ovisnosti',
|
show_dependencies: 'Prikaži ovisnosti',
|
||||||
hide_dependencies: 'Sakrij ovisnosti',
|
hide_dependencies: 'Sakrij ovisnosti',
|
||||||
@@ -64,21 +73,13 @@ export const hr: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: 'Preuredi dijagram',
|
title: 'Automatski preuredi dijagram',
|
||||||
description:
|
description:
|
||||||
'Ova radnja će preurediti sve tablice u dijagramu. Želite li nastaviti?',
|
'Ova radnja će preurediti sve tablice u dijagramu. Želite li nastaviti?',
|
||||||
reorder: 'Preuredi',
|
reorder: 'Automatski preuredi',
|
||||||
cancel: 'Odustani',
|
cancel: 'Odustani',
|
||||||
},
|
},
|
||||||
|
|
||||||
multiple_schemas_alert: {
|
|
||||||
title: 'Više shema',
|
|
||||||
description:
|
|
||||||
'{{schemasCount}} shema u ovom dijagramu. Trenutno prikazano: {{formattedSchemas}}.',
|
|
||||||
show_me: 'Prikaži mi',
|
|
||||||
none: 'nijedna',
|
|
||||||
},
|
|
||||||
|
|
||||||
copy_to_clipboard_toast: {
|
copy_to_clipboard_toast: {
|
||||||
unsupported: {
|
unsupported: {
|
||||||
title: 'Kopiranje neuspješno',
|
title: 'Kopiranje neuspješno',
|
||||||
@@ -113,14 +114,11 @@ export const hr: LanguageTranslation = {
|
|||||||
copied: 'Kopirano!',
|
copied: 'Kopirano!',
|
||||||
|
|
||||||
side_panel: {
|
side_panel: {
|
||||||
schema: 'Shema:',
|
|
||||||
filter_by_schema: 'Filtriraj po shemi',
|
|
||||||
search_schema: 'Pretraži shemu...',
|
|
||||||
no_schemas_found: 'Nema pronađenih shema.',
|
|
||||||
view_all_options: 'Prikaži sve opcije...',
|
view_all_options: 'Prikaži sve opcije...',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: 'Tablice',
|
tables: 'Tablice',
|
||||||
add_table: 'Dodaj tablicu',
|
add_table: 'Dodaj tablicu',
|
||||||
|
add_view: 'Dodaj Pogled',
|
||||||
filter: 'Filtriraj',
|
filter: 'Filtriraj',
|
||||||
collapse: 'Sažmi sve',
|
collapse: 'Sažmi sve',
|
||||||
clear: 'Očisti filter',
|
clear: 'Očisti filter',
|
||||||
@@ -145,6 +143,7 @@ export const hr: LanguageTranslation = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: 'Atributi polja',
|
title: 'Atributi polja',
|
||||||
unique: 'Jedinstven',
|
unique: 'Jedinstven',
|
||||||
|
auto_increment: 'Automatsko povećavanje',
|
||||||
character_length: 'Maksimalna dužina',
|
character_length: 'Maksimalna dužina',
|
||||||
precision: 'Preciznost',
|
precision: 'Preciznost',
|
||||||
scale: 'Skala',
|
scale: 'Skala',
|
||||||
@@ -158,6 +157,7 @@ export const hr: LanguageTranslation = {
|
|||||||
title: 'Atributi indeksa',
|
title: 'Atributi indeksa',
|
||||||
name: 'Naziv',
|
name: 'Naziv',
|
||||||
unique: 'Jedinstven',
|
unique: 'Jedinstven',
|
||||||
|
index_type: 'Vrsta indeksa',
|
||||||
delete_index: 'Izbriši indeks',
|
delete_index: 'Izbriši indeks',
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -174,12 +174,15 @@ export const hr: LanguageTranslation = {
|
|||||||
description: 'Stvorite tablicu za početak',
|
description: 'Stvorite tablicu za početak',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: 'Veze',
|
refs: 'Refs',
|
||||||
filter: 'Filtriraj',
|
filter: 'Filtriraj',
|
||||||
add_relationship: 'Dodaj vezu',
|
|
||||||
collapse: 'Sažmi sve',
|
collapse: 'Sažmi sve',
|
||||||
|
add_relationship: 'Dodaj vezu',
|
||||||
|
relationships: 'Veze',
|
||||||
|
dependencies: 'Ovisnosti',
|
||||||
relationship: {
|
relationship: {
|
||||||
|
relationship: 'Veza',
|
||||||
primary: 'Primarna tablica',
|
primary: 'Primarna tablica',
|
||||||
foreign: 'Referentna tablica',
|
foreign: 'Referentna tablica',
|
||||||
cardinality: 'Kardinalnost',
|
cardinality: 'Kardinalnost',
|
||||||
@@ -189,16 +192,8 @@ export const hr: LanguageTranslation = {
|
|||||||
delete_relationship: 'Izbriši',
|
delete_relationship: 'Izbriši',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
|
||||||
title: 'Nema veza',
|
|
||||||
description: 'Stvorite vezu za povezivanje tablica',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dependencies_section: {
|
|
||||||
dependencies: 'Ovisnosti',
|
|
||||||
filter: 'Filtriraj',
|
|
||||||
collapse: 'Sažmi sve',
|
|
||||||
dependency: {
|
dependency: {
|
||||||
|
dependency: 'Ovisnost',
|
||||||
table: 'Tablica',
|
table: 'Tablica',
|
||||||
dependent_table: 'Ovisni pogled',
|
dependent_table: 'Ovisni pogled',
|
||||||
delete_dependency: 'Izbriši',
|
delete_dependency: 'Izbriši',
|
||||||
@@ -208,8 +203,8 @@ export const hr: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: 'Nema ovisnosti',
|
title: 'Nema veze',
|
||||||
description: 'Stvorite pogled za početak',
|
description: 'Stvorite vezu za početak',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -250,6 +245,7 @@ export const hr: LanguageTranslation = {
|
|||||||
enum_values: 'Enum vrijednosti',
|
enum_values: 'Enum vrijednosti',
|
||||||
composite_fields: 'Polja',
|
composite_fields: 'Polja',
|
||||||
no_fields: 'Nema definiranih polja',
|
no_fields: 'Nema definiranih polja',
|
||||||
|
no_values: 'Nema definiranih enum vrijednosti',
|
||||||
field_name_placeholder: 'Naziv polja',
|
field_name_placeholder: 'Naziv polja',
|
||||||
field_type_placeholder: 'Odaberi tip',
|
field_type_placeholder: 'Odaberi tip',
|
||||||
add_field: 'Dodaj polje',
|
add_field: 'Dodaj polje',
|
||||||
@@ -273,7 +269,7 @@ export const hr: LanguageTranslation = {
|
|||||||
show_all: 'Prikaži sve',
|
show_all: 'Prikaži sve',
|
||||||
undo: 'Poništi',
|
undo: 'Poništi',
|
||||||
redo: 'Ponovi',
|
redo: 'Ponovi',
|
||||||
reorder_diagram: 'Preuredi dijagram',
|
reorder_diagram: 'Automatski preuredi dijagram',
|
||||||
highlight_overlapping_tables: 'Istakni preklapajuće tablice',
|
highlight_overlapping_tables: 'Istakni preklapajuće tablice',
|
||||||
clear_custom_type_highlight: 'Ukloni isticanje za "{{typeName}}"',
|
clear_custom_type_highlight: 'Ukloni isticanje za "{{typeName}}"',
|
||||||
custom_type_highlight_tooltip:
|
custom_type_highlight_tooltip:
|
||||||
@@ -309,13 +305,13 @@ export const hr: LanguageTranslation = {
|
|||||||
cancel: 'Odustani',
|
cancel: 'Odustani',
|
||||||
import_from_file: 'Uvezi iz datoteke',
|
import_from_file: 'Uvezi iz datoteke',
|
||||||
back: 'Natrag',
|
back: 'Natrag',
|
||||||
empty_diagram: 'Prazan dijagram',
|
empty_diagram: 'Prazna baza podataka',
|
||||||
continue: 'Nastavi',
|
continue: 'Nastavi',
|
||||||
import: 'Uvezi',
|
import: 'Uvezi',
|
||||||
},
|
},
|
||||||
|
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: 'Otvori dijagram',
|
title: 'Otvori bazu podataka',
|
||||||
description: 'Odaberite dijagram za otvaranje iz popisa ispod.',
|
description: 'Odaberite dijagram za otvaranje iz popisa ispod.',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
name: 'Naziv',
|
name: 'Naziv',
|
||||||
@@ -325,6 +321,12 @@ export const hr: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
cancel: 'Odustani',
|
cancel: 'Odustani',
|
||||||
open: 'Otvori',
|
open: 'Otvori',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: 'Otvori',
|
||||||
|
duplicate: 'Dupliciraj',
|
||||||
|
delete: 'Obriši',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -473,6 +475,7 @@ export const hr: LanguageTranslation = {
|
|||||||
|
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: 'Nova tablica',
|
new_table: 'Nova tablica',
|
||||||
|
new_view: 'Novi Pogled',
|
||||||
new_relationship: 'Nova veza',
|
new_relationship: 'Nova veza',
|
||||||
new_area: 'Novo područje',
|
new_area: 'Novo područje',
|
||||||
},
|
},
|
||||||
@@ -493,6 +496,9 @@ export const hr: LanguageTranslation = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: 'Jezik',
|
change_language: 'Jezik',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: 'Uključeno',
|
||||||
|
off: 'Isključeno',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
|||||||
|
|
||||||
export const id_ID: LanguageTranslation = {
|
export const id_ID: LanguageTranslation = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: 'Baru',
|
||||||
|
browse: 'Jelajahi',
|
||||||
|
tables: 'Tabel',
|
||||||
|
refs: 'Refs',
|
||||||
|
areas: 'Area',
|
||||||
|
dependencies: 'Ketergantungan',
|
||||||
|
custom_types: 'Tipe Kustom',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: 'Berkas',
|
actions: 'Aksi',
|
||||||
new: 'Buat Baru',
|
new: 'Baru...',
|
||||||
open: 'Buka',
|
browse: 'Jelajahi...',
|
||||||
save: 'Simpan',
|
save: 'Simpan',
|
||||||
import: 'Impor Database',
|
import: 'Impor Database',
|
||||||
export_sql: 'Ekspor SQL',
|
export_sql: 'Ekspor SQL',
|
||||||
export_as: 'Ekspor Sebagai',
|
export_as: 'Ekspor Sebagai',
|
||||||
delete_diagram: 'Hapus Diagram',
|
delete_diagram: 'Hapus',
|
||||||
exit: 'Keluar',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: 'Ubah',
|
edit: 'Ubah',
|
||||||
@@ -29,6 +37,7 @@ export const id_ID: LanguageTranslation = {
|
|||||||
hide_field_attributes: 'Sembunyikan Atribut Kolom',
|
hide_field_attributes: 'Sembunyikan Atribut Kolom',
|
||||||
show_field_attributes: 'Tampilkan Atribut Kolom',
|
show_field_attributes: 'Tampilkan Atribut Kolom',
|
||||||
zoom_on_scroll: 'Perbesar saat Scroll',
|
zoom_on_scroll: 'Perbesar saat Scroll',
|
||||||
|
show_views: 'Tampilan Database',
|
||||||
theme: 'Tema',
|
theme: 'Tema',
|
||||||
show_dependencies: 'Tampilkan Dependensi',
|
show_dependencies: 'Tampilkan Dependensi',
|
||||||
hide_dependencies: 'Sembunyikan Dependensi',
|
hide_dependencies: 'Sembunyikan Dependensi',
|
||||||
@@ -65,22 +74,13 @@ export const id_ID: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: 'Atur Ulang Diagram',
|
title: 'Atur Otomatis Diagram',
|
||||||
description:
|
description:
|
||||||
'Tindakan ini akan mengatur ulang semua tabel di diagram. Apakah Anda ingin melanjutkan?',
|
'Tindakan ini akan mengatur ulang semua tabel di diagram. Apakah Anda ingin melanjutkan?',
|
||||||
reorder: 'Atur Ulang',
|
reorder: 'Atur Otomatis',
|
||||||
cancel: 'Batal',
|
cancel: 'Batal',
|
||||||
},
|
},
|
||||||
|
|
||||||
multiple_schemas_alert: {
|
|
||||||
title: 'Schema Lebih dari satu',
|
|
||||||
description:
|
|
||||||
'{{schemasCount}} schema di diagram ini. Sedang ditampilkan: {{formattedSchemas}}.',
|
|
||||||
// TODO: Translate
|
|
||||||
show_me: 'Show me',
|
|
||||||
none: 'Tidak ada',
|
|
||||||
},
|
|
||||||
|
|
||||||
copy_to_clipboard_toast: {
|
copy_to_clipboard_toast: {
|
||||||
unsupported: {
|
unsupported: {
|
||||||
title: 'Gagal menyalin',
|
title: 'Gagal menyalin',
|
||||||
@@ -115,14 +115,11 @@ export const id_ID: LanguageTranslation = {
|
|||||||
copied: 'Tersalin!',
|
copied: 'Tersalin!',
|
||||||
|
|
||||||
side_panel: {
|
side_panel: {
|
||||||
schema: 'Skema:',
|
|
||||||
filter_by_schema: 'Saring berdasarkan skema',
|
|
||||||
search_schema: 'Cari skema...',
|
|
||||||
no_schemas_found: 'Tidak ada skema yang ditemukan.',
|
|
||||||
view_all_options: 'Tampilkan Semua Pilihan...',
|
view_all_options: 'Tampilkan Semua Pilihan...',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: 'Tabel',
|
tables: 'Tabel',
|
||||||
add_table: 'Tambah Tabel',
|
add_table: 'Tambah Tabel',
|
||||||
|
add_view: 'Tambah Tampilan',
|
||||||
filter: 'Saring',
|
filter: 'Saring',
|
||||||
collapse: 'Lipat Semua',
|
collapse: 'Lipat Semua',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
@@ -148,6 +145,7 @@ export const id_ID: LanguageTranslation = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: 'Atribut Kolom',
|
title: 'Atribut Kolom',
|
||||||
unique: 'Unik',
|
unique: 'Unik',
|
||||||
|
auto_increment: 'Kenaikan Otomatis',
|
||||||
comments: 'Komentar',
|
comments: 'Komentar',
|
||||||
no_comments: 'Tidak ada komentar',
|
no_comments: 'Tidak ada komentar',
|
||||||
delete_field: 'Hapus Kolom',
|
delete_field: 'Hapus Kolom',
|
||||||
@@ -163,6 +161,7 @@ export const id_ID: LanguageTranslation = {
|
|||||||
title: 'Atribut Indeks',
|
title: 'Atribut Indeks',
|
||||||
name: 'Nama',
|
name: 'Nama',
|
||||||
unique: 'Unik',
|
unique: 'Unik',
|
||||||
|
index_type: 'Tipe Indeks',
|
||||||
delete_index: 'Hapus Indeks',
|
delete_index: 'Hapus Indeks',
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -179,12 +178,15 @@ export const id_ID: LanguageTranslation = {
|
|||||||
description: 'Buat tabel untuk memulai',
|
description: 'Buat tabel untuk memulai',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: 'Hubungan',
|
refs: 'Refs',
|
||||||
filter: 'Saring',
|
filter: 'Saring',
|
||||||
add_relationship: 'Tambah Hubungan',
|
|
||||||
collapse: 'Lipat Semua',
|
collapse: 'Lipat Semua',
|
||||||
|
add_relationship: 'Tambah Hubungan',
|
||||||
|
relationships: 'Hubungan',
|
||||||
|
dependencies: 'Dependensi',
|
||||||
relationship: {
|
relationship: {
|
||||||
|
relationship: 'Hubungan',
|
||||||
primary: 'Tabel Primer',
|
primary: 'Tabel Primer',
|
||||||
foreign: 'Tabel Referensi',
|
foreign: 'Tabel Referensi',
|
||||||
cardinality: 'Kardinalitas',
|
cardinality: 'Kardinalitas',
|
||||||
@@ -194,16 +196,8 @@ export const id_ID: LanguageTranslation = {
|
|||||||
delete_relationship: 'Hapus',
|
delete_relationship: 'Hapus',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
|
||||||
title: 'Tidak ada hubungan',
|
|
||||||
description: 'Buat hubungan untuk menghubungkan tabel',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dependencies_section: {
|
|
||||||
dependencies: 'Dependensi',
|
|
||||||
filter: 'Saring',
|
|
||||||
collapse: 'Lipat Semua',
|
|
||||||
dependency: {
|
dependency: {
|
||||||
|
dependency: 'Dependensi',
|
||||||
table: 'Tabel',
|
table: 'Tabel',
|
||||||
dependent_table: 'Tampilan Dependen',
|
dependent_table: 'Tampilan Dependen',
|
||||||
delete_dependency: 'Hapus',
|
delete_dependency: 'Hapus',
|
||||||
@@ -213,8 +207,8 @@ export const id_ID: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: 'Tidak ada dependensi',
|
title: 'Tidak ada hubungan',
|
||||||
description: 'Buat tampilan untuk memulai',
|
description: 'Buat hubungan untuk memulai',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -254,6 +248,7 @@ export const id_ID: LanguageTranslation = {
|
|||||||
enum_values: 'Enum Values',
|
enum_values: 'Enum Values',
|
||||||
composite_fields: 'Fields',
|
composite_fields: 'Fields',
|
||||||
no_fields: 'No fields defined',
|
no_fields: 'No fields defined',
|
||||||
|
no_values: 'Tidak ada nilai enum yang ditentukan',
|
||||||
field_name_placeholder: 'Field name',
|
field_name_placeholder: 'Field name',
|
||||||
field_type_placeholder: 'Select type',
|
field_type_placeholder: 'Select type',
|
||||||
add_field: 'Add Field',
|
add_field: 'Add Field',
|
||||||
@@ -276,7 +271,7 @@ export const id_ID: LanguageTranslation = {
|
|||||||
show_all: 'Tampilkan Semua',
|
show_all: 'Tampilkan Semua',
|
||||||
undo: 'Undo',
|
undo: 'Undo',
|
||||||
redo: 'Redo',
|
redo: 'Redo',
|
||||||
reorder_diagram: 'Atur Ulang Diagram',
|
reorder_diagram: 'Atur Otomatis Diagram',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||||
custom_type_highlight_tooltip:
|
custom_type_highlight_tooltip:
|
||||||
@@ -314,13 +309,13 @@ export const id_ID: LanguageTranslation = {
|
|||||||
cancel: 'Batal',
|
cancel: 'Batal',
|
||||||
import_from_file: 'Impor dari file',
|
import_from_file: 'Impor dari file',
|
||||||
back: 'Kembali',
|
back: 'Kembali',
|
||||||
empty_diagram: 'Diagram Kosong',
|
empty_diagram: 'Database Kosong',
|
||||||
continue: 'Lanjutkan',
|
continue: 'Lanjutkan',
|
||||||
import: 'Impor',
|
import: 'Impor',
|
||||||
},
|
},
|
||||||
|
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: 'Buka Diagram',
|
title: 'Buka Database',
|
||||||
description: 'Pilih diagram untuk dibuka dari daftar di bawah.',
|
description: 'Pilih diagram untuk dibuka dari daftar di bawah.',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
name: 'Name',
|
name: 'Name',
|
||||||
@@ -330,6 +325,12 @@ export const id_ID: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
cancel: 'Batal',
|
cancel: 'Batal',
|
||||||
open: 'Buka',
|
open: 'Buka',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: 'Buka',
|
||||||
|
duplicate: 'Duplikat',
|
||||||
|
delete: 'Hapus',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -479,6 +480,7 @@ export const id_ID: LanguageTranslation = {
|
|||||||
|
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: 'Tabel Baru',
|
new_table: 'Tabel Baru',
|
||||||
|
new_view: 'Tampilan Baru',
|
||||||
new_relationship: 'Hubungan Baru',
|
new_relationship: 'Hubungan Baru',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
new_area: 'New Area',
|
new_area: 'New Area',
|
||||||
@@ -500,6 +502,9 @@ export const id_ID: LanguageTranslation = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: 'Bahasa',
|
change_language: 'Bahasa',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: 'Aktif',
|
||||||
|
off: 'Nonaktif',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
|||||||
|
|
||||||
export const ja: LanguageTranslation = {
|
export const ja: LanguageTranslation = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: '新規',
|
||||||
|
browse: '参照',
|
||||||
|
tables: 'テーブル',
|
||||||
|
refs: '参照',
|
||||||
|
areas: 'エリア',
|
||||||
|
dependencies: '依存関係',
|
||||||
|
custom_types: 'カスタムタイプ',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: 'ファイル',
|
actions: 'アクション',
|
||||||
new: '新規',
|
new: '新規...',
|
||||||
open: '開く',
|
browse: '参照...',
|
||||||
save: '保存',
|
save: '保存',
|
||||||
import: 'データベースをインポート',
|
import: 'データベースをインポート',
|
||||||
export_sql: 'SQLをエクスポート',
|
export_sql: 'SQLをエクスポート',
|
||||||
export_as: '形式を指定してエクスポート',
|
export_as: '形式を指定してエクスポート',
|
||||||
delete_diagram: 'ダイアグラムを削除',
|
delete_diagram: '削除',
|
||||||
exit: '終了',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: '編集',
|
edit: '編集',
|
||||||
@@ -29,6 +37,7 @@ export const ja: LanguageTranslation = {
|
|||||||
hide_field_attributes: 'フィールド属性を非表示',
|
hide_field_attributes: 'フィールド属性を非表示',
|
||||||
show_field_attributes: 'フィールド属性を表示',
|
show_field_attributes: 'フィールド属性を表示',
|
||||||
zoom_on_scroll: 'スクロールでズーム',
|
zoom_on_scroll: 'スクロールでズーム',
|
||||||
|
show_views: 'データベースビュー',
|
||||||
theme: 'テーマ',
|
theme: 'テーマ',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
show_dependencies: 'Show Dependencies',
|
show_dependencies: 'Show Dependencies',
|
||||||
@@ -67,22 +76,13 @@ export const ja: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: 'ダイアグラムを並べ替え',
|
title: 'ダイアグラムを自動配置',
|
||||||
description:
|
description:
|
||||||
'この操作によりダイアグラム内のすべてのテーブルが再配置されます。続行しますか?',
|
'この操作によりダイアグラム内のすべてのテーブルが再配置されます。続行しますか?',
|
||||||
reorder: '並べ替え',
|
reorder: '自動配置',
|
||||||
cancel: 'キャンセル',
|
cancel: 'キャンセル',
|
||||||
},
|
},
|
||||||
|
|
||||||
multiple_schemas_alert: {
|
|
||||||
title: '複数のスキーマ',
|
|
||||||
description:
|
|
||||||
'このダイアグラムには{{schemasCount}}個のスキーマがあります。現在表示中: {{formattedSchemas}}。',
|
|
||||||
// TODO: Translate
|
|
||||||
show_me: 'Show me',
|
|
||||||
none: 'なし',
|
|
||||||
},
|
|
||||||
|
|
||||||
copy_to_clipboard_toast: {
|
copy_to_clipboard_toast: {
|
||||||
unsupported: {
|
unsupported: {
|
||||||
title: 'コピー失敗',
|
title: 'コピー失敗',
|
||||||
@@ -119,14 +119,11 @@ export const ja: LanguageTranslation = {
|
|||||||
copied: 'Copied!',
|
copied: 'Copied!',
|
||||||
|
|
||||||
side_panel: {
|
side_panel: {
|
||||||
schema: 'スキーマ:',
|
|
||||||
filter_by_schema: 'スキーマでフィルタ',
|
|
||||||
search_schema: 'スキーマを検索...',
|
|
||||||
no_schemas_found: 'スキーマが見つかりません。',
|
|
||||||
view_all_options: 'すべてのオプションを表示...',
|
view_all_options: 'すべてのオプションを表示...',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: 'テーブル',
|
tables: 'テーブル',
|
||||||
add_table: 'テーブルを追加',
|
add_table: 'テーブルを追加',
|
||||||
|
add_view: 'ビューを追加',
|
||||||
filter: 'フィルタ',
|
filter: 'フィルタ',
|
||||||
collapse: 'すべて折りたたむ',
|
collapse: 'すべて折りたたむ',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
@@ -152,6 +149,7 @@ export const ja: LanguageTranslation = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: 'フィールド属性',
|
title: 'フィールド属性',
|
||||||
unique: 'ユニーク',
|
unique: 'ユニーク',
|
||||||
|
auto_increment: 'オートインクリメント',
|
||||||
comments: 'コメント',
|
comments: 'コメント',
|
||||||
no_comments: 'コメントがありません',
|
no_comments: 'コメントがありません',
|
||||||
delete_field: 'フィールドを削除',
|
delete_field: 'フィールドを削除',
|
||||||
@@ -167,6 +165,7 @@ export const ja: LanguageTranslation = {
|
|||||||
title: 'インデックス属性',
|
title: 'インデックス属性',
|
||||||
name: '名前',
|
name: '名前',
|
||||||
unique: 'ユニーク',
|
unique: 'ユニーク',
|
||||||
|
index_type: 'インデックスタイプ',
|
||||||
delete_index: 'インデックスを削除',
|
delete_index: 'インデックスを削除',
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -183,12 +182,15 @@ export const ja: LanguageTranslation = {
|
|||||||
description: 'テーブルを作成して開始してください',
|
description: 'テーブルを作成して開始してください',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: 'リレーションシップ',
|
refs: '参照',
|
||||||
filter: 'フィルタ',
|
filter: 'フィルタ',
|
||||||
add_relationship: 'リレーションシップを追加',
|
|
||||||
collapse: 'すべて折りたたむ',
|
collapse: 'すべて折りたたむ',
|
||||||
|
add_relationship: 'リレーションシップを追加',
|
||||||
|
relationships: 'リレーションシップ',
|
||||||
|
dependencies: '依存関係',
|
||||||
relationship: {
|
relationship: {
|
||||||
|
relationship: 'リレーションシップ',
|
||||||
primary: '主テーブル',
|
primary: '主テーブル',
|
||||||
foreign: '参照テーブル',
|
foreign: '参照テーブル',
|
||||||
cardinality: 'カーディナリティ',
|
cardinality: 'カーディナリティ',
|
||||||
@@ -198,29 +200,20 @@ export const ja: LanguageTranslation = {
|
|||||||
delete_relationship: '削除',
|
delete_relationship: '削除',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
dependency: {
|
||||||
|
dependency: '依存関係',
|
||||||
|
table: 'テーブル',
|
||||||
|
dependent_table: '依存ビュー',
|
||||||
|
delete_dependency: '削除',
|
||||||
|
dependency_actions: {
|
||||||
|
title: '操作',
|
||||||
|
delete_dependency: '削除',
|
||||||
|
},
|
||||||
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: 'リレーションシップがありません',
|
title: 'リレーションシップがありません',
|
||||||
description:
|
description:
|
||||||
'テーブルを接続するためにリレーションシップを作成してください',
|
'開始するためにリレーションシップを作成してください',
|
||||||
},
|
|
||||||
},
|
|
||||||
// TODO: Translate
|
|
||||||
dependencies_section: {
|
|
||||||
dependencies: 'Dependencies',
|
|
||||||
filter: 'Filter',
|
|
||||||
collapse: 'Collapse All',
|
|
||||||
dependency: {
|
|
||||||
table: 'Table',
|
|
||||||
dependent_table: 'Dependent View',
|
|
||||||
delete_dependency: 'Delete',
|
|
||||||
dependency_actions: {
|
|
||||||
title: 'Actions',
|
|
||||||
delete_dependency: 'Delete',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
empty_state: {
|
|
||||||
title: 'No dependencies',
|
|
||||||
description: 'Create a view to get started',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -260,6 +253,7 @@ export const ja: LanguageTranslation = {
|
|||||||
enum_values: 'Enum Values',
|
enum_values: 'Enum Values',
|
||||||
composite_fields: 'Fields',
|
composite_fields: 'Fields',
|
||||||
no_fields: 'No fields defined',
|
no_fields: 'No fields defined',
|
||||||
|
no_values: '列挙値が定義されていません',
|
||||||
field_name_placeholder: 'Field name',
|
field_name_placeholder: 'Field name',
|
||||||
field_type_placeholder: 'Select type',
|
field_type_placeholder: 'Select type',
|
||||||
add_field: 'Add Field',
|
add_field: 'Add Field',
|
||||||
@@ -282,7 +276,7 @@ export const ja: LanguageTranslation = {
|
|||||||
show_all: 'すべて表示',
|
show_all: 'すべて表示',
|
||||||
undo: '元に戻す',
|
undo: '元に戻す',
|
||||||
redo: 'やり直し',
|
redo: 'やり直し',
|
||||||
reorder_diagram: 'ダイアグラムを並べ替え',
|
reorder_diagram: 'ダイアグラムを自動配置',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
highlight_overlapping_tables: 'Highlight Overlapping Tables',
|
highlight_overlapping_tables: 'Highlight Overlapping Tables',
|
||||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||||
@@ -320,13 +314,13 @@ export const ja: LanguageTranslation = {
|
|||||||
back: '戻る',
|
back: '戻る',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
import_from_file: 'Import from File',
|
import_from_file: 'Import from File',
|
||||||
empty_diagram: '空のダイアグラム',
|
empty_diagram: '空のデータベース',
|
||||||
continue: '続行',
|
continue: '続行',
|
||||||
import: 'インポート',
|
import: 'インポート',
|
||||||
},
|
},
|
||||||
|
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: 'ダイアグラムを開く',
|
title: 'データベースを開く',
|
||||||
description: '以下のリストからダイアグラムを選択してください。',
|
description: '以下のリストからダイアグラムを選択してください。',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
name: '名前',
|
name: '名前',
|
||||||
@@ -336,6 +330,12 @@ export const ja: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
cancel: 'キャンセル',
|
cancel: 'キャンセル',
|
||||||
open: '開く',
|
open: '開く',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: '開く',
|
||||||
|
duplicate: '複製',
|
||||||
|
delete: '削除',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -485,6 +485,7 @@ export const ja: LanguageTranslation = {
|
|||||||
|
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: '新しいテーブル',
|
new_table: '新しいテーブル',
|
||||||
|
new_view: '新しいビュー',
|
||||||
new_relationship: '新しいリレーションシップ',
|
new_relationship: '新しいリレーションシップ',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
new_area: 'New Area',
|
new_area: 'New Area',
|
||||||
@@ -507,6 +508,9 @@ export const ja: LanguageTranslation = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: '言語',
|
change_language: '言語',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: 'オン',
|
||||||
|
off: 'オフ',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
|||||||
|
|
||||||
export const ko_KR: LanguageTranslation = {
|
export const ko_KR: LanguageTranslation = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: '새로 만들기',
|
||||||
|
browse: '찾아보기',
|
||||||
|
tables: '테이블',
|
||||||
|
refs: 'Refs',
|
||||||
|
areas: '영역',
|
||||||
|
dependencies: '종속성',
|
||||||
|
custom_types: '사용자 지정 타입',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: '파일',
|
actions: '작업',
|
||||||
new: '새 다이어그램',
|
new: '새로 만들기...',
|
||||||
open: '열기',
|
browse: '찾아보기...',
|
||||||
save: '저장',
|
save: '저장',
|
||||||
import: '데이터베이스 가져오기',
|
import: '데이터베이스 가져오기',
|
||||||
export_sql: 'SQL로 저장',
|
export_sql: 'SQL로 저장',
|
||||||
export_as: '다른 형식으로 저장',
|
export_as: '다른 형식으로 저장',
|
||||||
delete_diagram: '다이어그램 삭제',
|
delete_diagram: '삭제',
|
||||||
exit: '종료',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: '편집',
|
edit: '편집',
|
||||||
@@ -29,6 +37,7 @@ export const ko_KR: LanguageTranslation = {
|
|||||||
hide_field_attributes: '필드 속성 숨기기',
|
hide_field_attributes: '필드 속성 숨기기',
|
||||||
show_field_attributes: '필드 속성 보이기',
|
show_field_attributes: '필드 속성 보이기',
|
||||||
zoom_on_scroll: '스크롤 시 확대',
|
zoom_on_scroll: '스크롤 시 확대',
|
||||||
|
show_views: '데이터베이스 뷰',
|
||||||
theme: '테마',
|
theme: '테마',
|
||||||
show_dependencies: '종속성 보이기',
|
show_dependencies: '종속성 보이기',
|
||||||
hide_dependencies: '종속성 숨기기',
|
hide_dependencies: '종속성 숨기기',
|
||||||
@@ -65,22 +74,13 @@ export const ko_KR: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: '다이어그램 재정렬',
|
title: '다이어그램 자동 정렬',
|
||||||
description:
|
description:
|
||||||
'이 작업은 모든 다이어그램이 재정렬됩니다. 계속하시겠습니까?',
|
'이 작업은 모든 다이어그램이 재정렬됩니다. 계속하시겠습니까?',
|
||||||
reorder: '재정렬',
|
reorder: '자동 정렬',
|
||||||
cancel: '취소',
|
cancel: '취소',
|
||||||
},
|
},
|
||||||
|
|
||||||
multiple_schemas_alert: {
|
|
||||||
title: '다중 스키마',
|
|
||||||
description:
|
|
||||||
'현재 다이어그램에 {{schemasCount}}개의 스키마가 있습니다. Currently displaying: {{formattedSchemas}}.',
|
|
||||||
// TODO: Translate
|
|
||||||
show_me: 'Show me',
|
|
||||||
none: '없음',
|
|
||||||
},
|
|
||||||
|
|
||||||
copy_to_clipboard_toast: {
|
copy_to_clipboard_toast: {
|
||||||
unsupported: {
|
unsupported: {
|
||||||
title: '복사 실패',
|
title: '복사 실패',
|
||||||
@@ -115,14 +115,11 @@ export const ko_KR: LanguageTranslation = {
|
|||||||
copied: '복사됨!',
|
copied: '복사됨!',
|
||||||
|
|
||||||
side_panel: {
|
side_panel: {
|
||||||
schema: '스키마:',
|
|
||||||
filter_by_schema: '스키마로 필터링',
|
|
||||||
search_schema: '스키마 검색...',
|
|
||||||
no_schemas_found: '스키마를 찾을 수 없습니다.',
|
|
||||||
view_all_options: '전체 옵션 보기...',
|
view_all_options: '전체 옵션 보기...',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: '테이블',
|
tables: '테이블',
|
||||||
add_table: '테이블 추가',
|
add_table: '테이블 추가',
|
||||||
|
add_view: '뷰 추가',
|
||||||
filter: '필터',
|
filter: '필터',
|
||||||
collapse: '모두 접기',
|
collapse: '모두 접기',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
@@ -148,6 +145,7 @@ export const ko_KR: LanguageTranslation = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: '필드 속성',
|
title: '필드 속성',
|
||||||
unique: '유니크 여부',
|
unique: '유니크 여부',
|
||||||
|
auto_increment: '자동 증가',
|
||||||
comments: '주석',
|
comments: '주석',
|
||||||
no_comments: '주석 없음',
|
no_comments: '주석 없음',
|
||||||
delete_field: '필드 삭제',
|
delete_field: '필드 삭제',
|
||||||
@@ -163,6 +161,7 @@ export const ko_KR: LanguageTranslation = {
|
|||||||
title: '인덱스 속성',
|
title: '인덱스 속성',
|
||||||
name: '인덱스 명',
|
name: '인덱스 명',
|
||||||
unique: '유니크 여부',
|
unique: '유니크 여부',
|
||||||
|
index_type: '인덱스 타입',
|
||||||
delete_index: '인덱스 삭제',
|
delete_index: '인덱스 삭제',
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -179,12 +178,15 @@ export const ko_KR: LanguageTranslation = {
|
|||||||
description: '테이블을 만들어 시작하세요.',
|
description: '테이블을 만들어 시작하세요.',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: '연관 관계',
|
refs: 'Refs',
|
||||||
filter: '필터',
|
filter: '필터',
|
||||||
add_relationship: '연관 관계 추가',
|
|
||||||
collapse: '모두 접기',
|
collapse: '모두 접기',
|
||||||
|
add_relationship: '연관 관계 추가',
|
||||||
|
relationships: '연관 관계',
|
||||||
|
dependencies: '종속성',
|
||||||
relationship: {
|
relationship: {
|
||||||
|
relationship: '연관 관계',
|
||||||
primary: '주 테이블',
|
primary: '주 테이블',
|
||||||
foreign: '참조 테이블',
|
foreign: '참조 테이블',
|
||||||
cardinality: '카디널리티',
|
cardinality: '카디널리티',
|
||||||
@@ -194,16 +196,8 @@ export const ko_KR: LanguageTranslation = {
|
|||||||
delete_relationship: '연관 관계 삭제',
|
delete_relationship: '연관 관계 삭제',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
|
||||||
title: '연관 관계',
|
|
||||||
description: '테이블 연결을 위해 연관 관계를 생성하세요',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dependencies_section: {
|
|
||||||
dependencies: '종속성',
|
|
||||||
filter: '필터',
|
|
||||||
collapse: '모두 접기',
|
|
||||||
dependency: {
|
dependency: {
|
||||||
|
dependency: '종속성',
|
||||||
table: '테이블',
|
table: '테이블',
|
||||||
dependent_table: '뷰 테이블',
|
dependent_table: '뷰 테이블',
|
||||||
delete_dependency: '삭제',
|
delete_dependency: '삭제',
|
||||||
@@ -213,8 +207,8 @@ export const ko_KR: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: '뷰 테이블 없음',
|
title: '연관 관계 없음',
|
||||||
description: '뷰 테이블을 만들어 시작하세요.',
|
description: '연관 관계를 만들어 시작하세요.',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -254,6 +248,7 @@ export const ko_KR: LanguageTranslation = {
|
|||||||
enum_values: 'Enum Values',
|
enum_values: 'Enum Values',
|
||||||
composite_fields: 'Fields',
|
composite_fields: 'Fields',
|
||||||
no_fields: 'No fields defined',
|
no_fields: 'No fields defined',
|
||||||
|
no_values: '정의된 열거형 값이 없습니다',
|
||||||
field_name_placeholder: 'Field name',
|
field_name_placeholder: 'Field name',
|
||||||
field_type_placeholder: 'Select type',
|
field_type_placeholder: 'Select type',
|
||||||
add_field: 'Add Field',
|
add_field: 'Add Field',
|
||||||
@@ -276,7 +271,7 @@ export const ko_KR: LanguageTranslation = {
|
|||||||
show_all: '전체 저장',
|
show_all: '전체 저장',
|
||||||
undo: '실행 취소',
|
undo: '실행 취소',
|
||||||
redo: '다시 실행',
|
redo: '다시 실행',
|
||||||
reorder_diagram: '다이어그램 재정렬',
|
reorder_diagram: '다이어그램 자동 정렬',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||||
custom_type_highlight_tooltip:
|
custom_type_highlight_tooltip:
|
||||||
@@ -314,13 +309,13 @@ export const ko_KR: LanguageTranslation = {
|
|||||||
cancel: '취소',
|
cancel: '취소',
|
||||||
back: '뒤로가기',
|
back: '뒤로가기',
|
||||||
import_from_file: '파일에서 가져오기',
|
import_from_file: '파일에서 가져오기',
|
||||||
empty_diagram: '빈 다이어그램으로 시작',
|
empty_diagram: '빈 데이터베이스',
|
||||||
continue: '계속',
|
continue: '계속',
|
||||||
import: '가져오기',
|
import: '가져오기',
|
||||||
},
|
},
|
||||||
|
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: '다이어그램 열기',
|
title: '데이터베이스 열기',
|
||||||
description: '아래의 목록에서 다이어그램을 선택하세요.',
|
description: '아래의 목록에서 다이어그램을 선택하세요.',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
name: '이름',
|
name: '이름',
|
||||||
@@ -330,6 +325,12 @@ export const ko_KR: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
cancel: '취소',
|
cancel: '취소',
|
||||||
open: '열기',
|
open: '열기',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: '열기',
|
||||||
|
duplicate: '복제',
|
||||||
|
delete: '삭제',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -476,6 +477,7 @@ export const ko_KR: LanguageTranslation = {
|
|||||||
|
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: '새 테이블',
|
new_table: '새 테이블',
|
||||||
|
new_view: '새 뷰',
|
||||||
new_relationship: '새 연관관계',
|
new_relationship: '새 연관관계',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
new_area: 'New Area',
|
new_area: 'New Area',
|
||||||
@@ -497,6 +499,9 @@ export const ko_KR: LanguageTranslation = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: '언어',
|
change_language: '언어',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: '켜기',
|
||||||
|
off: '끄기',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
|||||||
|
|
||||||
export const mr: LanguageTranslation = {
|
export const mr: LanguageTranslation = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: 'नवीन',
|
||||||
|
browse: 'ब्राउज',
|
||||||
|
tables: 'टेबल',
|
||||||
|
refs: 'Refs',
|
||||||
|
areas: 'क्षेत्रे',
|
||||||
|
dependencies: 'अवलंबने',
|
||||||
|
custom_types: 'कस्टम प्रकार',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: 'फाइल',
|
actions: 'क्रिया',
|
||||||
new: 'नवीन',
|
new: 'नवीन...',
|
||||||
open: 'उघडा',
|
browse: 'ब्राउज करा...',
|
||||||
save: 'जतन करा',
|
save: 'जतन करा',
|
||||||
import: 'डेटाबेस इम्पोर्ट करा',
|
import: 'डेटाबेस इम्पोर्ट करा',
|
||||||
export_sql: 'SQL एक्स्पोर्ट करा',
|
export_sql: 'SQL एक्स्पोर्ट करा',
|
||||||
export_as: 'म्हणून एक्स्पोर्ट करा',
|
export_as: 'म्हणून एक्स्पोर्ट करा',
|
||||||
delete_diagram: 'आरेख हटवा',
|
delete_diagram: 'हटवा',
|
||||||
exit: 'बाहेर पडा',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: 'संपादन करा',
|
edit: 'संपादन करा',
|
||||||
@@ -29,6 +37,7 @@ export const mr: LanguageTranslation = {
|
|||||||
hide_field_attributes: 'फील्ड गुणधर्म लपवा',
|
hide_field_attributes: 'फील्ड गुणधर्म लपवा',
|
||||||
show_field_attributes: 'फील्ड गुणधर्म दाखवा',
|
show_field_attributes: 'फील्ड गुणधर्म दाखवा',
|
||||||
zoom_on_scroll: 'स्क्रोलवर झूम करा',
|
zoom_on_scroll: 'स्क्रोलवर झूम करा',
|
||||||
|
show_views: 'डेटाबेस व्ह्यूज',
|
||||||
theme: 'थीम',
|
theme: 'थीम',
|
||||||
show_dependencies: 'डिपेंडेन्सि दाखवा',
|
show_dependencies: 'डिपेंडेन्सि दाखवा',
|
||||||
hide_dependencies: 'डिपेंडेन्सि लपवा',
|
hide_dependencies: 'डिपेंडेन्सि लपवा',
|
||||||
@@ -66,22 +75,13 @@ export const mr: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: 'आरेख पुनःक्रमित करा',
|
title: 'आरेख स्वयंचलित व्यवस्थित करा',
|
||||||
description:
|
description:
|
||||||
'ही क्रिया आरेखातील सर्व टेबल्सची पुनर्रचना करेल. तुम्हाला पुढे जायचे आहे का?',
|
'ही क्रिया आरेखातील सर्व टेबल्सची पुनर्रचना करेल. तुम्हाला पुढे जायचे आहे का?',
|
||||||
reorder: 'पुनःक्रमित करा',
|
reorder: 'स्वयंचलित व्यवस्थित करा',
|
||||||
cancel: 'रद्द करा',
|
cancel: 'रद्द करा',
|
||||||
},
|
},
|
||||||
|
|
||||||
multiple_schemas_alert: {
|
|
||||||
title: 'एकाधिक स्कीमा',
|
|
||||||
description:
|
|
||||||
'{{schemasCount}} स्कीमा या आरेखात आहेत. सध्या दाखवत आहोत: {{formattedSchemas}}.',
|
|
||||||
// TODO: Translate
|
|
||||||
show_me: 'Show me',
|
|
||||||
none: 'काहीही नाही',
|
|
||||||
},
|
|
||||||
|
|
||||||
copy_to_clipboard_toast: {
|
copy_to_clipboard_toast: {
|
||||||
unsupported: {
|
unsupported: {
|
||||||
title: 'कॉपी अयशस्वी',
|
title: 'कॉपी अयशस्वी',
|
||||||
@@ -118,14 +118,11 @@ export const mr: LanguageTranslation = {
|
|||||||
copied: 'Copied!',
|
copied: 'Copied!',
|
||||||
|
|
||||||
side_panel: {
|
side_panel: {
|
||||||
schema: 'स्कीमा:',
|
|
||||||
filter_by_schema: 'स्कीमा द्वारे फिल्टर करा',
|
|
||||||
search_schema: 'स्कीमा शोधा...',
|
|
||||||
no_schemas_found: 'कोणतेही स्कीमा सापडले नाहीत.',
|
|
||||||
view_all_options: 'सर्व पर्याय पहा...',
|
view_all_options: 'सर्व पर्याय पहा...',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: 'टेबल्स',
|
tables: 'टेबल्स',
|
||||||
add_table: 'टेबल जोडा',
|
add_table: 'टेबल जोडा',
|
||||||
|
add_view: 'व्ह्यू जोडा',
|
||||||
filter: 'फिल्टर',
|
filter: 'फिल्टर',
|
||||||
collapse: 'सर्व संकुचित करा',
|
collapse: 'सर्व संकुचित करा',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
@@ -151,6 +148,7 @@ export const mr: LanguageTranslation = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: 'फील्ड गुणधर्म',
|
title: 'फील्ड गुणधर्म',
|
||||||
unique: 'युनिक',
|
unique: 'युनिक',
|
||||||
|
auto_increment: 'ऑटो इंक्रिमेंट',
|
||||||
comments: 'टिप्पण्या',
|
comments: 'टिप्पण्या',
|
||||||
no_comments: 'कोणत्याही टिप्पणी नाहीत',
|
no_comments: 'कोणत्याही टिप्पणी नाहीत',
|
||||||
delete_field: 'फील्ड हटवा',
|
delete_field: 'फील्ड हटवा',
|
||||||
@@ -166,6 +164,7 @@ export const mr: LanguageTranslation = {
|
|||||||
title: 'इंडेक्स गुणधर्म',
|
title: 'इंडेक्स गुणधर्म',
|
||||||
name: 'नाव',
|
name: 'नाव',
|
||||||
unique: 'युनिक',
|
unique: 'युनिक',
|
||||||
|
index_type: 'इंडेक्स प्रकार',
|
||||||
delete_index: 'इंडेक्स हटवा',
|
delete_index: 'इंडेक्स हटवा',
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -183,12 +182,15 @@ export const mr: LanguageTranslation = {
|
|||||||
description: 'सुरू करण्यासाठी एक टेबल तयार करा',
|
description: 'सुरू करण्यासाठी एक टेबल तयार करा',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: 'रिलेशनशिप',
|
refs: 'Refs',
|
||||||
filter: 'फिल्टर',
|
filter: 'फिल्टर',
|
||||||
add_relationship: 'रिलेशनशिप जोडा',
|
|
||||||
collapse: 'सर्व संकुचित करा',
|
collapse: 'सर्व संकुचित करा',
|
||||||
|
add_relationship: 'रिलेशनशिप जोडा',
|
||||||
|
relationships: 'रिलेशनशिप',
|
||||||
|
dependencies: 'डिपेंडेन्सि',
|
||||||
relationship: {
|
relationship: {
|
||||||
|
relationship: 'रिलेशनशिप',
|
||||||
primary: 'प्राथमिक टेबल',
|
primary: 'प्राथमिक टेबल',
|
||||||
foreign: 'रेफरंस टेबल',
|
foreign: 'रेफरंस टेबल',
|
||||||
cardinality: 'कार्डिनॅलिटी',
|
cardinality: 'कार्डिनॅलिटी',
|
||||||
@@ -198,17 +200,8 @@ export const mr: LanguageTranslation = {
|
|||||||
delete_relationship: 'हटवा',
|
delete_relationship: 'हटवा',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
|
||||||
title: 'कोणतेही रिलेशनशिप नाहीत',
|
|
||||||
description:
|
|
||||||
'टेबल्स कनेक्ट करण्यासाठी एक रिलेशनशिप तयार करा',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dependencies_section: {
|
|
||||||
dependencies: 'डिपेंडेन्सि',
|
|
||||||
filter: 'फिल्टर',
|
|
||||||
collapse: 'सर्व संकुचित करा',
|
|
||||||
dependency: {
|
dependency: {
|
||||||
|
dependency: 'डिपेंडेन्सि',
|
||||||
table: 'टेबल',
|
table: 'टेबल',
|
||||||
dependent_table: 'डिपेंडेन्सि दृश्य',
|
dependent_table: 'डिपेंडेन्सि दृश्य',
|
||||||
delete_dependency: 'हटवा',
|
delete_dependency: 'हटवा',
|
||||||
@@ -218,8 +211,8 @@ export const mr: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: 'कोणत्याही डिपेंडेन्सि नाहीत',
|
title: 'कोणतेही रिलेशनशिप नाहीत',
|
||||||
description: 'सुरू करण्यासाठी एक दृश्य तयार करा',
|
description: 'सुरू करण्यासाठी एक रिलेशनशिप तयार करा',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -259,6 +252,7 @@ export const mr: LanguageTranslation = {
|
|||||||
enum_values: 'Enum Values',
|
enum_values: 'Enum Values',
|
||||||
composite_fields: 'Fields',
|
composite_fields: 'Fields',
|
||||||
no_fields: 'No fields defined',
|
no_fields: 'No fields defined',
|
||||||
|
no_values: 'कोणतीही enum मूल्ये परिभाषित नाहीत',
|
||||||
field_name_placeholder: 'Field name',
|
field_name_placeholder: 'Field name',
|
||||||
field_type_placeholder: 'Select type',
|
field_type_placeholder: 'Select type',
|
||||||
add_field: 'Add Field',
|
add_field: 'Add Field',
|
||||||
@@ -281,7 +275,7 @@ export const mr: LanguageTranslation = {
|
|||||||
show_all: 'सर्व दाखवा',
|
show_all: 'सर्व दाखवा',
|
||||||
undo: 'पूर्ववत करा',
|
undo: 'पूर्ववत करा',
|
||||||
redo: 'पुन्हा करा',
|
redo: 'पुन्हा करा',
|
||||||
reorder_diagram: 'आरेख पुनःक्रमित करा',
|
reorder_diagram: 'आरेख स्वयंचलित व्यवस्थित करा',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||||
custom_type_highlight_tooltip:
|
custom_type_highlight_tooltip:
|
||||||
@@ -321,13 +315,13 @@ export const mr: LanguageTranslation = {
|
|||||||
// TODO: Add translations
|
// TODO: Add translations
|
||||||
import_from_file: 'Import from File',
|
import_from_file: 'Import from File',
|
||||||
back: 'मागे',
|
back: 'मागे',
|
||||||
empty_diagram: 'रिक्त आरेख',
|
empty_diagram: 'रिक्त डेटाबेस',
|
||||||
continue: 'सुरू ठेवा',
|
continue: 'सुरू ठेवा',
|
||||||
import: 'आयात करा',
|
import: 'आयात करा',
|
||||||
},
|
},
|
||||||
|
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: 'आरेख उघडा',
|
title: 'डेटाबेस उघडा',
|
||||||
description: 'खालील यादीतून उघडण्यासाठी एक आरेख निवडा.',
|
description: 'खालील यादीतून उघडण्यासाठी एक आरेख निवडा.',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
name: 'नाव',
|
name: 'नाव',
|
||||||
@@ -337,6 +331,12 @@ export const mr: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
cancel: 'रद्द करा',
|
cancel: 'रद्द करा',
|
||||||
open: 'उघडा',
|
open: 'उघडा',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: 'उघडा',
|
||||||
|
duplicate: 'डुप्लिकेट',
|
||||||
|
delete: 'हटवा',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -489,6 +489,7 @@ export const mr: LanguageTranslation = {
|
|||||||
|
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: 'नवीन टेबल',
|
new_table: 'नवीन टेबल',
|
||||||
|
new_view: 'नवीन व्ह्यू',
|
||||||
new_relationship: 'नवीन रिलेशनशिप',
|
new_relationship: 'नवीन रिलेशनशिप',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
new_area: 'New Area',
|
new_area: 'New Area',
|
||||||
@@ -512,6 +513,9 @@ export const mr: LanguageTranslation = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: 'भाषा बदला',
|
change_language: 'भाषा बदला',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: 'चालू',
|
||||||
|
off: 'बंद',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
|||||||
|
|
||||||
export const ne: LanguageTranslation = {
|
export const ne: LanguageTranslation = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: 'नयाँ',
|
||||||
|
browse: 'ब्राउज',
|
||||||
|
tables: 'टेबलहरू',
|
||||||
|
refs: 'Refs',
|
||||||
|
areas: 'क्षेत्रहरू',
|
||||||
|
dependencies: 'निर्भरताहरू',
|
||||||
|
custom_types: 'कस्टम प्रकारहरू',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: 'फाइल',
|
actions: 'कार्यहरू',
|
||||||
new: 'नयाँ',
|
new: 'नयाँ...',
|
||||||
open: 'खोल्नुहोस्',
|
browse: 'ब्राउज गर्नुहोस्...',
|
||||||
save: 'सुरक्षित गर्नुहोस्',
|
save: 'सुरक्षित गर्नुहोस्',
|
||||||
import: 'डाटाबेस आयात गर्नुहोस्',
|
import: 'डाटाबेस आयात गर्नुहोस्',
|
||||||
export_sql: 'SQL निर्यात गर्नुहोस्',
|
export_sql: 'SQL निर्यात गर्नुहोस्',
|
||||||
export_as: 'निर्यात गर्नुहोस्',
|
export_as: 'निर्यात गर्नुहोस्',
|
||||||
delete_diagram: 'डायाग्राम हटाउनुहोस्',
|
delete_diagram: 'हटाउनुहोस्',
|
||||||
exit: 'बाहिर निस्कनुहोस्',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: 'सम्पादन',
|
edit: 'सम्पादन',
|
||||||
@@ -29,6 +37,7 @@ export const ne: LanguageTranslation = {
|
|||||||
hide_field_attributes: 'फिल्ड विशेषताहरू लुकाउनुहोस्',
|
hide_field_attributes: 'फिल्ड विशेषताहरू लुकाउनुहोस्',
|
||||||
show_field_attributes: 'फिल्ड विशेषताहरू देखाउनुहोस्',
|
show_field_attributes: 'फिल्ड विशेषताहरू देखाउनुहोस्',
|
||||||
zoom_on_scroll: 'स्क्रोलमा जुम गर्नुहोस्',
|
zoom_on_scroll: 'स्क्रोलमा जुम गर्नुहोस्',
|
||||||
|
show_views: 'डाटाबेस भ्यूहरू',
|
||||||
theme: 'थिम',
|
theme: 'थिम',
|
||||||
show_dependencies: 'डिपेन्डेन्सीहरू देखाउनुहोस्',
|
show_dependencies: 'डिपेन्डेन्सीहरू देखाउनुहोस्',
|
||||||
hide_dependencies: 'डिपेन्डेन्सीहरू लुकाउनुहोस्',
|
hide_dependencies: 'डिपेन्डेन्सीहरू लुकाउनुहोस्',
|
||||||
@@ -66,22 +75,13 @@ export const ne: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: 'डायाग्राम पुनः क्रमबद्ध गर्नुहोस्',
|
title: 'डायाग्राम स्वचालित मिलाउनुहोस्',
|
||||||
description:
|
description:
|
||||||
'यो कार्य पूर्ववत गर्न सकिँदैन। यो डायाग्राम स्थायी रूपमा हटाउनेछ।',
|
'यो कार्य पूर्ववत गर्न सकिँदैन। यो डायाग्राम स्थायी रूपमा हटाउनेछ।',
|
||||||
reorder: 'पुनः क्रमबद्ध गर्नुहोस्',
|
reorder: 'स्वचालित मिलाउनुहोस्',
|
||||||
cancel: 'रद्द गर्नुहोस्',
|
cancel: 'रद्द गर्नुहोस्',
|
||||||
},
|
},
|
||||||
|
|
||||||
multiple_schemas_alert: {
|
|
||||||
title: 'विविध स्कीमहरू',
|
|
||||||
description:
|
|
||||||
'{{schemasCount}} डायाग्राममा स्कीमहरू। हालको रूपमा देखाइएको छ: {{formattedSchemas}}।',
|
|
||||||
// TODO: Translate
|
|
||||||
show_me: 'Show me',
|
|
||||||
none: 'कुनै पनि छैन',
|
|
||||||
},
|
|
||||||
|
|
||||||
copy_to_clipboard_toast: {
|
copy_to_clipboard_toast: {
|
||||||
unsupported: {
|
unsupported: {
|
||||||
title: 'प्रतिलिपि असफल',
|
title: 'प्रतिलिपि असफल',
|
||||||
@@ -116,14 +116,11 @@ export const ne: LanguageTranslation = {
|
|||||||
copied: 'प्रतिलिपि गरियो!',
|
copied: 'प्रतिलिपि गरियो!',
|
||||||
|
|
||||||
side_panel: {
|
side_panel: {
|
||||||
schema: 'स्कीम:',
|
|
||||||
filter_by_schema: 'स्कीम अनुसार फिल्टर गर्नुहोस्',
|
|
||||||
search_schema: 'स्कीम खोज्नुहोस्...',
|
|
||||||
no_schemas_found: 'कुनै स्कीमहरू फेला परेनन्',
|
|
||||||
view_all_options: 'सबै विकल्पहरू हेर्नुहोस्',
|
view_all_options: 'सबै विकल्पहरू हेर्नुहोस्',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: 'तालिकाहरू',
|
tables: 'तालिकाहरू',
|
||||||
add_table: 'तालिका थप्नुहोस्',
|
add_table: 'तालिका थप्नुहोस्',
|
||||||
|
add_view: 'भ्यू थप्नुहोस्',
|
||||||
filter: 'फिल्टर',
|
filter: 'फिल्टर',
|
||||||
collapse: 'सबै लुकाउनुहोस्',
|
collapse: 'सबै लुकाउनुहोस्',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
@@ -149,6 +146,7 @@ export const ne: LanguageTranslation = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: 'क्षेत्र विशेषताहरू',
|
title: 'क्षेत्र विशेषताहरू',
|
||||||
unique: 'अनन्य',
|
unique: 'अनन्य',
|
||||||
|
auto_increment: 'स्वचालित वृद्धि',
|
||||||
comments: 'टिप्पणीहरू',
|
comments: 'टिप्पणीहरू',
|
||||||
no_comments: 'कुनै टिप्पणीहरू छैनन्',
|
no_comments: 'कुनै टिप्पणीहरू छैनन्',
|
||||||
delete_field: 'क्षेत्र हटाउनुहोस्',
|
delete_field: 'क्षेत्र हटाउनुहोस्',
|
||||||
@@ -164,6 +162,7 @@ export const ne: LanguageTranslation = {
|
|||||||
title: 'सूचक विशेषताहरू',
|
title: 'सूचक विशेषताहरू',
|
||||||
name: 'नाम',
|
name: 'नाम',
|
||||||
unique: 'अनन्य',
|
unique: 'अनन्य',
|
||||||
|
index_type: 'इन्डेक्स प्रकार',
|
||||||
delete_index: 'सूचक हटाउनुहोस्',
|
delete_index: 'सूचक हटाउनुहोस्',
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -180,12 +179,15 @@ export const ne: LanguageTranslation = {
|
|||||||
description: 'सुरु गर्नका लागि एक तालिका बनाउनुहोस्',
|
description: 'सुरु गर्नका लागि एक तालिका बनाउनुहोस्',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: 'सम्बन्धहरू',
|
refs: 'Refs',
|
||||||
filter: 'फिल्टर',
|
filter: 'फिल्टर',
|
||||||
add_relationship: 'सम्बन्ध थप्नुहोस्',
|
|
||||||
collapse: 'सबै लुकाउनुहोस्',
|
collapse: 'सबै लुकाउनुहोस्',
|
||||||
|
add_relationship: 'सम्बन्ध थप्नुहोस्',
|
||||||
|
relationships: 'सम्बन्धहरू',
|
||||||
|
dependencies: 'डिपेन्डेन्सीहरू',
|
||||||
relationship: {
|
relationship: {
|
||||||
|
relationship: 'सम्बन्ध',
|
||||||
primary: 'मुख्य तालिका',
|
primary: 'मुख्य तालिका',
|
||||||
foreign: 'परिचित तालिका',
|
foreign: 'परिचित तालिका',
|
||||||
cardinality: 'कार्डिन्यालिटी',
|
cardinality: 'कार्डिन्यालिटी',
|
||||||
@@ -195,16 +197,8 @@ export const ne: LanguageTranslation = {
|
|||||||
delete_relationship: 'हटाउनुहोस्',
|
delete_relationship: 'हटाउनुहोस्',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
|
||||||
title: 'कुनै सम्बन्धहरू छैनन्',
|
|
||||||
description: 'तालिकाहरू जोड्नका लागि एक सम्बन्ध बनाउनुहोस्',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dependencies_section: {
|
|
||||||
dependencies: 'डिपेन्डेन्सीहरू',
|
|
||||||
filter: 'फिल्टर',
|
|
||||||
collapse: 'सबै लुकाउनुहोस्',
|
|
||||||
dependency: {
|
dependency: {
|
||||||
|
dependency: 'डिपेन्डेन्सी',
|
||||||
table: 'तालिका',
|
table: 'तालिका',
|
||||||
dependent_table: 'विचलित तालिका',
|
dependent_table: 'विचलित तालिका',
|
||||||
delete_dependency: 'हटाउनुहोस्',
|
delete_dependency: 'हटाउनुहोस्',
|
||||||
@@ -214,9 +208,8 @@ export const ne: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: 'कुनै डिपेन्डेन्सीहरू छैनन्',
|
title: 'कुनै सम्बन्धहरू छैनन्',
|
||||||
description:
|
description: 'सुरु गर्नका लागि एक सम्बन्ध बनाउनुहोस्',
|
||||||
'डिपेन्डेन्सीहरू देखाउनका लागि एक व्यू बनाउनुहोस्',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -256,6 +249,7 @@ export const ne: LanguageTranslation = {
|
|||||||
enum_values: 'Enum Values',
|
enum_values: 'Enum Values',
|
||||||
composite_fields: 'Fields',
|
composite_fields: 'Fields',
|
||||||
no_fields: 'No fields defined',
|
no_fields: 'No fields defined',
|
||||||
|
no_values: 'कुनै enum मानहरू परिभाषित छैनन्',
|
||||||
field_name_placeholder: 'Field name',
|
field_name_placeholder: 'Field name',
|
||||||
field_type_placeholder: 'Select type',
|
field_type_placeholder: 'Select type',
|
||||||
add_field: 'Add Field',
|
add_field: 'Add Field',
|
||||||
@@ -278,7 +272,7 @@ export const ne: LanguageTranslation = {
|
|||||||
show_all: 'सबै देखाउनुहोस्',
|
show_all: 'सबै देखाउनुहोस्',
|
||||||
undo: 'पूर्ववत',
|
undo: 'पूर्ववत',
|
||||||
redo: 'पुनः गर्नुहोस्',
|
redo: 'पुनः गर्नुहोस्',
|
||||||
reorder_diagram: 'पुनः क्रमबद्ध गर्नुहोस्',
|
reorder_diagram: 'डायाग्राम स्वचालित मिलाउनुहोस्',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||||
custom_type_highlight_tooltip:
|
custom_type_highlight_tooltip:
|
||||||
@@ -317,13 +311,13 @@ export const ne: LanguageTranslation = {
|
|||||||
cancel: 'रद्द गर्नुहोस्',
|
cancel: 'रद्द गर्नुहोस्',
|
||||||
import_from_file: 'फाइलबाट आयात गर्नुहोस्',
|
import_from_file: 'फाइलबाट आयात गर्नुहोस्',
|
||||||
back: 'फर्क',
|
back: 'फर्क',
|
||||||
empty_diagram: 'रिक्त डायाग्राम',
|
empty_diagram: 'खाली डाटाबेस',
|
||||||
continue: 'जारी राख्नुहोस्',
|
continue: 'जारी राख्नुहोस्',
|
||||||
import: 'आयात गर्नुहोस्',
|
import: 'आयात गर्नुहोस्',
|
||||||
},
|
},
|
||||||
|
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: 'डायाग्राम खोल्नुहोस्',
|
title: 'डाटाबेस खोल्नुहोस्',
|
||||||
description:
|
description:
|
||||||
'तलको सूचीबाट खोल्नका लागि एक डायाग्राम चयन गर्नुहोस्।',
|
'तलको सूचीबाट खोल्नका लागि एक डायाग्राम चयन गर्नुहोस्।',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
@@ -334,6 +328,12 @@ export const ne: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
cancel: 'रद्द गर्नुहोस्',
|
cancel: 'रद्द गर्नुहोस्',
|
||||||
open: 'खोल्नुहोस्',
|
open: 'खोल्नुहोस्',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: 'खोल्नुहोस्',
|
||||||
|
duplicate: 'डुप्लिकेट',
|
||||||
|
delete: 'मेटाउनुहोस्',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -483,6 +483,7 @@ export const ne: LanguageTranslation = {
|
|||||||
|
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: 'नयाँ तालिका',
|
new_table: 'नयाँ तालिका',
|
||||||
|
new_view: 'नयाँ भ्यू',
|
||||||
new_relationship: 'नयाँ सम्बन्ध',
|
new_relationship: 'नयाँ सम्बन्ध',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
new_area: 'New Area',
|
new_area: 'New Area',
|
||||||
@@ -504,6 +505,9 @@ export const ne: LanguageTranslation = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: 'भाषा परिवर्तन गर्नुहोस्',
|
change_language: 'भाषा परिवर्तन गर्नुहोस्',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: 'सक्रिय',
|
||||||
|
off: 'निष्क्रिय',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
|||||||
|
|
||||||
export const pt_BR: LanguageTranslation = {
|
export const pt_BR: LanguageTranslation = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: 'Novo',
|
||||||
|
browse: 'Navegar',
|
||||||
|
tables: 'Tabelas',
|
||||||
|
refs: 'Refs',
|
||||||
|
areas: 'Áreas',
|
||||||
|
dependencies: 'Dependências',
|
||||||
|
custom_types: 'Tipos Personalizados',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: 'Arquivo',
|
actions: 'Ações',
|
||||||
new: 'Novo',
|
new: 'Novo...',
|
||||||
open: 'Abrir',
|
browse: 'Navegar...',
|
||||||
save: 'Salvar',
|
save: 'Salvar',
|
||||||
import: 'Importar Banco de Dados',
|
import: 'Importar Banco de Dados',
|
||||||
export_sql: 'Exportar SQL',
|
export_sql: 'Exportar SQL',
|
||||||
export_as: 'Exportar como',
|
export_as: 'Exportar como',
|
||||||
delete_diagram: 'Excluir Diagrama',
|
delete_diagram: 'Excluir',
|
||||||
exit: 'Sair',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: 'Editar',
|
edit: 'Editar',
|
||||||
@@ -29,6 +37,7 @@ export const pt_BR: LanguageTranslation = {
|
|||||||
hide_field_attributes: 'Ocultar Atributos de Campo',
|
hide_field_attributes: 'Ocultar Atributos de Campo',
|
||||||
show_field_attributes: 'Mostrar Atributos de Campo',
|
show_field_attributes: 'Mostrar Atributos de Campo',
|
||||||
zoom_on_scroll: 'Zoom ao Rolar',
|
zoom_on_scroll: 'Zoom ao Rolar',
|
||||||
|
show_views: 'Visualizações do Banco de Dados',
|
||||||
theme: 'Tema',
|
theme: 'Tema',
|
||||||
show_dependencies: 'Mostrar Dependências',
|
show_dependencies: 'Mostrar Dependências',
|
||||||
hide_dependencies: 'Ocultar Dependências',
|
hide_dependencies: 'Ocultar Dependências',
|
||||||
@@ -66,22 +75,13 @@ export const pt_BR: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: 'Reordenar Diagrama',
|
title: 'Organizar Diagrama Automaticamente',
|
||||||
description:
|
description:
|
||||||
'Esta ação reorganizará todas as tabelas no diagrama. Deseja continuar?',
|
'Esta ação reorganizará todas as tabelas no diagrama. Deseja continuar?',
|
||||||
reorder: 'Reordenar',
|
reorder: 'Organizar Automaticamente',
|
||||||
cancel: 'Cancelar',
|
cancel: 'Cancelar',
|
||||||
},
|
},
|
||||||
|
|
||||||
multiple_schemas_alert: {
|
|
||||||
title: 'Múltiplos Esquemas',
|
|
||||||
description:
|
|
||||||
'{{schemasCount}} esquemas neste diagrama. Atualmente exibindo: {{formattedSchemas}}.',
|
|
||||||
// TODO: Translate
|
|
||||||
show_me: 'Show me',
|
|
||||||
none: 'nenhum',
|
|
||||||
},
|
|
||||||
|
|
||||||
copy_to_clipboard_toast: {
|
copy_to_clipboard_toast: {
|
||||||
unsupported: {
|
unsupported: {
|
||||||
title: 'Falha na cópia',
|
title: 'Falha na cópia',
|
||||||
@@ -116,14 +116,11 @@ export const pt_BR: LanguageTranslation = {
|
|||||||
copied: 'Copiado!',
|
copied: 'Copiado!',
|
||||||
|
|
||||||
side_panel: {
|
side_panel: {
|
||||||
schema: 'Esquema:',
|
|
||||||
filter_by_schema: 'Filtrar por esquema',
|
|
||||||
search_schema: 'Buscar esquema...',
|
|
||||||
no_schemas_found: 'Nenhum esquema encontrado.',
|
|
||||||
view_all_options: 'Ver todas as Opções...',
|
view_all_options: 'Ver todas as Opções...',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: 'Tabelas',
|
tables: 'Tabelas',
|
||||||
add_table: 'Adicionar Tabela',
|
add_table: 'Adicionar Tabela',
|
||||||
|
add_view: 'Adicionar Visualização',
|
||||||
filter: 'Filtrar',
|
filter: 'Filtrar',
|
||||||
collapse: 'Colapsar Todas',
|
collapse: 'Colapsar Todas',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
@@ -149,6 +146,7 @@ export const pt_BR: LanguageTranslation = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: 'Atributos do Campo',
|
title: 'Atributos do Campo',
|
||||||
unique: 'Único',
|
unique: 'Único',
|
||||||
|
auto_increment: 'Incremento Automático',
|
||||||
comments: 'Comentários',
|
comments: 'Comentários',
|
||||||
no_comments: 'Sem comentários',
|
no_comments: 'Sem comentários',
|
||||||
delete_field: 'Excluir Campo',
|
delete_field: 'Excluir Campo',
|
||||||
@@ -164,6 +162,7 @@ export const pt_BR: LanguageTranslation = {
|
|||||||
title: 'Atributos do Índice',
|
title: 'Atributos do Índice',
|
||||||
name: 'Nome',
|
name: 'Nome',
|
||||||
unique: 'Único',
|
unique: 'Único',
|
||||||
|
index_type: 'Tipo de Índice',
|
||||||
delete_index: 'Excluir Índice',
|
delete_index: 'Excluir Índice',
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -180,12 +179,15 @@ export const pt_BR: LanguageTranslation = {
|
|||||||
description: 'Crie uma tabela para começar',
|
description: 'Crie uma tabela para começar',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: 'Relacionamentos',
|
refs: 'Refs',
|
||||||
filter: 'Filtrar',
|
filter: 'Filtrar',
|
||||||
add_relationship: 'Adicionar Relacionamento',
|
|
||||||
collapse: 'Colapsar Todas',
|
collapse: 'Colapsar Todas',
|
||||||
|
add_relationship: 'Adicionar Relacionamento',
|
||||||
|
relationships: 'Relacionamentos',
|
||||||
|
dependencies: 'Dependências',
|
||||||
relationship: {
|
relationship: {
|
||||||
|
relationship: 'Relacionamento',
|
||||||
primary: 'Tabela Primária',
|
primary: 'Tabela Primária',
|
||||||
foreign: 'Tabela Referenciada',
|
foreign: 'Tabela Referenciada',
|
||||||
cardinality: 'Cardinalidade',
|
cardinality: 'Cardinalidade',
|
||||||
@@ -195,16 +197,8 @@ export const pt_BR: LanguageTranslation = {
|
|||||||
delete_relationship: 'Excluir',
|
delete_relationship: 'Excluir',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
|
||||||
title: 'Sem relacionamentos',
|
|
||||||
description: 'Crie um relacionamento para conectar tabelas',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dependencies_section: {
|
|
||||||
dependencies: 'Dependências',
|
|
||||||
filter: 'Filtrar',
|
|
||||||
collapse: 'Colapsar Todas',
|
|
||||||
dependency: {
|
dependency: {
|
||||||
|
dependency: 'Dependência',
|
||||||
table: 'Tabela',
|
table: 'Tabela',
|
||||||
dependent_table: 'Visualização Dependente',
|
dependent_table: 'Visualização Dependente',
|
||||||
delete_dependency: 'Excluir',
|
delete_dependency: 'Excluir',
|
||||||
@@ -214,8 +208,8 @@ export const pt_BR: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: 'Sem dependências',
|
title: 'Sem relacionamentos',
|
||||||
description: 'Crie uma visualização para começar',
|
description: 'Crie um relacionamento para começar',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -255,6 +249,7 @@ export const pt_BR: LanguageTranslation = {
|
|||||||
enum_values: 'Enum Values',
|
enum_values: 'Enum Values',
|
||||||
composite_fields: 'Fields',
|
composite_fields: 'Fields',
|
||||||
no_fields: 'No fields defined',
|
no_fields: 'No fields defined',
|
||||||
|
no_values: 'Nenhum valor de enum definido',
|
||||||
field_name_placeholder: 'Field name',
|
field_name_placeholder: 'Field name',
|
||||||
field_type_placeholder: 'Select type',
|
field_type_placeholder: 'Select type',
|
||||||
add_field: 'Add Field',
|
add_field: 'Add Field',
|
||||||
@@ -277,7 +272,7 @@ export const pt_BR: LanguageTranslation = {
|
|||||||
show_all: 'Mostrar Tudo',
|
show_all: 'Mostrar Tudo',
|
||||||
undo: 'Desfazer',
|
undo: 'Desfazer',
|
||||||
redo: 'Refazer',
|
redo: 'Refazer',
|
||||||
reorder_diagram: 'Reordenar Diagrama',
|
reorder_diagram: 'Organizar Diagrama Automaticamente',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||||
custom_type_highlight_tooltip:
|
custom_type_highlight_tooltip:
|
||||||
@@ -316,13 +311,13 @@ export const pt_BR: LanguageTranslation = {
|
|||||||
back: 'Voltar',
|
back: 'Voltar',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
import_from_file: 'Import from File',
|
import_from_file: 'Import from File',
|
||||||
empty_diagram: 'Diagrama vazio',
|
empty_diagram: 'Banco de dados vazio',
|
||||||
continue: 'Continuar',
|
continue: 'Continuar',
|
||||||
import: 'Importar',
|
import: 'Importar',
|
||||||
},
|
},
|
||||||
|
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: 'Abrir Diagrama',
|
title: 'Abrir Banco de Dados',
|
||||||
description: 'Selecione um diagrama para abrir da lista abaixo.',
|
description: 'Selecione um diagrama para abrir da lista abaixo.',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
name: 'Nome',
|
name: 'Nome',
|
||||||
@@ -332,6 +327,12 @@ export const pt_BR: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
cancel: 'Cancelar',
|
cancel: 'Cancelar',
|
||||||
open: 'Abrir',
|
open: 'Abrir',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: 'Abrir',
|
||||||
|
duplicate: 'Duplicar',
|
||||||
|
delete: 'Excluir',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -481,6 +482,7 @@ export const pt_BR: LanguageTranslation = {
|
|||||||
|
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: 'Nova Tabela',
|
new_table: 'Nova Tabela',
|
||||||
|
new_view: 'Nova Visualização',
|
||||||
new_relationship: 'Novo Relacionamento',
|
new_relationship: 'Novo Relacionamento',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
new_area: 'New Area',
|
new_area: 'New Area',
|
||||||
@@ -503,6 +505,9 @@ export const pt_BR: LanguageTranslation = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: 'Idioma',
|
change_language: 'Idioma',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: 'Ligado',
|
||||||
|
off: 'Desligado',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
|||||||
|
|
||||||
export const ru: LanguageTranslation = {
|
export const ru: LanguageTranslation = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: 'Новая',
|
||||||
|
browse: 'Обзор',
|
||||||
|
tables: 'Таблицы',
|
||||||
|
refs: 'Ссылки',
|
||||||
|
areas: 'Области',
|
||||||
|
dependencies: 'Зависимости',
|
||||||
|
custom_types: 'Пользовательские типы',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: 'Файл',
|
actions: 'Действия',
|
||||||
new: 'Создать',
|
new: 'Новая...',
|
||||||
open: 'Открыть',
|
browse: 'Обзор...',
|
||||||
save: 'Сохранить',
|
save: 'Сохранить',
|
||||||
import: 'Импортировать базу данных',
|
import: 'Импортировать базу данных',
|
||||||
export_sql: 'Экспорт SQL',
|
export_sql: 'Экспорт SQL',
|
||||||
export_as: 'Экспортировать как',
|
export_as: 'Экспортировать как',
|
||||||
delete_diagram: 'Удалить диаграмму',
|
delete_diagram: 'Удалить',
|
||||||
exit: 'Выход',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: 'Изменение',
|
edit: 'Изменение',
|
||||||
@@ -29,6 +37,7 @@ export const ru: LanguageTranslation = {
|
|||||||
show_field_attributes: 'Показать атрибуты поля',
|
show_field_attributes: 'Показать атрибуты поля',
|
||||||
hide_field_attributes: 'Скрыть атрибуты поля',
|
hide_field_attributes: 'Скрыть атрибуты поля',
|
||||||
zoom_on_scroll: 'Увеличение при прокрутке',
|
zoom_on_scroll: 'Увеличение при прокрутке',
|
||||||
|
show_views: 'Представления базы данных',
|
||||||
theme: 'Тема',
|
theme: 'Тема',
|
||||||
show_dependencies: 'Показать зависимости',
|
show_dependencies: 'Показать зависимости',
|
||||||
hide_dependencies: 'Скрыть зависимости',
|
hide_dependencies: 'Скрыть зависимости',
|
||||||
@@ -64,22 +73,13 @@ export const ru: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: 'Переупорядочить диаграмму',
|
title: 'Автоматическая расстановка диаграммы',
|
||||||
description:
|
description:
|
||||||
'Это действие переставит все таблицы на диаграмме. Хотите продолжить?',
|
'Это действие переставит все таблицы на диаграмме. Хотите продолжить?',
|
||||||
reorder: 'Изменить порядок',
|
reorder: 'Автоматическая расстановка',
|
||||||
cancel: 'Отменить',
|
cancel: 'Отменить',
|
||||||
},
|
},
|
||||||
|
|
||||||
multiple_schemas_alert: {
|
|
||||||
title: 'Множественные схемы',
|
|
||||||
description:
|
|
||||||
'{{schemasCount}} схем в этой диаграмме. В данный момент отображается: {{formattedSchemas}}.',
|
|
||||||
// TODO: Translate
|
|
||||||
show_me: 'Show me',
|
|
||||||
none: 'никто',
|
|
||||||
},
|
|
||||||
|
|
||||||
copy_to_clipboard_toast: {
|
copy_to_clipboard_toast: {
|
||||||
unsupported: {
|
unsupported: {
|
||||||
title: 'Ошибка копирования',
|
title: 'Ошибка копирования',
|
||||||
@@ -113,14 +113,11 @@ export const ru: LanguageTranslation = {
|
|||||||
show_less: 'Показать меньше',
|
show_less: 'Показать меньше',
|
||||||
|
|
||||||
side_panel: {
|
side_panel: {
|
||||||
schema: 'Схема:',
|
|
||||||
filter_by_schema: 'Фильтр по схеме',
|
|
||||||
search_schema: 'Схема поиска...',
|
|
||||||
no_schemas_found: 'Схемы не найдены.',
|
|
||||||
view_all_options: 'Просмотреть все варианты...',
|
view_all_options: 'Просмотреть все варианты...',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: 'Таблицы',
|
tables: 'Таблицы',
|
||||||
add_table: 'Добавить таблицу',
|
add_table: 'Добавить таблицу',
|
||||||
|
add_view: 'Добавить представление',
|
||||||
filter: 'Фильтр',
|
filter: 'Фильтр',
|
||||||
collapse: 'Свернуть все',
|
collapse: 'Свернуть все',
|
||||||
clear: 'Очистить фильтр',
|
clear: 'Очистить фильтр',
|
||||||
@@ -146,6 +143,7 @@ export const ru: LanguageTranslation = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: 'Атрибуты поля',
|
title: 'Атрибуты поля',
|
||||||
unique: 'Уникальный',
|
unique: 'Уникальный',
|
||||||
|
auto_increment: 'Автоинкремент',
|
||||||
comments: 'Комментарии',
|
comments: 'Комментарии',
|
||||||
no_comments: 'Нет комментария',
|
no_comments: 'Нет комментария',
|
||||||
delete_field: 'Удалить поле',
|
delete_field: 'Удалить поле',
|
||||||
@@ -160,6 +158,7 @@ export const ru: LanguageTranslation = {
|
|||||||
title: 'Атрибуты индекса',
|
title: 'Атрибуты индекса',
|
||||||
name: 'Имя',
|
name: 'Имя',
|
||||||
unique: 'Уникальный',
|
unique: 'Уникальный',
|
||||||
|
index_type: 'Тип индекса',
|
||||||
delete_index: 'Удалить индекс',
|
delete_index: 'Удалить индекс',
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -176,12 +175,15 @@ export const ru: LanguageTranslation = {
|
|||||||
description: 'Создайте таблицу, чтобы начать',
|
description: 'Создайте таблицу, чтобы начать',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: 'Отношения',
|
refs: 'Ссылки',
|
||||||
filter: 'Фильтр',
|
filter: 'Фильтр',
|
||||||
add_relationship: 'Добавить отношение',
|
|
||||||
collapse: 'Свернуть все',
|
collapse: 'Свернуть все',
|
||||||
|
add_relationship: 'Добавить отношение',
|
||||||
|
relationships: 'Отношения',
|
||||||
|
dependencies: 'Зависимости',
|
||||||
relationship: {
|
relationship: {
|
||||||
|
relationship: 'Отношение',
|
||||||
primary: 'Основная таблица',
|
primary: 'Основная таблица',
|
||||||
foreign: 'Справочная таблица',
|
foreign: 'Справочная таблица',
|
||||||
cardinality: 'Тип множественной связи',
|
cardinality: 'Тип множественной связи',
|
||||||
@@ -191,18 +193,10 @@ export const ru: LanguageTranslation = {
|
|||||||
delete_relationship: 'Удалить',
|
delete_relationship: 'Удалить',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
|
||||||
title: 'Нет отношений',
|
|
||||||
description: 'Создайте связь для соединения таблиц',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dependencies_section: {
|
|
||||||
dependencies: 'Зависимости',
|
|
||||||
filter: 'Фильтр',
|
|
||||||
collapse: 'Свернуть все',
|
|
||||||
dependency: {
|
dependency: {
|
||||||
table: 'Стол',
|
dependency: 'Зависимость',
|
||||||
dependent_table: 'Зависимый вид',
|
table: 'Таблица',
|
||||||
|
dependent_table: 'Зависимое представление',
|
||||||
delete_dependency: 'Удалить',
|
delete_dependency: 'Удалить',
|
||||||
dependency_actions: {
|
dependency_actions: {
|
||||||
title: 'Действия',
|
title: 'Действия',
|
||||||
@@ -210,8 +204,8 @@ export const ru: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: 'Нет зависимостей',
|
title: 'Нет отношений',
|
||||||
description: 'Создайте представление, чтобы начать',
|
description: 'Создайте отношение, чтобы начать',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -252,6 +246,7 @@ export const ru: LanguageTranslation = {
|
|||||||
enum_values: 'Enum Values',
|
enum_values: 'Enum Values',
|
||||||
composite_fields: 'Fields',
|
composite_fields: 'Fields',
|
||||||
no_fields: 'No fields defined',
|
no_fields: 'No fields defined',
|
||||||
|
no_values: 'Значения перечисления не определены',
|
||||||
field_name_placeholder: 'Field name',
|
field_name_placeholder: 'Field name',
|
||||||
field_type_placeholder: 'Select type',
|
field_type_placeholder: 'Select type',
|
||||||
add_field: 'Add Field',
|
add_field: 'Add Field',
|
||||||
@@ -274,7 +269,7 @@ export const ru: LanguageTranslation = {
|
|||||||
show_all: 'Показать все',
|
show_all: 'Показать все',
|
||||||
undo: 'Отменить',
|
undo: 'Отменить',
|
||||||
redo: 'Вернуть',
|
redo: 'Вернуть',
|
||||||
reorder_diagram: 'Переупорядочить диаграмму',
|
reorder_diagram: 'Автоматическая расстановка диаграммы',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||||
custom_type_highlight_tooltip:
|
custom_type_highlight_tooltip:
|
||||||
@@ -312,13 +307,13 @@ export const ru: LanguageTranslation = {
|
|||||||
cancel: 'Отменить',
|
cancel: 'Отменить',
|
||||||
back: 'Назад',
|
back: 'Назад',
|
||||||
import_from_file: 'Импортировать из файла',
|
import_from_file: 'Импортировать из файла',
|
||||||
empty_diagram: 'Пустая диаграмма',
|
empty_diagram: 'Пустая база данных',
|
||||||
continue: 'Продолжить',
|
continue: 'Продолжить',
|
||||||
import: 'Импорт',
|
import: 'Импорт',
|
||||||
},
|
},
|
||||||
|
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: 'Открыть диаграмму',
|
title: 'Открыть базу данных',
|
||||||
description:
|
description:
|
||||||
'Выберите диаграмму, которую нужно открыть, из списка ниже.',
|
'Выберите диаграмму, которую нужно открыть, из списка ниже.',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
@@ -329,6 +324,12 @@ export const ru: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
cancel: 'Отмена',
|
cancel: 'Отмена',
|
||||||
open: 'Открыть',
|
open: 'Открыть',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: 'Открыть',
|
||||||
|
duplicate: 'Дублировать',
|
||||||
|
delete: 'Удалить',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -477,6 +478,7 @@ export const ru: LanguageTranslation = {
|
|||||||
|
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: 'Создать таблицу',
|
new_table: 'Создать таблицу',
|
||||||
|
new_view: 'Новое представление',
|
||||||
new_relationship: 'Создать отношение',
|
new_relationship: 'Создать отношение',
|
||||||
new_area: 'Новая область',
|
new_area: 'Новая область',
|
||||||
},
|
},
|
||||||
@@ -498,6 +500,9 @@ export const ru: LanguageTranslation = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: 'Сменить язык',
|
change_language: 'Сменить язык',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: 'Вкл',
|
||||||
|
off: 'Выкл',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
|||||||
|
|
||||||
export const te: LanguageTranslation = {
|
export const te: LanguageTranslation = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: 'కొత్తది',
|
||||||
|
browse: 'బ్రాఉజ్',
|
||||||
|
tables: 'టేబల్లు',
|
||||||
|
refs: 'సంబంధాలు',
|
||||||
|
areas: 'ప్రదేశాలు',
|
||||||
|
dependencies: 'ఆధారతలు',
|
||||||
|
custom_types: 'కస్టమ్ టైప్స్',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: 'ఫైల్',
|
actions: 'చర్యలు',
|
||||||
new: 'కొత్తది',
|
new: 'కొత్తది...',
|
||||||
open: 'తెరవు',
|
browse: 'బ్రాఉజ్ చేయండి...',
|
||||||
save: 'సేవ్',
|
save: 'సేవ్',
|
||||||
import: 'డేటాబేస్ను దిగుమతి చేసుకోండి',
|
import: 'డేటాబేస్ను దిగుమతి చేసుకోండి',
|
||||||
export_sql: 'SQL ఎగుమతి',
|
export_sql: 'SQL ఎగుమతి',
|
||||||
export_as: 'వగా ఎగుమతి చేయండి',
|
export_as: 'వగా ఎగుమతి చేయండి',
|
||||||
delete_diagram: 'చిత్రాన్ని తొలగించండి',
|
delete_diagram: 'తొలగించండి',
|
||||||
exit: 'నిష్క్రమించు',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: 'సవరించు',
|
edit: 'సవరించు',
|
||||||
@@ -29,6 +37,7 @@ export const te: LanguageTranslation = {
|
|||||||
show_field_attributes: 'ఫీల్డ్ గుణాలను చూపించు',
|
show_field_attributes: 'ఫీల్డ్ గుణాలను చూపించు',
|
||||||
hide_field_attributes: 'ఫీల్డ్ గుణాలను దాచండి',
|
hide_field_attributes: 'ఫీల్డ్ గుణాలను దాచండి',
|
||||||
zoom_on_scroll: 'స్క్రోల్పై జూమ్',
|
zoom_on_scroll: 'స్క్రోల్పై జూమ్',
|
||||||
|
show_views: 'డేటాబేస్ వ్యూలు',
|
||||||
theme: 'థీమ్',
|
theme: 'థీమ్',
|
||||||
show_dependencies: 'ఆధారాలు చూపించండి',
|
show_dependencies: 'ఆధారాలు చూపించండి',
|
||||||
hide_dependencies: 'ఆధారాలను దాచండి',
|
hide_dependencies: 'ఆధారాలను దాచండి',
|
||||||
@@ -66,22 +75,13 @@ export const te: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: 'చిత్రాన్ని పునఃసరిచేయండి',
|
title: 'చిత్రాన్ని స్వయంచాలకంగా అమర్చండి',
|
||||||
description:
|
description:
|
||||||
'ఈ చర్య చిత్రంలోని అన్ని పట్టికలను పునఃస్థాపిస్తుంది. మీరు కొనసాగించాలనుకుంటున్నారా?',
|
'ఈ చర్య చిత్రంలోని అన్ని పట్టికలను పునఃస్థాపిస్తుంది. మీరు కొనసాగించాలనుకుంటున్నారా?',
|
||||||
reorder: 'పునఃసరిచేయండి',
|
reorder: 'స్వయంచాలకంగా అమర్చండి',
|
||||||
cancel: 'రద్దు',
|
cancel: 'రద్దు',
|
||||||
},
|
},
|
||||||
|
|
||||||
multiple_schemas_alert: {
|
|
||||||
title: 'బహుళ స్కీమాలు',
|
|
||||||
description:
|
|
||||||
'{{schemasCount}} స్కీమాలు ఈ చిత్రంలో ఉన్నాయి. ప్రస్తుత స్కీమాలు: {{formattedSchemas}}.',
|
|
||||||
// TODO: Translate
|
|
||||||
show_me: 'Show me',
|
|
||||||
none: 'ఎదరికాదు',
|
|
||||||
},
|
|
||||||
|
|
||||||
copy_to_clipboard_toast: {
|
copy_to_clipboard_toast: {
|
||||||
unsupported: {
|
unsupported: {
|
||||||
title: 'కాపీ విఫలమైంది',
|
title: 'కాపీ విఫలమైంది',
|
||||||
@@ -116,14 +116,11 @@ export const te: LanguageTranslation = {
|
|||||||
copied: 'కాపీ చేయబడింది!',
|
copied: 'కాపీ చేయబడింది!',
|
||||||
|
|
||||||
side_panel: {
|
side_panel: {
|
||||||
schema: 'స్కీమా:',
|
|
||||||
filter_by_schema: 'స్కీమా ద్వారా ఫిల్టర్ చేయండి',
|
|
||||||
search_schema: 'స్కీమా కోసం శోధించండి...',
|
|
||||||
no_schemas_found: 'ఏ స్కీమాలు కూడా కనుగొనబడలేదు.',
|
|
||||||
view_all_options: 'అన్ని ఎంపికలను చూడండి...',
|
view_all_options: 'అన్ని ఎంపికలను చూడండి...',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: 'పట్టికలు',
|
tables: 'పట్టికలు',
|
||||||
add_table: 'పట్టికను జోడించు',
|
add_table: 'పట్టికను జోడించు',
|
||||||
|
add_view: 'వ్యూ జోడించండి',
|
||||||
filter: 'ఫిల్టర్',
|
filter: 'ఫిల్టర్',
|
||||||
collapse: 'అన్ని కూల్ చేయి',
|
collapse: 'అన్ని కూల్ చేయి',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
@@ -149,6 +146,7 @@ export const te: LanguageTranslation = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: 'ఫీల్డ్ గుణాలు',
|
title: 'ఫీల్డ్ గుణాలు',
|
||||||
unique: 'అద్వితీయ',
|
unique: 'అద్వితీయ',
|
||||||
|
auto_increment: 'ఆటో ఇంక్రిమెంట్',
|
||||||
comments: 'వ్యాఖ్యలు',
|
comments: 'వ్యాఖ్యలు',
|
||||||
no_comments: 'వ్యాఖ్యలు లేవు',
|
no_comments: 'వ్యాఖ్యలు లేవు',
|
||||||
delete_field: 'ఫీల్డ్ తొలగించు',
|
delete_field: 'ఫీల్డ్ తొలగించు',
|
||||||
@@ -164,6 +162,7 @@ export const te: LanguageTranslation = {
|
|||||||
title: 'ఇండెక్స్ గుణాలు',
|
title: 'ఇండెక్స్ గుణాలు',
|
||||||
name: 'పేరు',
|
name: 'పేరు',
|
||||||
unique: 'అద్వితీయ',
|
unique: 'అద్వితీయ',
|
||||||
|
index_type: 'ఇండెక్స్ రకం',
|
||||||
delete_index: 'ఇండెక్స్ తొలగించు',
|
delete_index: 'ఇండెక్స్ తొలగించు',
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -181,12 +180,15 @@ export const te: LanguageTranslation = {
|
|||||||
description: 'ప్రారంభించడానికి ఒక పట్టిక సృష్టించండి',
|
description: 'ప్రారంభించడానికి ఒక పట్టిక సృష్టించండి',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: 'సంబంధాలు',
|
refs: 'Refs',
|
||||||
filter: 'ఫిల్టర్',
|
filter: 'ఫిల్టర్',
|
||||||
add_relationship: 'సంబంధం జోడించు',
|
|
||||||
collapse: 'అన్ని కూల్ చేయి',
|
collapse: 'అన్ని కూల్ చేయి',
|
||||||
|
add_relationship: 'సంబంధం జోడించు',
|
||||||
|
relationships: 'సంబంధాలు',
|
||||||
|
dependencies: 'ఆధారాలు',
|
||||||
relationship: {
|
relationship: {
|
||||||
|
relationship: 'సంబంధం',
|
||||||
primary: 'ప్రాథమిక పట్టిక',
|
primary: 'ప్రాథమిక పట్టిక',
|
||||||
foreign: 'సూచించబడిన పట్టిక',
|
foreign: 'సూచించబడిన పట్టిక',
|
||||||
cardinality: 'కార్డినాలిటీ',
|
cardinality: 'కార్డినాలిటీ',
|
||||||
@@ -196,16 +198,8 @@ export const te: LanguageTranslation = {
|
|||||||
delete_relationship: 'సంబంధం తొలగించు',
|
delete_relationship: 'సంబంధం తొలగించు',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
|
||||||
title: 'సంబంధాలు లేవు',
|
|
||||||
description: 'పట్టికలను అనుసంధించడానికి సంబంధం సృష్టించండి',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dependencies_section: {
|
|
||||||
dependencies: 'ఆధారాలు',
|
|
||||||
filter: 'ఫిల్టర్',
|
|
||||||
collapse: 'అన్ని కూల్ చేయి',
|
|
||||||
dependency: {
|
dependency: {
|
||||||
|
dependency: 'ఆధారం',
|
||||||
table: 'పట్టిక',
|
table: 'పట్టిక',
|
||||||
dependent_table: 'ఆధారిత వీక్షణ',
|
dependent_table: 'ఆధారిత వీక్షణ',
|
||||||
delete_dependency: 'ఆధారాన్ని తొలగించు',
|
delete_dependency: 'ఆధారాన్ని తొలగించు',
|
||||||
@@ -215,8 +209,8 @@ export const te: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: 'ఆధారాలు లేవు',
|
title: 'సంబంధాలు లేవు',
|
||||||
description: 'ప్రారంభించడానికి ఒక వీక్షణ సృష్టించండి',
|
description: 'ప్రారంభించడానికి ఒక సంబంధం సృష్టించండి',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -256,6 +250,7 @@ export const te: LanguageTranslation = {
|
|||||||
enum_values: 'Enum Values',
|
enum_values: 'Enum Values',
|
||||||
composite_fields: 'Fields',
|
composite_fields: 'Fields',
|
||||||
no_fields: 'No fields defined',
|
no_fields: 'No fields defined',
|
||||||
|
no_values: 'ఏ enum విలువలు నిర్వచించబడలేదు',
|
||||||
field_name_placeholder: 'Field name',
|
field_name_placeholder: 'Field name',
|
||||||
field_type_placeholder: 'Select type',
|
field_type_placeholder: 'Select type',
|
||||||
add_field: 'Add Field',
|
add_field: 'Add Field',
|
||||||
@@ -278,7 +273,7 @@ export const te: LanguageTranslation = {
|
|||||||
show_all: 'అన్ని చూపించు',
|
show_all: 'అన్ని చూపించు',
|
||||||
undo: 'తిరిగి చేయు',
|
undo: 'తిరిగి చేయు',
|
||||||
redo: 'మరలా చేయు',
|
redo: 'మరలా చేయు',
|
||||||
reorder_diagram: 'చిత్రాన్ని పునఃసరిచేయండి',
|
reorder_diagram: 'చిత్రాన్ని స్వయంచాలకంగా అమర్చండి',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||||
custom_type_highlight_tooltip:
|
custom_type_highlight_tooltip:
|
||||||
@@ -317,13 +312,13 @@ export const te: LanguageTranslation = {
|
|||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
import_from_file: 'Import from File',
|
import_from_file: 'Import from File',
|
||||||
back: 'తిరుగు',
|
back: 'తిరుగు',
|
||||||
empty_diagram: 'ఖాళీ చిత్రము',
|
empty_diagram: 'ఖాళీ డేటాబేస్',
|
||||||
continue: 'కొనసాగించు',
|
continue: 'కొనసాగించు',
|
||||||
import: 'డిగుమతి',
|
import: 'డిగుమతి',
|
||||||
},
|
},
|
||||||
|
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: 'చిత్రం తెరవండి',
|
title: 'డేటాబేస్ తెరవండి',
|
||||||
description: 'కింద ఉన్న జాబితా నుండి చిత్రాన్ని ఎంచుకోండి.',
|
description: 'కింద ఉన్న జాబితా నుండి చిత్రాన్ని ఎంచుకోండి.',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
name: 'పేరు',
|
name: 'పేరు',
|
||||||
@@ -333,6 +328,12 @@ export const te: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
cancel: 'రద్దు',
|
cancel: 'రద్దు',
|
||||||
open: 'తెరవు',
|
open: 'తెరవు',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: 'తెరవు',
|
||||||
|
duplicate: 'నకలు',
|
||||||
|
delete: 'తొలగించు',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -485,6 +486,7 @@ export const te: LanguageTranslation = {
|
|||||||
|
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: 'కొత్త పట్టిక',
|
new_table: 'కొత్త పట్టిక',
|
||||||
|
new_view: 'కొత్త వ్యూ',
|
||||||
new_relationship: 'కొత్త సంబంధం',
|
new_relationship: 'కొత్త సంబంధం',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
new_area: 'New Area',
|
new_area: 'New Area',
|
||||||
@@ -508,6 +510,9 @@ export const te: LanguageTranslation = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: 'భాష మార్చు',
|
change_language: 'భాష మార్చు',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: 'ఆన్',
|
||||||
|
off: 'ఆఫ్',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
|||||||
|
|
||||||
export const tr: LanguageTranslation = {
|
export const tr: LanguageTranslation = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: 'Yeni',
|
||||||
|
browse: 'Gözat',
|
||||||
|
tables: 'Tablolar',
|
||||||
|
refs: 'Refs',
|
||||||
|
areas: 'Alanlar',
|
||||||
|
dependencies: 'Bağımlılıklar',
|
||||||
|
custom_types: 'Özel Tipler',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: 'Dosya',
|
actions: 'Eylemler',
|
||||||
new: 'Yeni',
|
new: 'Yeni...',
|
||||||
open: 'Aç',
|
browse: 'Gözat...',
|
||||||
save: 'Kaydet',
|
save: 'Kaydet',
|
||||||
import: 'Veritabanı İçe Aktar',
|
import: 'Veritabanı İçe Aktar',
|
||||||
export_sql: 'SQL Olarak Dışa Aktar',
|
export_sql: 'SQL Olarak Dışa Aktar',
|
||||||
export_as: 'Olarak Dışa Aktar',
|
export_as: 'Olarak Dışa Aktar',
|
||||||
delete_diagram: 'Diyagramı Sil',
|
delete_diagram: 'Sil',
|
||||||
exit: 'Çıkış',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: 'Düzenle',
|
edit: 'Düzenle',
|
||||||
@@ -29,6 +37,7 @@ export const tr: LanguageTranslation = {
|
|||||||
show_field_attributes: 'Alan Özelliklerini Göster',
|
show_field_attributes: 'Alan Özelliklerini Göster',
|
||||||
hide_field_attributes: 'Alan Özelliklerini Gizle',
|
hide_field_attributes: 'Alan Özelliklerini Gizle',
|
||||||
zoom_on_scroll: 'Kaydırarak Yakınlaştır',
|
zoom_on_scroll: 'Kaydırarak Yakınlaştır',
|
||||||
|
show_views: 'Veritabanı Görünümleri',
|
||||||
theme: 'Tema',
|
theme: 'Tema',
|
||||||
show_dependencies: 'Bağımlılıkları Göster',
|
show_dependencies: 'Bağımlılıkları Göster',
|
||||||
hide_dependencies: 'Bağımlılıkları Gizle',
|
hide_dependencies: 'Bağımlılıkları Gizle',
|
||||||
@@ -66,22 +75,13 @@ export const tr: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: 'Diyagramı Yeniden Sırala',
|
title: 'Diyagramı Otomatik Düzenle',
|
||||||
description:
|
description:
|
||||||
'Bu işlem tüm tabloları yeniden düzenleyecektir. Devam etmek istiyor musunuz?',
|
'Bu işlem tüm tabloları yeniden düzenleyecektir. Devam etmek istiyor musunuz?',
|
||||||
reorder: 'Yeniden Sırala',
|
reorder: 'Otomatik Düzenle',
|
||||||
cancel: 'İptal',
|
cancel: 'İptal',
|
||||||
},
|
},
|
||||||
|
|
||||||
multiple_schemas_alert: {
|
|
||||||
title: 'Birden Fazla Şema',
|
|
||||||
description:
|
|
||||||
'Bu diyagramda {{schemasCount}} şema var. Şu anda görüntülenen: {{formattedSchemas}}.',
|
|
||||||
// TODO: Translate
|
|
||||||
show_me: 'Show me',
|
|
||||||
none: 'yok',
|
|
||||||
},
|
|
||||||
|
|
||||||
copy_to_clipboard_toast: {
|
copy_to_clipboard_toast: {
|
||||||
unsupported: {
|
unsupported: {
|
||||||
title: 'Kopyalama başarısız',
|
title: 'Kopyalama başarısız',
|
||||||
@@ -115,14 +115,11 @@ export const tr: LanguageTranslation = {
|
|||||||
copy_to_clipboard: 'Panoya Kopyala',
|
copy_to_clipboard: 'Panoya Kopyala',
|
||||||
copied: 'Kopyalandı!',
|
copied: 'Kopyalandı!',
|
||||||
side_panel: {
|
side_panel: {
|
||||||
schema: 'Şema:',
|
|
||||||
filter_by_schema: 'Şemaya Göre Filtrele',
|
|
||||||
search_schema: 'Şema ara...',
|
|
||||||
no_schemas_found: 'Şema bulunamadı.',
|
|
||||||
view_all_options: 'Tüm Seçenekleri Gör...',
|
view_all_options: 'Tüm Seçenekleri Gör...',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: 'Tablolar',
|
tables: 'Tablolar',
|
||||||
add_table: 'Tablo Ekle',
|
add_table: 'Tablo Ekle',
|
||||||
|
add_view: 'Görünüm Ekle',
|
||||||
filter: 'Filtrele',
|
filter: 'Filtrele',
|
||||||
collapse: 'Hepsini Daralt',
|
collapse: 'Hepsini Daralt',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
@@ -148,6 +145,7 @@ export const tr: LanguageTranslation = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: 'Alan Özellikleri',
|
title: 'Alan Özellikleri',
|
||||||
unique: 'Tekil',
|
unique: 'Tekil',
|
||||||
|
auto_increment: 'Otomatik Artış',
|
||||||
comments: 'Yorumlar',
|
comments: 'Yorumlar',
|
||||||
no_comments: 'Yorum yok',
|
no_comments: 'Yorum yok',
|
||||||
delete_field: 'Alanı Sil',
|
delete_field: 'Alanı Sil',
|
||||||
@@ -163,6 +161,7 @@ export const tr: LanguageTranslation = {
|
|||||||
title: 'İndeks Özellikleri',
|
title: 'İndeks Özellikleri',
|
||||||
name: 'Ad',
|
name: 'Ad',
|
||||||
unique: 'Tekil',
|
unique: 'Tekil',
|
||||||
|
index_type: 'İndeks Türü',
|
||||||
delete_index: 'İndeksi Sil',
|
delete_index: 'İndeksi Sil',
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -180,12 +179,15 @@ export const tr: LanguageTranslation = {
|
|||||||
description: 'Başlamak için bir tablo oluşturun',
|
description: 'Başlamak için bir tablo oluşturun',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: 'İlişkiler',
|
refs: 'Refs',
|
||||||
filter: 'Filtrele',
|
filter: 'Filtrele',
|
||||||
add_relationship: 'İlişki Ekle',
|
|
||||||
collapse: 'Hepsini Daralt',
|
collapse: 'Hepsini Daralt',
|
||||||
|
add_relationship: 'İlişki Ekle',
|
||||||
|
relationships: 'İlişkiler',
|
||||||
|
dependencies: 'Bağımlılıklar',
|
||||||
relationship: {
|
relationship: {
|
||||||
|
relationship: 'İlişki',
|
||||||
primary: 'Birincil Tablo',
|
primary: 'Birincil Tablo',
|
||||||
foreign: 'Referans Tablo',
|
foreign: 'Referans Tablo',
|
||||||
cardinality: 'Kardinalite',
|
cardinality: 'Kardinalite',
|
||||||
@@ -195,16 +197,8 @@ export const tr: LanguageTranslation = {
|
|||||||
delete_relationship: 'Sil',
|
delete_relationship: 'Sil',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
|
||||||
title: 'İlişki yok',
|
|
||||||
description: 'Tabloları bağlamak için bir ilişki oluşturun',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dependencies_section: {
|
|
||||||
dependencies: 'Bağımlılıklar',
|
|
||||||
filter: 'Filtrele',
|
|
||||||
collapse: 'Hepsini Daralt',
|
|
||||||
dependency: {
|
dependency: {
|
||||||
|
dependency: 'Bağımlılık',
|
||||||
table: 'Tablo',
|
table: 'Tablo',
|
||||||
dependent_table: 'Bağımlı Görünüm',
|
dependent_table: 'Bağımlı Görünüm',
|
||||||
delete_dependency: 'Sil',
|
delete_dependency: 'Sil',
|
||||||
@@ -214,8 +208,8 @@ export const tr: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: 'Bağımlılık yok',
|
title: 'İlişki yok',
|
||||||
description: 'Başlamak için bir görünüm oluşturun',
|
description: 'Başlamak için bir ilişki oluşturun',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -255,6 +249,7 @@ export const tr: LanguageTranslation = {
|
|||||||
enum_values: 'Enum Values',
|
enum_values: 'Enum Values',
|
||||||
composite_fields: 'Fields',
|
composite_fields: 'Fields',
|
||||||
no_fields: 'No fields defined',
|
no_fields: 'No fields defined',
|
||||||
|
no_values: 'Tanımlanmış enum değeri yok',
|
||||||
field_name_placeholder: 'Field name',
|
field_name_placeholder: 'Field name',
|
||||||
field_type_placeholder: 'Select type',
|
field_type_placeholder: 'Select type',
|
||||||
add_field: 'Add Field',
|
add_field: 'Add Field',
|
||||||
@@ -276,7 +271,7 @@ export const tr: LanguageTranslation = {
|
|||||||
show_all: 'Hepsini Gör',
|
show_all: 'Hepsini Gör',
|
||||||
undo: 'Geri Al',
|
undo: 'Geri Al',
|
||||||
redo: 'Yinele',
|
redo: 'Yinele',
|
||||||
reorder_diagram: 'Diyagramı Yeniden Sırala',
|
reorder_diagram: 'Diyagramı Otomatik Düzenle',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||||
custom_type_highlight_tooltip:
|
custom_type_highlight_tooltip:
|
||||||
@@ -313,12 +308,12 @@ export const tr: LanguageTranslation = {
|
|||||||
import_from_file: 'Import from File',
|
import_from_file: 'Import from File',
|
||||||
cancel: 'İptal',
|
cancel: 'İptal',
|
||||||
back: 'Geri',
|
back: 'Geri',
|
||||||
empty_diagram: 'Boş diyagram',
|
empty_diagram: 'Boş veritabanı',
|
||||||
continue: 'Devam',
|
continue: 'Devam',
|
||||||
import: 'İçe Aktar',
|
import: 'İçe Aktar',
|
||||||
},
|
},
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: 'Diyagramı Aç',
|
title: 'Veritabanı Aç',
|
||||||
description: 'Aşağıdaki listeden açmak için bir diyagram seçin.',
|
description: 'Aşağıdaki listeden açmak için bir diyagram seçin.',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
name: 'Ad',
|
name: 'Ad',
|
||||||
@@ -328,6 +323,12 @@ export const tr: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
cancel: 'İptal',
|
cancel: 'İptal',
|
||||||
open: 'Aç',
|
open: 'Aç',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: 'Aç',
|
||||||
|
duplicate: 'Kopyala',
|
||||||
|
delete: 'Sil',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -470,6 +471,7 @@ export const tr: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: 'Yeni Tablo',
|
new_table: 'Yeni Tablo',
|
||||||
|
new_view: 'Yeni Görünüm',
|
||||||
new_relationship: 'Yeni İlişki',
|
new_relationship: 'Yeni İlişki',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
new_area: 'New Area',
|
new_area: 'New Area',
|
||||||
@@ -492,6 +494,9 @@ export const tr: LanguageTranslation = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: 'Dil',
|
change_language: 'Dil',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: 'Açık',
|
||||||
|
off: 'Kapalı',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
|||||||
|
|
||||||
export const uk: LanguageTranslation = {
|
export const uk: LanguageTranslation = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: 'Нова',
|
||||||
|
browse: 'Огляд',
|
||||||
|
tables: 'Таблиці',
|
||||||
|
refs: 'Зв’язки',
|
||||||
|
areas: 'Області',
|
||||||
|
dependencies: 'Залежності',
|
||||||
|
custom_types: 'Користувацькі типи',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: 'Файл',
|
actions: 'Дії',
|
||||||
new: 'Новий',
|
new: 'Нова...',
|
||||||
open: 'Відкрити',
|
browse: 'Огляд...',
|
||||||
save: 'Зберегти',
|
save: 'Зберегти',
|
||||||
import: 'Імпорт бази даних',
|
import: 'Імпорт бази даних',
|
||||||
export_sql: 'Експорт SQL',
|
export_sql: 'Експорт SQL',
|
||||||
export_as: 'Експортувати як',
|
export_as: 'Експортувати як',
|
||||||
delete_diagram: 'Видалити діаграму',
|
delete_diagram: 'Видалити',
|
||||||
exit: 'Вийти',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: 'Редагувати',
|
edit: 'Редагувати',
|
||||||
@@ -29,6 +37,7 @@ export const uk: LanguageTranslation = {
|
|||||||
show_field_attributes: 'Показати атрибути полів',
|
show_field_attributes: 'Показати атрибути полів',
|
||||||
hide_field_attributes: 'Приховати атрибути полів',
|
hide_field_attributes: 'Приховати атрибути полів',
|
||||||
zoom_on_scroll: 'Масштабувати прокручуванням',
|
zoom_on_scroll: 'Масштабувати прокручуванням',
|
||||||
|
show_views: 'Представлення бази даних',
|
||||||
theme: 'Тема',
|
theme: 'Тема',
|
||||||
show_dependencies: 'Показати залежності',
|
show_dependencies: 'Показати залежності',
|
||||||
hide_dependencies: 'Приховати залежності',
|
hide_dependencies: 'Приховати залежності',
|
||||||
@@ -64,22 +73,13 @@ export const uk: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: 'Перевпорядкувати діаграму',
|
title: 'Автоматичне розміщення діаграми',
|
||||||
description:
|
description:
|
||||||
'Ця дія перевпорядкує всі таблиці на діаграмі. Хочете продовжити?',
|
'Ця дія перевпорядкує всі таблиці на діаграмі. Хочете продовжити?',
|
||||||
reorder: 'Перевпорядкувати',
|
reorder: 'Автоматичне розміщення',
|
||||||
cancel: 'Скасувати',
|
cancel: 'Скасувати',
|
||||||
},
|
},
|
||||||
|
|
||||||
multiple_schemas_alert: {
|
|
||||||
title: 'Кілька схем',
|
|
||||||
description:
|
|
||||||
'{{schemasCount}} схеми на цій діаграмі. Зараз відображається: {{formattedSchemas}}.',
|
|
||||||
// TODO: Translate
|
|
||||||
show_me: 'Show me',
|
|
||||||
none: 'немає',
|
|
||||||
},
|
|
||||||
|
|
||||||
copy_to_clipboard_toast: {
|
copy_to_clipboard_toast: {
|
||||||
unsupported: {
|
unsupported: {
|
||||||
title: 'Помилка копіювання',
|
title: 'Помилка копіювання',
|
||||||
@@ -114,14 +114,11 @@ export const uk: LanguageTranslation = {
|
|||||||
copied: 'Скопійовано!',
|
copied: 'Скопійовано!',
|
||||||
|
|
||||||
side_panel: {
|
side_panel: {
|
||||||
schema: 'Схема:',
|
|
||||||
filter_by_schema: 'Фільтрувати за схемою',
|
|
||||||
search_schema: 'Пошук схеми…',
|
|
||||||
no_schemas_found: 'Схеми не знайдено.',
|
|
||||||
view_all_options: 'Переглянути всі параметри…',
|
view_all_options: 'Переглянути всі параметри…',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: 'Таблиці',
|
tables: 'Таблиці',
|
||||||
add_table: 'Додати таблицю',
|
add_table: 'Додати таблицю',
|
||||||
|
add_view: 'Додати представлення',
|
||||||
filter: 'Фільтр',
|
filter: 'Фільтр',
|
||||||
collapse: 'Згорнути все',
|
collapse: 'Згорнути все',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
@@ -147,6 +144,7 @@ export const uk: LanguageTranslation = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: 'Атрибути полів',
|
title: 'Атрибути полів',
|
||||||
unique: 'Унікальне',
|
unique: 'Унікальне',
|
||||||
|
auto_increment: 'Автоінкремент',
|
||||||
comments: 'Коментарі',
|
comments: 'Коментарі',
|
||||||
no_comments: 'Немає коментарів',
|
no_comments: 'Немає коментарів',
|
||||||
delete_field: 'Видалити поле',
|
delete_field: 'Видалити поле',
|
||||||
@@ -162,6 +160,7 @@ export const uk: LanguageTranslation = {
|
|||||||
title: 'Атрибути індексу',
|
title: 'Атрибути індексу',
|
||||||
name: 'Назва індекса',
|
name: 'Назва індекса',
|
||||||
unique: 'Унікальний',
|
unique: 'Унікальний',
|
||||||
|
index_type: 'Тип індексу',
|
||||||
delete_index: 'Видалити індекс',
|
delete_index: 'Видалити індекс',
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -178,12 +177,15 @@ export const uk: LanguageTranslation = {
|
|||||||
description: 'Щоб почати, створіть таблицю',
|
description: 'Щоб почати, створіть таблицю',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: 'Звʼязки',
|
refs: 'Refs',
|
||||||
filter: 'Фільтр',
|
filter: 'Фільтр',
|
||||||
add_relationship: 'Додати звʼязок',
|
|
||||||
collapse: 'Згорнути все',
|
collapse: 'Згорнути все',
|
||||||
|
add_relationship: 'Додати звʼязок',
|
||||||
|
relationships: 'Звʼязки',
|
||||||
|
dependencies: 'Залежності',
|
||||||
relationship: {
|
relationship: {
|
||||||
|
relationship: 'Звʼязок',
|
||||||
primary: 'Первинна таблиця',
|
primary: 'Первинна таблиця',
|
||||||
foreign: 'Посилання на таблицю',
|
foreign: 'Посилання на таблицю',
|
||||||
cardinality: 'Звʼязок',
|
cardinality: 'Звʼязок',
|
||||||
@@ -193,16 +195,8 @@ export const uk: LanguageTranslation = {
|
|||||||
delete_relationship: 'Видалити',
|
delete_relationship: 'Видалити',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
|
||||||
title: 'Звʼязків немає',
|
|
||||||
description: 'Створіть звʼязок для зʼєднання таблиць',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dependencies_section: {
|
|
||||||
dependencies: 'Залежності',
|
|
||||||
filter: 'Фільтр',
|
|
||||||
collapse: 'Згорнути все',
|
|
||||||
dependency: {
|
dependency: {
|
||||||
|
dependency: 'Залежність',
|
||||||
table: 'Таблиця',
|
table: 'Таблиця',
|
||||||
dependent_table: 'Залежне подання',
|
dependent_table: 'Залежне подання',
|
||||||
delete_dependency: 'Видалити',
|
delete_dependency: 'Видалити',
|
||||||
@@ -212,8 +206,8 @@ export const uk: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: 'Жодних залежностей',
|
title: 'Жодних зв’язків',
|
||||||
description: 'Створіть подання, щоб почати',
|
description: 'Створіть зв’язок, щоб почати',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -253,6 +247,7 @@ export const uk: LanguageTranslation = {
|
|||||||
enum_values: 'Enum Values',
|
enum_values: 'Enum Values',
|
||||||
composite_fields: 'Fields',
|
composite_fields: 'Fields',
|
||||||
no_fields: 'No fields defined',
|
no_fields: 'No fields defined',
|
||||||
|
no_values: 'Значення переліку не визначені',
|
||||||
field_name_placeholder: 'Field name',
|
field_name_placeholder: 'Field name',
|
||||||
field_type_placeholder: 'Select type',
|
field_type_placeholder: 'Select type',
|
||||||
add_field: 'Add Field',
|
add_field: 'Add Field',
|
||||||
@@ -275,7 +270,7 @@ export const uk: LanguageTranslation = {
|
|||||||
show_all: 'Показати все',
|
show_all: 'Показати все',
|
||||||
undo: 'Скасувати',
|
undo: 'Скасувати',
|
||||||
redo: 'Повторити',
|
redo: 'Повторити',
|
||||||
reorder_diagram: 'Перевпорядкувати діаграму',
|
reorder_diagram: 'Автоматичне розміщення діаграми',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||||
custom_type_highlight_tooltip:
|
custom_type_highlight_tooltip:
|
||||||
@@ -313,13 +308,13 @@ export const uk: LanguageTranslation = {
|
|||||||
cancel: 'Скасувати',
|
cancel: 'Скасувати',
|
||||||
back: 'Назад',
|
back: 'Назад',
|
||||||
import_from_file: 'Імпортувати з файлу',
|
import_from_file: 'Імпортувати з файлу',
|
||||||
empty_diagram: 'Порожня діаграма',
|
empty_diagram: 'Порожня база даних',
|
||||||
continue: 'Продовжити',
|
continue: 'Продовжити',
|
||||||
import: 'Імпорт',
|
import: 'Імпорт',
|
||||||
},
|
},
|
||||||
|
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: 'Відкрити діаграму',
|
title: 'Відкрити базу даних',
|
||||||
description:
|
description:
|
||||||
'Виберіть діаграму, яку потрібно відкрити, зі списку нижче.',
|
'Виберіть діаграму, яку потрібно відкрити, зі списку нижче.',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
@@ -330,6 +325,12 @@ export const uk: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
cancel: 'Скасувати',
|
cancel: 'Скасувати',
|
||||||
open: 'Відкрити',
|
open: 'Відкрити',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: 'Відкрити',
|
||||||
|
duplicate: 'Дублювати',
|
||||||
|
delete: 'Видалити',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -476,6 +477,7 @@ export const uk: LanguageTranslation = {
|
|||||||
|
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: 'Нова таблиця',
|
new_table: 'Нова таблиця',
|
||||||
|
new_view: 'Нове представлення',
|
||||||
new_relationship: 'Новий звʼязок',
|
new_relationship: 'Новий звʼязок',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
new_area: 'New Area',
|
new_area: 'New Area',
|
||||||
@@ -497,6 +499,9 @@ export const uk: LanguageTranslation = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: 'Мова',
|
change_language: 'Мова',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: 'Увімк',
|
||||||
|
off: 'Вимк',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
|||||||
|
|
||||||
export const vi: LanguageTranslation = {
|
export const vi: LanguageTranslation = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: 'Mới',
|
||||||
|
browse: 'Duyệt',
|
||||||
|
tables: 'Bảng',
|
||||||
|
refs: 'Refs',
|
||||||
|
areas: 'Khu vực',
|
||||||
|
dependencies: 'Phụ thuộc',
|
||||||
|
custom_types: 'Kiểu tùy chỉnh',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: 'Tệp',
|
actions: 'Hành động',
|
||||||
new: 'Tạo mới',
|
new: 'Mới...',
|
||||||
open: 'Mở',
|
browse: 'Duyệt...',
|
||||||
save: 'Lưu',
|
save: 'Lưu',
|
||||||
import: 'Nhập cơ sở dữ liệu',
|
import: 'Nhập cơ sở dữ liệu',
|
||||||
export_sql: 'Xuất SQL',
|
export_sql: 'Xuất SQL',
|
||||||
export_as: 'Xuất thành',
|
export_as: 'Xuất thành',
|
||||||
delete_diagram: 'Xóa sơ đồ',
|
delete_diagram: 'Xóa',
|
||||||
exit: 'Thoát',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: 'Sửa',
|
edit: 'Sửa',
|
||||||
@@ -29,6 +37,7 @@ export const vi: LanguageTranslation = {
|
|||||||
show_field_attributes: 'Hiển thị thuộc tính trường',
|
show_field_attributes: 'Hiển thị thuộc tính trường',
|
||||||
hide_field_attributes: 'Ẩn thuộc tính trường',
|
hide_field_attributes: 'Ẩn thuộc tính trường',
|
||||||
zoom_on_scroll: 'Thu phóng khi cuộn',
|
zoom_on_scroll: 'Thu phóng khi cuộn',
|
||||||
|
show_views: 'Chế độ xem Cơ sở dữ liệu',
|
||||||
theme: 'Chủ đề',
|
theme: 'Chủ đề',
|
||||||
show_dependencies: 'Hiển thị các phụ thuộc',
|
show_dependencies: 'Hiển thị các phụ thuộc',
|
||||||
hide_dependencies: 'Ẩn các phụ thuộc',
|
hide_dependencies: 'Ẩn các phụ thuộc',
|
||||||
@@ -65,22 +74,13 @@ export const vi: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: 'Sắp xếp lại sơ đồ',
|
title: 'Tự động sắp xếp sơ đồ',
|
||||||
description:
|
description:
|
||||||
'Hành động này sẽ sắp xếp lại tất cả các bảng trong sơ đồ. Bạn có muốn tiếp tục không?',
|
'Hành động này sẽ sắp xếp lại tất cả các bảng trong sơ đồ. Bạn có muốn tiếp tục không?',
|
||||||
reorder: 'Sắp xếp',
|
reorder: 'Tự động sắp xếp',
|
||||||
cancel: 'Hủy',
|
cancel: 'Hủy',
|
||||||
},
|
},
|
||||||
|
|
||||||
multiple_schemas_alert: {
|
|
||||||
title: 'Có nhiều lược đồ',
|
|
||||||
description:
|
|
||||||
'Có {{schemasCount}} lược đồ trong sơ đồ này. Hiện đang hiển thị: {{formattedSchemas}}.',
|
|
||||||
// TODO: Translate
|
|
||||||
show_me: 'Show me',
|
|
||||||
none: 'không có',
|
|
||||||
},
|
|
||||||
|
|
||||||
copy_to_clipboard_toast: {
|
copy_to_clipboard_toast: {
|
||||||
unsupported: {
|
unsupported: {
|
||||||
title: 'Sao chép thất bại',
|
title: 'Sao chép thất bại',
|
||||||
@@ -115,14 +115,11 @@ export const vi: LanguageTranslation = {
|
|||||||
copied: 'Đã sao chép!',
|
copied: 'Đã sao chép!',
|
||||||
|
|
||||||
side_panel: {
|
side_panel: {
|
||||||
schema: 'Lược đồ:',
|
|
||||||
filter_by_schema: 'Lọc bởi lược đồ',
|
|
||||||
search_schema: 'Tìm kiếm lược đồ...',
|
|
||||||
no_schemas_found: 'Không tìm thấy lược đồ.',
|
|
||||||
view_all_options: 'Xem tất cả tùy chọn...',
|
view_all_options: 'Xem tất cả tùy chọn...',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: 'Bảng',
|
tables: 'Bảng',
|
||||||
add_table: 'Thêm bảng',
|
add_table: 'Thêm bảng',
|
||||||
|
add_view: 'Thêm Chế độ xem',
|
||||||
filter: 'Lọc',
|
filter: 'Lọc',
|
||||||
collapse: 'Thu gọn tất cả',
|
collapse: 'Thu gọn tất cả',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
@@ -148,6 +145,7 @@ export const vi: LanguageTranslation = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: 'Thuộc tính trường',
|
title: 'Thuộc tính trường',
|
||||||
unique: 'Giá trị duy nhất',
|
unique: 'Giá trị duy nhất',
|
||||||
|
auto_increment: 'Tự động tăng',
|
||||||
comments: 'Bình luận',
|
comments: 'Bình luận',
|
||||||
no_comments: 'Không có bình luận',
|
no_comments: 'Không có bình luận',
|
||||||
delete_field: 'Xóa trường',
|
delete_field: 'Xóa trường',
|
||||||
@@ -163,6 +161,7 @@ export const vi: LanguageTranslation = {
|
|||||||
title: 'Thuộc tính chỉ mục',
|
title: 'Thuộc tính chỉ mục',
|
||||||
name: 'Tên',
|
name: 'Tên',
|
||||||
unique: 'Giá trị duy nhất',
|
unique: 'Giá trị duy nhất',
|
||||||
|
index_type: 'Loại chỉ mục',
|
||||||
delete_index: 'Xóa chỉ mục',
|
delete_index: 'Xóa chỉ mục',
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -179,12 +178,15 @@ export const vi: LanguageTranslation = {
|
|||||||
description: 'Tạo một bảng để bắt đầu',
|
description: 'Tạo một bảng để bắt đầu',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: 'Quan hệ',
|
refs: 'Refs',
|
||||||
filter: 'Lọc',
|
filter: 'Lọc',
|
||||||
add_relationship: 'Thêm quan hệ',
|
|
||||||
collapse: 'Thu gọn tất cả',
|
collapse: 'Thu gọn tất cả',
|
||||||
|
add_relationship: 'Thêm quan hệ',
|
||||||
|
relationships: 'Quan hệ',
|
||||||
|
dependencies: 'Phụ thuộc',
|
||||||
relationship: {
|
relationship: {
|
||||||
|
relationship: 'Quan hệ',
|
||||||
primary: 'Bảng khóa chính',
|
primary: 'Bảng khóa chính',
|
||||||
foreign: 'Bảng khóa ngoại',
|
foreign: 'Bảng khóa ngoại',
|
||||||
cardinality: 'Quan hệ',
|
cardinality: 'Quan hệ',
|
||||||
@@ -194,16 +196,8 @@ export const vi: LanguageTranslation = {
|
|||||||
delete_relationship: 'Xóa',
|
delete_relationship: 'Xóa',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
|
||||||
title: 'Không có quan hệ',
|
|
||||||
description: 'Tạo quan hệ để kết nối các bảng',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dependencies_section: {
|
|
||||||
dependencies: 'Phụ thuộc',
|
|
||||||
filter: 'Lọc',
|
|
||||||
collapse: 'Thu gọn tất cả',
|
|
||||||
dependency: {
|
dependency: {
|
||||||
|
dependency: 'Phụ thuộc',
|
||||||
table: 'Bảng',
|
table: 'Bảng',
|
||||||
dependent_table: 'Bảng xem phụ thuộc',
|
dependent_table: 'Bảng xem phụ thuộc',
|
||||||
delete_dependency: 'Xóa',
|
delete_dependency: 'Xóa',
|
||||||
@@ -213,8 +207,8 @@ export const vi: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: 'Không có phụ thuộc',
|
title: 'Không có quan hệ',
|
||||||
description: 'Tạo bảng xem phụ thuộc để bắt đầu',
|
description: 'Tạo một quan hệ để bắt đầu',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -254,6 +248,7 @@ export const vi: LanguageTranslation = {
|
|||||||
enum_values: 'Enum Values',
|
enum_values: 'Enum Values',
|
||||||
composite_fields: 'Fields',
|
composite_fields: 'Fields',
|
||||||
no_fields: 'No fields defined',
|
no_fields: 'No fields defined',
|
||||||
|
no_values: 'Không có giá trị enum được định nghĩa',
|
||||||
field_name_placeholder: 'Field name',
|
field_name_placeholder: 'Field name',
|
||||||
field_type_placeholder: 'Select type',
|
field_type_placeholder: 'Select type',
|
||||||
add_field: 'Add Field',
|
add_field: 'Add Field',
|
||||||
@@ -276,7 +271,7 @@ export const vi: LanguageTranslation = {
|
|||||||
show_all: 'Hiển thị tất cả',
|
show_all: 'Hiển thị tất cả',
|
||||||
undo: 'Hoàn tác',
|
undo: 'Hoàn tác',
|
||||||
redo: 'Làm lại',
|
redo: 'Làm lại',
|
||||||
reorder_diagram: 'Sắp xếp lại sơ đồ',
|
reorder_diagram: 'Tự động sắp xếp sơ đồ',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||||
custom_type_highlight_tooltip:
|
custom_type_highlight_tooltip:
|
||||||
@@ -314,13 +309,13 @@ export const vi: LanguageTranslation = {
|
|||||||
cancel: 'Hủy',
|
cancel: 'Hủy',
|
||||||
import_from_file: 'Nhập từ tệp',
|
import_from_file: 'Nhập từ tệp',
|
||||||
back: 'Trở lại',
|
back: 'Trở lại',
|
||||||
empty_diagram: 'Sơ đồ trống',
|
empty_diagram: 'Cơ sở dữ liệu trống',
|
||||||
continue: 'Tiếp tục',
|
continue: 'Tiếp tục',
|
||||||
import: 'Nhập',
|
import: 'Nhập',
|
||||||
},
|
},
|
||||||
|
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: 'Mở sơ đồ',
|
title: 'Mở cơ sở dữ liệu',
|
||||||
description: 'Chọn sơ đồ để mở từ danh sách bên dưới.',
|
description: 'Chọn sơ đồ để mở từ danh sách bên dưới.',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
name: 'Tên',
|
name: 'Tên',
|
||||||
@@ -330,6 +325,12 @@ export const vi: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
cancel: 'Hủy',
|
cancel: 'Hủy',
|
||||||
open: 'Mở',
|
open: 'Mở',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: 'Mở',
|
||||||
|
duplicate: 'Nhân bản',
|
||||||
|
delete: 'Xóa',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -477,6 +478,7 @@ export const vi: LanguageTranslation = {
|
|||||||
|
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: 'Tạo bảng mới',
|
new_table: 'Tạo bảng mới',
|
||||||
|
new_view: 'Chế độ xem Mới',
|
||||||
new_relationship: 'Tạo quan hệ mới',
|
new_relationship: 'Tạo quan hệ mới',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
new_area: 'New Area',
|
new_area: 'New Area',
|
||||||
@@ -498,6 +500,9 @@ export const vi: LanguageTranslation = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: 'Ngôn ngữ',
|
change_language: 'Ngôn ngữ',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: 'Bật',
|
||||||
|
off: 'Tắt',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
|||||||
|
|
||||||
export const zh_CN: LanguageTranslation = {
|
export const zh_CN: LanguageTranslation = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: '新建',
|
||||||
|
browse: '浏览',
|
||||||
|
tables: '表',
|
||||||
|
refs: '引用',
|
||||||
|
areas: '区域',
|
||||||
|
dependencies: '依赖关系',
|
||||||
|
custom_types: '自定义类型',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: '文件',
|
actions: '操作',
|
||||||
new: '新建',
|
new: '新建...',
|
||||||
open: '打开',
|
browse: '浏览...',
|
||||||
save: '保存',
|
save: '保存',
|
||||||
import: '导入数据库',
|
import: '导入数据库',
|
||||||
export_sql: '导出 SQL 语句',
|
export_sql: '导出 SQL 语句',
|
||||||
export_as: '导出为',
|
export_as: '导出为',
|
||||||
delete_diagram: '删除关系图',
|
delete_diagram: '删除',
|
||||||
exit: '退出',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: '编辑',
|
edit: '编辑',
|
||||||
@@ -29,6 +37,7 @@ export const zh_CN: LanguageTranslation = {
|
|||||||
show_field_attributes: '展示字段属性',
|
show_field_attributes: '展示字段属性',
|
||||||
hide_field_attributes: '隐藏字段属性',
|
hide_field_attributes: '隐藏字段属性',
|
||||||
zoom_on_scroll: '滚动缩放',
|
zoom_on_scroll: '滚动缩放',
|
||||||
|
show_views: '数据库视图',
|
||||||
theme: '主题',
|
theme: '主题',
|
||||||
show_dependencies: '展示依赖',
|
show_dependencies: '展示依赖',
|
||||||
hide_dependencies: '隐藏依赖',
|
hide_dependencies: '隐藏依赖',
|
||||||
@@ -63,21 +72,12 @@ export const zh_CN: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: '重新排列关系图',
|
title: '自动排列关系图',
|
||||||
description: '此操作将重新排列关系图中的所有表。是否要继续?',
|
description: '此操作将重新排列关系图中的所有表。是否要继续?',
|
||||||
reorder: '重新排列',
|
reorder: '自动排列',
|
||||||
cancel: '取消',
|
cancel: '取消',
|
||||||
},
|
},
|
||||||
|
|
||||||
multiple_schemas_alert: {
|
|
||||||
title: '多个模式',
|
|
||||||
description:
|
|
||||||
'此关系图中有 {{schemasCount}} 个模式,当前显示:{{formattedSchemas}}。',
|
|
||||||
// TODO: Translate
|
|
||||||
show_me: 'Show me',
|
|
||||||
none: '无',
|
|
||||||
},
|
|
||||||
|
|
||||||
copy_to_clipboard_toast: {
|
copy_to_clipboard_toast: {
|
||||||
unsupported: {
|
unsupported: {
|
||||||
title: '复制失败',
|
title: '复制失败',
|
||||||
@@ -112,14 +112,11 @@ export const zh_CN: LanguageTranslation = {
|
|||||||
copied: '复制了!',
|
copied: '复制了!',
|
||||||
|
|
||||||
side_panel: {
|
side_panel: {
|
||||||
schema: '模式:',
|
|
||||||
filter_by_schema: '按模式筛选',
|
|
||||||
search_schema: '搜索模式...',
|
|
||||||
no_schemas_found: '未找到模式。',
|
|
||||||
view_all_options: '查看所有选项...',
|
view_all_options: '查看所有选项...',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: '表',
|
tables: '表',
|
||||||
add_table: '添加表',
|
add_table: '添加表',
|
||||||
|
add_view: '添加视图',
|
||||||
filter: '筛选',
|
filter: '筛选',
|
||||||
collapse: '全部折叠',
|
collapse: '全部折叠',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
@@ -145,6 +142,7 @@ export const zh_CN: LanguageTranslation = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: '字段属性',
|
title: '字段属性',
|
||||||
unique: '唯一',
|
unique: '唯一',
|
||||||
|
auto_increment: '自动递增',
|
||||||
comments: '注释',
|
comments: '注释',
|
||||||
no_comments: '空',
|
no_comments: '空',
|
||||||
delete_field: '删除字段',
|
delete_field: '删除字段',
|
||||||
@@ -160,6 +158,7 @@ export const zh_CN: LanguageTranslation = {
|
|||||||
title: '索引属性',
|
title: '索引属性',
|
||||||
name: '名称',
|
name: '名称',
|
||||||
unique: '唯一',
|
unique: '唯一',
|
||||||
|
index_type: '索引类型',
|
||||||
delete_index: '删除索引',
|
delete_index: '删除索引',
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -176,12 +175,15 @@ export const zh_CN: LanguageTranslation = {
|
|||||||
description: '新建表以开始',
|
description: '新建表以开始',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: '关系',
|
refs: '引用',
|
||||||
filter: '筛选',
|
filter: '筛选',
|
||||||
add_relationship: '添加关系',
|
|
||||||
collapse: '全部折叠',
|
collapse: '全部折叠',
|
||||||
|
add_relationship: '添加关系',
|
||||||
|
relationships: '关系',
|
||||||
|
dependencies: '依赖关系',
|
||||||
relationship: {
|
relationship: {
|
||||||
|
relationship: '关系',
|
||||||
primary: '主表',
|
primary: '主表',
|
||||||
foreign: '被引用表',
|
foreign: '被引用表',
|
||||||
cardinality: '基数',
|
cardinality: '基数',
|
||||||
@@ -191,16 +193,8 @@ export const zh_CN: LanguageTranslation = {
|
|||||||
delete_relationship: '删除',
|
delete_relationship: '删除',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
|
||||||
title: '无关系',
|
|
||||||
description: '创建关系以连接表',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dependencies_section: {
|
|
||||||
dependencies: '依赖关系',
|
|
||||||
filter: '筛选',
|
|
||||||
collapse: '全部折叠',
|
|
||||||
dependency: {
|
dependency: {
|
||||||
|
dependency: '依赖',
|
||||||
table: '表',
|
table: '表',
|
||||||
dependent_table: '依赖视图',
|
dependent_table: '依赖视图',
|
||||||
delete_dependency: '删除',
|
delete_dependency: '删除',
|
||||||
@@ -210,8 +204,8 @@ export const zh_CN: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: '无依赖',
|
title: '无关系',
|
||||||
description: '创建视图以开始',
|
description: '创建关系以开始',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -251,6 +245,7 @@ export const zh_CN: LanguageTranslation = {
|
|||||||
enum_values: 'Enum Values',
|
enum_values: 'Enum Values',
|
||||||
composite_fields: 'Fields',
|
composite_fields: 'Fields',
|
||||||
no_fields: 'No fields defined',
|
no_fields: 'No fields defined',
|
||||||
|
no_values: '没有定义枚举值',
|
||||||
field_name_placeholder: 'Field name',
|
field_name_placeholder: 'Field name',
|
||||||
field_type_placeholder: 'Select type',
|
field_type_placeholder: 'Select type',
|
||||||
add_field: 'Add Field',
|
add_field: 'Add Field',
|
||||||
@@ -273,7 +268,7 @@ export const zh_CN: LanguageTranslation = {
|
|||||||
show_all: '展示全部',
|
show_all: '展示全部',
|
||||||
undo: '撤销',
|
undo: '撤销',
|
||||||
redo: '重做',
|
redo: '重做',
|
||||||
reorder_diagram: '重新排列关系图',
|
reorder_diagram: '自动排列关系图',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||||
custom_type_highlight_tooltip:
|
custom_type_highlight_tooltip:
|
||||||
@@ -311,13 +306,13 @@ export const zh_CN: LanguageTranslation = {
|
|||||||
cancel: '取消',
|
cancel: '取消',
|
||||||
import_from_file: '从文件导入',
|
import_from_file: '从文件导入',
|
||||||
back: '上一步',
|
back: '上一步',
|
||||||
empty_diagram: '新建空关系图',
|
empty_diagram: '空数据库',
|
||||||
continue: '下一步',
|
continue: '下一步',
|
||||||
import: '导入',
|
import: '导入',
|
||||||
},
|
},
|
||||||
|
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: '打开关系图',
|
title: '打开数据库',
|
||||||
description: '从下面的列表中选择一个图表打开。',
|
description: '从下面的列表中选择一个图表打开。',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
name: '名称',
|
name: '名称',
|
||||||
@@ -327,6 +322,12 @@ export const zh_CN: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
cancel: '取消',
|
cancel: '取消',
|
||||||
open: '打开',
|
open: '打开',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: '打开',
|
||||||
|
duplicate: '复制',
|
||||||
|
delete: '删除',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -472,6 +473,7 @@ export const zh_CN: LanguageTranslation = {
|
|||||||
|
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: '新建表',
|
new_table: '新建表',
|
||||||
|
new_view: '新建视图',
|
||||||
new_relationship: '新建关系',
|
new_relationship: '新建关系',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
new_area: 'New Area',
|
new_area: 'New Area',
|
||||||
@@ -493,6 +495,9 @@ export const zh_CN: LanguageTranslation = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: '语言',
|
change_language: '语言',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: '开启',
|
||||||
|
off: '关闭',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
|||||||
|
|
||||||
export const zh_TW: LanguageTranslation = {
|
export const zh_TW: LanguageTranslation = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: '新建',
|
||||||
|
browse: '瀏覽',
|
||||||
|
tables: '表格',
|
||||||
|
refs: 'Refs',
|
||||||
|
areas: '區域',
|
||||||
|
dependencies: '相依性',
|
||||||
|
custom_types: '自定義類型',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: '檔案',
|
actions: '操作',
|
||||||
new: '新增',
|
new: '新增...',
|
||||||
open: '開啟',
|
browse: '瀏覽...',
|
||||||
save: '儲存',
|
save: '儲存',
|
||||||
import: '匯入資料庫',
|
import: '匯入資料庫',
|
||||||
export_sql: '匯出 SQL',
|
export_sql: '匯出 SQL',
|
||||||
export_as: '匯出為特定格式',
|
export_as: '匯出為特定格式',
|
||||||
delete_diagram: '刪除圖表',
|
delete_diagram: '刪除',
|
||||||
exit: '退出',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: '編輯',
|
edit: '編輯',
|
||||||
@@ -29,6 +37,7 @@ export const zh_TW: LanguageTranslation = {
|
|||||||
hide_field_attributes: '隱藏欄位屬性',
|
hide_field_attributes: '隱藏欄位屬性',
|
||||||
show_field_attributes: '顯示欄位屬性',
|
show_field_attributes: '顯示欄位屬性',
|
||||||
zoom_on_scroll: '滾動縮放',
|
zoom_on_scroll: '滾動縮放',
|
||||||
|
show_views: '資料庫檢視',
|
||||||
theme: '主題',
|
theme: '主題',
|
||||||
show_dependencies: '顯示相依性',
|
show_dependencies: '顯示相依性',
|
||||||
hide_dependencies: '隱藏相依性',
|
hide_dependencies: '隱藏相依性',
|
||||||
@@ -63,21 +72,12 @@ export const zh_TW: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: '重新排列圖表',
|
title: '自動排列圖表',
|
||||||
description: '此操作將重新排列圖表中的所有表格。是否繼續?',
|
description: '此操作將重新排列圖表中的所有表格。是否繼續?',
|
||||||
reorder: '重新排列',
|
reorder: '自動排列',
|
||||||
cancel: '取消',
|
cancel: '取消',
|
||||||
},
|
},
|
||||||
|
|
||||||
multiple_schemas_alert: {
|
|
||||||
title: '多重 Schema',
|
|
||||||
description:
|
|
||||||
'此圖表中包含 {{schemasCount}} 個 Schema,目前顯示:{{formattedSchemas}}。',
|
|
||||||
// TODO: Translate
|
|
||||||
show_me: 'Show me',
|
|
||||||
none: '無',
|
|
||||||
},
|
|
||||||
|
|
||||||
copy_to_clipboard_toast: {
|
copy_to_clipboard_toast: {
|
||||||
unsupported: {
|
unsupported: {
|
||||||
title: '複製失敗',
|
title: '複製失敗',
|
||||||
@@ -112,14 +112,11 @@ export const zh_TW: LanguageTranslation = {
|
|||||||
copied: '已複製!',
|
copied: '已複製!',
|
||||||
|
|
||||||
side_panel: {
|
side_panel: {
|
||||||
schema: 'Schema:',
|
|
||||||
filter_by_schema: '依 Schema 篩選',
|
|
||||||
search_schema: '搜尋 Schema...',
|
|
||||||
no_schemas_found: '未找到 Schema。',
|
|
||||||
view_all_options: '顯示所有選項...',
|
view_all_options: '顯示所有選項...',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: '表格',
|
tables: '表格',
|
||||||
add_table: '新增表格',
|
add_table: '新增表格',
|
||||||
|
add_view: '新增檢視',
|
||||||
filter: '篩選',
|
filter: '篩選',
|
||||||
collapse: '全部摺疊',
|
collapse: '全部摺疊',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
@@ -145,6 +142,7 @@ export const zh_TW: LanguageTranslation = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: '欄位屬性',
|
title: '欄位屬性',
|
||||||
unique: '唯一',
|
unique: '唯一',
|
||||||
|
auto_increment: '自動遞增',
|
||||||
comments: '註解',
|
comments: '註解',
|
||||||
no_comments: '無註解',
|
no_comments: '無註解',
|
||||||
delete_field: '刪除欄位',
|
delete_field: '刪除欄位',
|
||||||
@@ -160,6 +158,7 @@ export const zh_TW: LanguageTranslation = {
|
|||||||
title: '索引屬性',
|
title: '索引屬性',
|
||||||
name: '名稱',
|
name: '名稱',
|
||||||
unique: '唯一',
|
unique: '唯一',
|
||||||
|
index_type: '索引類型',
|
||||||
delete_index: '刪除索引',
|
delete_index: '刪除索引',
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -176,12 +175,15 @@ export const zh_TW: LanguageTranslation = {
|
|||||||
description: '請新增表格以開始',
|
description: '請新增表格以開始',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: '關聯',
|
refs: 'Refs',
|
||||||
filter: '篩選',
|
filter: '篩選',
|
||||||
add_relationship: '新增關聯',
|
|
||||||
collapse: '全部摺疊',
|
collapse: '全部摺疊',
|
||||||
|
add_relationship: '新增關聯',
|
||||||
|
relationships: '關聯',
|
||||||
|
dependencies: '相依性',
|
||||||
relationship: {
|
relationship: {
|
||||||
|
relationship: '關聯',
|
||||||
primary: '主表格',
|
primary: '主表格',
|
||||||
foreign: '參照表格',
|
foreign: '參照表格',
|
||||||
cardinality: '基數',
|
cardinality: '基數',
|
||||||
@@ -191,16 +193,8 @@ export const zh_TW: LanguageTranslation = {
|
|||||||
delete_relationship: '刪除',
|
delete_relationship: '刪除',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
|
||||||
title: '尚無關聯',
|
|
||||||
description: '請新增關聯以連接表格',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dependencies_section: {
|
|
||||||
dependencies: '相依性',
|
|
||||||
filter: '篩選',
|
|
||||||
collapse: '全部摺疊',
|
|
||||||
dependency: {
|
dependency: {
|
||||||
|
dependency: '相依性',
|
||||||
table: '表格',
|
table: '表格',
|
||||||
dependent_table: '相依檢視',
|
dependent_table: '相依檢視',
|
||||||
delete_dependency: '刪除',
|
delete_dependency: '刪除',
|
||||||
@@ -210,8 +204,8 @@ export const zh_TW: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: '尚無相依性',
|
title: '尚無關聯',
|
||||||
description: '請建立檢視以開始',
|
description: '請建立關聯以開始',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -251,6 +245,7 @@ export const zh_TW: LanguageTranslation = {
|
|||||||
enum_values: 'Enum Values',
|
enum_values: 'Enum Values',
|
||||||
composite_fields: 'Fields',
|
composite_fields: 'Fields',
|
||||||
no_fields: 'No fields defined',
|
no_fields: 'No fields defined',
|
||||||
|
no_values: '沒有定義列舉值',
|
||||||
field_name_placeholder: 'Field name',
|
field_name_placeholder: 'Field name',
|
||||||
field_type_placeholder: 'Select type',
|
field_type_placeholder: 'Select type',
|
||||||
add_field: 'Add Field',
|
add_field: 'Add Field',
|
||||||
@@ -273,7 +268,7 @@ export const zh_TW: LanguageTranslation = {
|
|||||||
show_all: '顯示全部',
|
show_all: '顯示全部',
|
||||||
undo: '復原',
|
undo: '復原',
|
||||||
redo: '重做',
|
redo: '重做',
|
||||||
reorder_diagram: '重新排列圖表',
|
reorder_diagram: '自動排列圖表',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||||
custom_type_highlight_tooltip:
|
custom_type_highlight_tooltip:
|
||||||
@@ -310,13 +305,13 @@ export const zh_TW: LanguageTranslation = {
|
|||||||
cancel: '取消',
|
cancel: '取消',
|
||||||
import_from_file: '從檔案匯入',
|
import_from_file: '從檔案匯入',
|
||||||
back: '返回',
|
back: '返回',
|
||||||
empty_diagram: '空白圖表',
|
empty_diagram: '空資料庫',
|
||||||
continue: '繼續',
|
continue: '繼續',
|
||||||
import: '匯入',
|
import: '匯入',
|
||||||
},
|
},
|
||||||
|
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: '開啟圖表',
|
title: '開啟資料庫',
|
||||||
description: '請從以下列表中選擇一個圖表。',
|
description: '請從以下列表中選擇一個圖表。',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
name: '名稱',
|
name: '名稱',
|
||||||
@@ -326,6 +321,12 @@ export const zh_TW: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
cancel: '取消',
|
cancel: '取消',
|
||||||
open: '開啟',
|
open: '開啟',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: '開啟',
|
||||||
|
duplicate: '複製',
|
||||||
|
delete: '刪除',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -472,6 +473,7 @@ export const zh_TW: LanguageTranslation = {
|
|||||||
|
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: '新建表格',
|
new_table: '新建表格',
|
||||||
|
new_view: '新檢視',
|
||||||
new_relationship: '新建關聯',
|
new_relationship: '新建關聯',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
new_area: 'New Area',
|
new_area: 'New Area',
|
||||||
@@ -493,6 +495,9 @@ export const zh_TW: LanguageTranslation = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: '變更語言',
|
change_language: '變更語言',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: '開啟',
|
||||||
|
off: '關閉',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -18,4 +18,7 @@
|
|||||||
|
|
||||||
.marker-definitions {
|
.marker-definitions {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nodrag {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,3 +19,5 @@ export const randomColor = () => {
|
|||||||
|
|
||||||
export const viewColor = '#b0b0b0';
|
export const viewColor = '#b0b0b0';
|
||||||
export const materializedViewColor = '#7d7d7d';
|
export const materializedViewColor = '#7d7d7d';
|
||||||
|
export const defaultTableColor = '#8eb7ff';
|
||||||
|
export const defaultAreaColor = '#b067e9';
|
||||||
|
|||||||
@@ -129,9 +129,6 @@ export const clickhouseDataTypes: readonly DataTypeData[] = [
|
|||||||
{ name: 'enum', id: 'enum' },
|
{ name: 'enum', id: 'enum' },
|
||||||
{ name: 'lowcardinality', id: 'lowcardinality' },
|
{ name: 'lowcardinality', id: 'lowcardinality' },
|
||||||
|
|
||||||
// Array Type
|
|
||||||
{ name: 'array', id: 'array' },
|
|
||||||
|
|
||||||
// Tuple Type
|
// Tuple Type
|
||||||
{ name: 'tuple', id: 'tuple' },
|
{ name: 'tuple', id: 'tuple' },
|
||||||
{ name: 'map', id: 'map' },
|
{ name: 'map', id: 'map' },
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { DatabaseType } from '../../domain/database-type';
|
import { DatabaseType } from '../../domain/database-type';
|
||||||
|
import { databaseSupportsArrays } from '../../domain/database-capabilities';
|
||||||
import { clickhouseDataTypes } from './clickhouse-data-types';
|
import { clickhouseDataTypes } from './clickhouse-data-types';
|
||||||
import { genericDataTypes } from './generic-data-types';
|
import { genericDataTypes } from './generic-data-types';
|
||||||
import { mariadbDataTypes } from './mariadb-data-types';
|
import { mariadbDataTypes } from './mariadb-data-types';
|
||||||
@@ -146,3 +147,53 @@ export const findDataTypeDataById = (
|
|||||||
|
|
||||||
return dataTypesOptions.find((dataType) => dataType.id === id);
|
return dataTypesOptions.find((dataType) => dataType.id === id);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const supportsAutoIncrementDataType = (
|
||||||
|
dataTypeName: string
|
||||||
|
): boolean => {
|
||||||
|
return [
|
||||||
|
'integer',
|
||||||
|
'int',
|
||||||
|
'bigint',
|
||||||
|
'smallint',
|
||||||
|
'tinyint',
|
||||||
|
'mediumint',
|
||||||
|
'serial',
|
||||||
|
'bigserial',
|
||||||
|
'smallserial',
|
||||||
|
'number',
|
||||||
|
'numeric',
|
||||||
|
'decimal',
|
||||||
|
].includes(dataTypeName.toLocaleLowerCase());
|
||||||
|
};
|
||||||
|
|
||||||
|
export const autoIncrementAlwaysOn = (dataTypeName: string): boolean => {
|
||||||
|
return ['serial', 'bigserial', 'smallserial'].includes(
|
||||||
|
dataTypeName.toLowerCase()
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const requiresNotNull = (dataTypeName: string): boolean => {
|
||||||
|
return ['serial', 'bigserial', 'smallserial'].includes(
|
||||||
|
dataTypeName.toLowerCase()
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ARRAY_INCOMPATIBLE_TYPES = [
|
||||||
|
'serial',
|
||||||
|
'bigserial',
|
||||||
|
'smallserial',
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export const supportsArrayDataType = (
|
||||||
|
dataTypeName: string,
|
||||||
|
databaseType: DatabaseType
|
||||||
|
): boolean => {
|
||||||
|
if (!databaseSupportsArrays(databaseType)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !ARRAY_INCOMPATIBLE_TYPES.includes(
|
||||||
|
dataTypeName.toLowerCase() as (typeof ARRAY_INCOMPATIBLE_TYPES)[number]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export const postgresDataTypes: readonly DataTypeData[] = [
|
|||||||
{ name: 'text', id: 'text', usageLevel: 1 },
|
{ name: 'text', id: 'text', usageLevel: 1 },
|
||||||
{ name: 'boolean', id: 'boolean', usageLevel: 1 },
|
{ name: 'boolean', id: 'boolean', usageLevel: 1 },
|
||||||
{ name: 'timestamp', id: 'timestamp', usageLevel: 1 },
|
{ name: 'timestamp', id: 'timestamp', usageLevel: 1 },
|
||||||
|
{ name: 'timestamptz', id: 'timestamptz', usageLevel: 1 },
|
||||||
{ name: 'date', id: 'date', usageLevel: 1 },
|
{ name: 'date', id: 'date', usageLevel: 1 },
|
||||||
|
|
||||||
// Level 2 - Second most common types
|
// Level 2 - Second most common types
|
||||||
@@ -42,6 +43,7 @@ export const postgresDataTypes: readonly DataTypeData[] = [
|
|||||||
id: 'timestamp_with_time_zone',
|
id: 'timestamp_with_time_zone',
|
||||||
usageLevel: 2,
|
usageLevel: 2,
|
||||||
},
|
},
|
||||||
|
{ name: 'int', id: 'int', usageLevel: 2 },
|
||||||
|
|
||||||
// Less common types
|
// Less common types
|
||||||
{
|
{
|
||||||
@@ -95,7 +97,6 @@ export const postgresDataTypes: readonly DataTypeData[] = [
|
|||||||
{ name: 'tsvector', id: 'tsvector' },
|
{ name: 'tsvector', id: 'tsvector' },
|
||||||
{ name: 'tsquery', id: 'tsquery' },
|
{ name: 'tsquery', id: 'tsquery' },
|
||||||
{ name: 'xml', id: 'xml' },
|
{ name: 'xml', id: 'xml' },
|
||||||
{ name: 'array', id: 'array' },
|
|
||||||
{ name: 'int4range', id: 'int4range' },
|
{ name: 'int4range', id: 'int4range' },
|
||||||
{ name: 'int8range', id: 'int8range' },
|
{ name: 'int8range', id: 'int8range' },
|
||||||
{ name: 'numrange', id: 'numrange' },
|
{ name: 'numrange', id: 'numrange' },
|
||||||
|
|||||||
@@ -50,5 +50,8 @@ export const sqliteDataTypes: readonly DataTypeData[] = [
|
|||||||
{ name: 'smallint', id: 'smallint' },
|
{ name: 'smallint', id: 'smallint' },
|
||||||
{ name: 'bigint', id: 'bigint' },
|
{ name: 'bigint', id: 'bigint' },
|
||||||
{ name: 'bool', id: 'bool' },
|
{ name: 'bool', id: 'bool' },
|
||||||
|
{ name: 'boolean', id: 'boolean' }, // Added for smartquery compatibility
|
||||||
{ name: 'time', id: 'time' },
|
{ name: 'time', id: 'time' },
|
||||||
|
{ name: 'date', id: 'date' }, // Added for smartquery compatibility
|
||||||
|
{ name: 'datetime', id: 'datetime' }, // Added for smartquery compatibility
|
||||||
] as const;
|
] as const;
|
||||||
|
|||||||
21
src/lib/data/import-metadata/import/custom-types.ts
Normal file
21
src/lib/data/import-metadata/import/custom-types.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import type { DBCustomType, DBCustomTypeKind } from '@/lib/domain';
|
||||||
|
import { schemaNameToDomainSchemaName } from '@/lib/domain';
|
||||||
|
import type { DBCustomTypeInfo } from '../metadata-types/custom-type-info';
|
||||||
|
import { generateId } from '@/lib/utils';
|
||||||
|
|
||||||
|
export const createCustomTypesFromMetadata = ({
|
||||||
|
customTypes,
|
||||||
|
}: {
|
||||||
|
customTypes: DBCustomTypeInfo[];
|
||||||
|
}): DBCustomType[] => {
|
||||||
|
return customTypes.map((customType) => {
|
||||||
|
return {
|
||||||
|
id: generateId(),
|
||||||
|
schema: schemaNameToDomainSchemaName(customType.schema),
|
||||||
|
name: customType.type,
|
||||||
|
kind: customType.kind as DBCustomTypeKind,
|
||||||
|
values: customType.values,
|
||||||
|
fields: customType.fields,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
351
src/lib/data/import-metadata/import/dependencies.ts
Normal file
351
src/lib/data/import-metadata/import/dependencies.ts
Normal file
@@ -0,0 +1,351 @@
|
|||||||
|
import { generateId } from '@/lib/utils';
|
||||||
|
import type { AST } from 'node-sql-parser';
|
||||||
|
import type { DBDependency, DBTable } from '@/lib/domain';
|
||||||
|
import { DatabaseType, schemaNameToDomainSchemaName } from '@/lib/domain';
|
||||||
|
import type { ViewInfo } from '../metadata-types/view-info';
|
||||||
|
import { decodeViewDefinition } from './tables';
|
||||||
|
|
||||||
|
const astDatabaseTypes: Record<DatabaseType, string> = {
|
||||||
|
[DatabaseType.POSTGRESQL]: 'postgresql',
|
||||||
|
[DatabaseType.MYSQL]: 'postgresql',
|
||||||
|
[DatabaseType.MARIADB]: 'postgresql',
|
||||||
|
[DatabaseType.GENERIC]: 'postgresql',
|
||||||
|
[DatabaseType.SQLITE]: 'postgresql',
|
||||||
|
[DatabaseType.SQL_SERVER]: 'postgresql',
|
||||||
|
[DatabaseType.CLICKHOUSE]: 'postgresql',
|
||||||
|
[DatabaseType.COCKROACHDB]: 'postgresql',
|
||||||
|
[DatabaseType.ORACLE]: 'postgresql',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createDependenciesFromMetadata = async ({
|
||||||
|
views,
|
||||||
|
tables,
|
||||||
|
databaseType,
|
||||||
|
}: {
|
||||||
|
views: ViewInfo[];
|
||||||
|
tables: DBTable[];
|
||||||
|
databaseType: DatabaseType;
|
||||||
|
}): Promise<DBDependency[]> => {
|
||||||
|
if (!views || views.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const { Parser } = await import('node-sql-parser');
|
||||||
|
const parser = new Parser();
|
||||||
|
|
||||||
|
const dependencies = views
|
||||||
|
.flatMap((view) => {
|
||||||
|
const viewSchema = schemaNameToDomainSchemaName(view.schema);
|
||||||
|
const viewTable = tables.find(
|
||||||
|
(table) =>
|
||||||
|
table.name === view.view_name && viewSchema === table.schema
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!viewTable) {
|
||||||
|
console.warn(
|
||||||
|
`Source table for view ${view.view_name} not found (schema: ${viewSchema})`
|
||||||
|
);
|
||||||
|
return []; // Skip this view and proceed to the next
|
||||||
|
}
|
||||||
|
|
||||||
|
if (view.view_definition) {
|
||||||
|
try {
|
||||||
|
const decodedViewDefinition = decodeViewDefinition(
|
||||||
|
databaseType,
|
||||||
|
view.view_definition
|
||||||
|
);
|
||||||
|
|
||||||
|
let modifiedViewDefinition = '';
|
||||||
|
if (
|
||||||
|
databaseType === DatabaseType.MYSQL ||
|
||||||
|
databaseType === DatabaseType.MARIADB
|
||||||
|
) {
|
||||||
|
modifiedViewDefinition = preprocessViewDefinitionMySQL(
|
||||||
|
decodedViewDefinition
|
||||||
|
);
|
||||||
|
} else if (databaseType === DatabaseType.SQL_SERVER) {
|
||||||
|
modifiedViewDefinition =
|
||||||
|
preprocessViewDefinitionSQLServer(
|
||||||
|
decodedViewDefinition
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
modifiedViewDefinition = preprocessViewDefinition(
|
||||||
|
decodedViewDefinition
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse using the appropriate dialect
|
||||||
|
const ast = parser.astify(modifiedViewDefinition, {
|
||||||
|
database: astDatabaseTypes[databaseType],
|
||||||
|
type: 'select', // Parsing a SELECT statement
|
||||||
|
});
|
||||||
|
|
||||||
|
let relatedTables = extractTablesFromAST(ast);
|
||||||
|
|
||||||
|
// Filter out duplicate tables without schema
|
||||||
|
relatedTables = filterDuplicateTables(relatedTables);
|
||||||
|
|
||||||
|
return relatedTables.map((relTable) => {
|
||||||
|
const relSchema = relTable.schema || view.schema; // Use view's schema if relSchema is undefined
|
||||||
|
const relTableName = relTable.tableName;
|
||||||
|
|
||||||
|
const table = tables.find(
|
||||||
|
(table) =>
|
||||||
|
table.name === relTableName &&
|
||||||
|
(table.schema || '') === relSchema
|
||||||
|
);
|
||||||
|
|
||||||
|
if (table) {
|
||||||
|
const dependency: DBDependency = {
|
||||||
|
id: generateId(),
|
||||||
|
schema: view.schema,
|
||||||
|
tableId: table.id, // related table
|
||||||
|
dependentSchema: table.schema,
|
||||||
|
dependentTableId: viewTable.id, // dependent view
|
||||||
|
createdAt: Date.now(),
|
||||||
|
};
|
||||||
|
|
||||||
|
return dependency;
|
||||||
|
} else {
|
||||||
|
console.warn(
|
||||||
|
`Dependent table ${relSchema}.${relTableName} not found for view ${view.schema}.${view.view_name}`
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error(
|
||||||
|
`Error parsing view ${view.schema}.${view.view_name}:`,
|
||||||
|
error
|
||||||
|
);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.warn(
|
||||||
|
`View definition missing for ${view.schema}.${view.view_name}`
|
||||||
|
);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter((dependency) => dependency !== null);
|
||||||
|
|
||||||
|
return dependencies;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add this new function to filter out duplicate tables
|
||||||
|
function filterDuplicateTables(
|
||||||
|
tables: { schema?: string; tableName: string }[]
|
||||||
|
): { schema?: string; tableName: string }[] {
|
||||||
|
const tableMap = new Map<string, { schema?: string; tableName: string }>();
|
||||||
|
|
||||||
|
for (const table of tables) {
|
||||||
|
const key = table.tableName;
|
||||||
|
const existingTable = tableMap.get(key);
|
||||||
|
|
||||||
|
if (!existingTable || (table.schema && !existingTable.schema)) {
|
||||||
|
tableMap.set(key, table);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.from(tableMap.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preprocess the view_definition to remove schema from CREATE VIEW
|
||||||
|
function preprocessViewDefinition(viewDefinition: string): string {
|
||||||
|
if (!viewDefinition) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove leading and trailing whitespace
|
||||||
|
viewDefinition = viewDefinition.replace(/\s+/g, ' ').trim();
|
||||||
|
|
||||||
|
// Replace escaped double quotes with regular ones
|
||||||
|
viewDefinition = viewDefinition.replace(/\\"/g, '"');
|
||||||
|
|
||||||
|
// Replace 'CREATE MATERIALIZED VIEW' with 'CREATE VIEW'
|
||||||
|
viewDefinition = viewDefinition.replace(
|
||||||
|
/CREATE\s+MATERIALIZED\s+VIEW/i,
|
||||||
|
'CREATE VIEW'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Regular expression to match 'CREATE VIEW [schema.]view_name [ (column definitions) ] AS'
|
||||||
|
// This regex captures the view name and skips any content between the view name and 'AS'
|
||||||
|
const regex =
|
||||||
|
/CREATE\s+VIEW\s+(?:(?:`[^`]+`|"[^"]+"|\w+)\.)?(?:`([^`]+)`|"([^"]+)"|(\w+))[\s\S]*?\bAS\b\s+/i;
|
||||||
|
|
||||||
|
const match = viewDefinition.match(regex);
|
||||||
|
let modifiedDefinition: string;
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
const viewName = match[1] || match[2] || match[3];
|
||||||
|
// Extract the SQL after the 'AS' keyword
|
||||||
|
const restOfDefinition = viewDefinition.substring(
|
||||||
|
match.index! + match[0].length
|
||||||
|
);
|
||||||
|
|
||||||
|
// Replace double-quoted identifiers with unquoted ones
|
||||||
|
let modifiedSQL = restOfDefinition.replace(/"(\w+)"/g, '$1');
|
||||||
|
|
||||||
|
// Replace '::' type casts with 'CAST' expressions
|
||||||
|
modifiedSQL = modifiedSQL.replace(
|
||||||
|
/\(([^()]+)\)::(\w+)/g,
|
||||||
|
'CAST($1 AS $2)'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Remove ClickHouse-specific syntax that may still be present
|
||||||
|
// For example, remove SETTINGS clauses inside the SELECT statement
|
||||||
|
modifiedSQL = modifiedSQL.replace(/\bSETTINGS\b[\s\S]*$/i, '');
|
||||||
|
|
||||||
|
modifiedDefinition = `CREATE VIEW ${viewName} AS ${modifiedSQL}`;
|
||||||
|
} else {
|
||||||
|
console.warn('Could not preprocess view definition:', viewDefinition);
|
||||||
|
modifiedDefinition = viewDefinition;
|
||||||
|
}
|
||||||
|
|
||||||
|
return modifiedDefinition;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preprocess the view_definition for SQL Server
|
||||||
|
function preprocessViewDefinitionSQLServer(viewDefinition: string): string {
|
||||||
|
if (!viewDefinition) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove BOM if present
|
||||||
|
viewDefinition = viewDefinition.replace(/^\uFEFF/, '');
|
||||||
|
|
||||||
|
// Normalize whitespace
|
||||||
|
viewDefinition = viewDefinition.replace(/\s+/g, ' ').trim();
|
||||||
|
|
||||||
|
// Remove square brackets and replace with double quotes
|
||||||
|
viewDefinition = viewDefinition.replace(/\[([^\]]+)\]/g, '"$1"');
|
||||||
|
|
||||||
|
// Remove database names from fully qualified identifiers
|
||||||
|
viewDefinition = viewDefinition.replace(
|
||||||
|
/"([a-zA-Z0-9_]+)"\."([a-zA-Z0-9_]+)"\."([a-zA-Z0-9_]+)"/g,
|
||||||
|
'"$2"."$3"'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Replace SQL Server functions with PostgreSQL equivalents
|
||||||
|
viewDefinition = viewDefinition.replace(/\bGETDATE\(\)/gi, 'NOW()');
|
||||||
|
viewDefinition = viewDefinition.replace(/\bISNULL\(/gi, 'COALESCE(');
|
||||||
|
|
||||||
|
// Replace 'TOP N' with 'LIMIT N' at the end of the query
|
||||||
|
const topMatch = viewDefinition.match(/SELECT\s+TOP\s+(\d+)/i);
|
||||||
|
if (topMatch) {
|
||||||
|
const topN = topMatch[1];
|
||||||
|
viewDefinition = viewDefinition.replace(
|
||||||
|
/SELECT\s+TOP\s+\d+/i,
|
||||||
|
'SELECT'
|
||||||
|
);
|
||||||
|
viewDefinition = viewDefinition.replace(/;+\s*$/, ''); // Remove semicolons at the end
|
||||||
|
viewDefinition += ` LIMIT ${topN}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
viewDefinition = viewDefinition.replace(/\n/g, ''); // Remove newlines
|
||||||
|
|
||||||
|
// Adjust CREATE VIEW syntax
|
||||||
|
const regex =
|
||||||
|
/CREATE\s+VIEW\s+(?:"?([^".\s]+)"?\.)?"?([^".\s]+)"?\s+AS\s+/i;
|
||||||
|
const match = viewDefinition.match(regex);
|
||||||
|
let modifiedDefinition: string;
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
const viewName = match[2];
|
||||||
|
const modifiedSQL = viewDefinition.substring(
|
||||||
|
match.index! + match[0].length
|
||||||
|
);
|
||||||
|
|
||||||
|
// Remove semicolons at the end
|
||||||
|
const finalSQL = modifiedSQL.replace(/;+\s*$/, '');
|
||||||
|
|
||||||
|
modifiedDefinition = `CREATE VIEW "${viewName}" AS ${finalSQL}`;
|
||||||
|
} else {
|
||||||
|
console.warn('Could not preprocess view definition:', viewDefinition);
|
||||||
|
modifiedDefinition = viewDefinition;
|
||||||
|
}
|
||||||
|
|
||||||
|
return modifiedDefinition;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preprocess the view_definition to remove schema from CREATE VIEW
|
||||||
|
function preprocessViewDefinitionMySQL(viewDefinition: string): string {
|
||||||
|
if (!viewDefinition) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove any trailing semicolons
|
||||||
|
viewDefinition = viewDefinition.replace(/;\s*$/, '');
|
||||||
|
|
||||||
|
// Remove backticks from identifiers
|
||||||
|
viewDefinition = viewDefinition.replace(/`/g, '');
|
||||||
|
|
||||||
|
// Remove unnecessary parentheses around joins and ON clauses
|
||||||
|
viewDefinition = removeRedundantParentheses(viewDefinition);
|
||||||
|
|
||||||
|
return viewDefinition;
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeRedundantParentheses(sql: string): string {
|
||||||
|
// Regular expressions to match unnecessary parentheses
|
||||||
|
const patterns = [
|
||||||
|
/\(\s*(JOIN\s+[^()]+?)\s*\)/gi,
|
||||||
|
/\(\s*(ON\s+[^()]+?)\s*\)/gi,
|
||||||
|
// Additional patterns if necessary
|
||||||
|
];
|
||||||
|
|
||||||
|
let prevSql;
|
||||||
|
do {
|
||||||
|
prevSql = sql;
|
||||||
|
patterns.forEach((pattern) => {
|
||||||
|
sql = sql.replace(pattern, '$1');
|
||||||
|
});
|
||||||
|
} while (sql !== prevSql);
|
||||||
|
|
||||||
|
return sql;
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractTablesFromAST(
|
||||||
|
ast: AST | AST[]
|
||||||
|
): { schema?: string; tableName: string }[] {
|
||||||
|
const tablesMap = new Map<string, { schema: string; tableName: string }>();
|
||||||
|
const visitedNodes = new Set();
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
function traverse(node: any) {
|
||||||
|
if (!node || visitedNodes.has(node)) return;
|
||||||
|
visitedNodes.add(node);
|
||||||
|
|
||||||
|
if (Array.isArray(node)) {
|
||||||
|
node.forEach(traverse);
|
||||||
|
} else if (typeof node === 'object') {
|
||||||
|
// Check if node represents a table
|
||||||
|
if (
|
||||||
|
Object.hasOwnProperty.call(node, 'table') &&
|
||||||
|
typeof node.table === 'string'
|
||||||
|
) {
|
||||||
|
let schema = node.db || node.schema;
|
||||||
|
const tableName = node.table;
|
||||||
|
if (tableName) {
|
||||||
|
// Assign default schema if undefined
|
||||||
|
schema = schemaNameToDomainSchemaName(schema) || '';
|
||||||
|
const key = `${schema}.${tableName}`;
|
||||||
|
if (!tablesMap.has(key)) {
|
||||||
|
tablesMap.set(key, { schema, tableName });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively traverse all properties
|
||||||
|
for (const key in node) {
|
||||||
|
if (Object.hasOwnProperty.call(node, key)) {
|
||||||
|
traverse(node[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
traverse(ast);
|
||||||
|
|
||||||
|
return Array.from(tablesMap.values());
|
||||||
|
}
|
||||||
68
src/lib/data/import-metadata/import/fields.ts
Normal file
68
src/lib/data/import-metadata/import/fields.ts
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import type { DBField } from '@/lib/domain';
|
||||||
|
import type { ColumnInfo } from '../metadata-types/column-info';
|
||||||
|
import type { AggregatedIndexInfo } from '../metadata-types/index-info';
|
||||||
|
import type { PrimaryKeyInfo } from '../metadata-types/primary-key-info';
|
||||||
|
import type { TableInfo } from '../metadata-types/table-info';
|
||||||
|
import { generateId } from '@/lib/utils';
|
||||||
|
|
||||||
|
export const createFieldsFromMetadata = ({
|
||||||
|
tableColumns,
|
||||||
|
tablePrimaryKeys,
|
||||||
|
aggregatedIndexes,
|
||||||
|
}: {
|
||||||
|
tableColumns: ColumnInfo[];
|
||||||
|
tableSchema?: string;
|
||||||
|
tableInfo: TableInfo;
|
||||||
|
tablePrimaryKeys: PrimaryKeyInfo[];
|
||||||
|
aggregatedIndexes: AggregatedIndexInfo[];
|
||||||
|
}) => {
|
||||||
|
const uniqueColumns = tableColumns.reduce((acc, col) => {
|
||||||
|
if (!acc.has(col.name)) {
|
||||||
|
acc.set(col.name, col);
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, new Map<string, ColumnInfo>());
|
||||||
|
|
||||||
|
const sortedColumns = Array.from(uniqueColumns.values()).sort(
|
||||||
|
(a, b) => a.ordinal_position - b.ordinal_position
|
||||||
|
);
|
||||||
|
|
||||||
|
const tablePrimaryKeysColumns = tablePrimaryKeys.map((pk) =>
|
||||||
|
pk.column.trim()
|
||||||
|
);
|
||||||
|
|
||||||
|
return sortedColumns.map(
|
||||||
|
(col: ColumnInfo): DBField => ({
|
||||||
|
id: generateId(),
|
||||||
|
name: col.name,
|
||||||
|
type: {
|
||||||
|
id: col.type.split(' ').join('_').toLowerCase(),
|
||||||
|
name: col.type.toLowerCase(),
|
||||||
|
},
|
||||||
|
primaryKey: tablePrimaryKeysColumns.includes(col.name),
|
||||||
|
unique: Object.values(aggregatedIndexes).some(
|
||||||
|
(idx) =>
|
||||||
|
idx.unique &&
|
||||||
|
idx.columns.length === 1 &&
|
||||||
|
idx.columns[0].name === col.name
|
||||||
|
),
|
||||||
|
nullable: Boolean(col.nullable),
|
||||||
|
...(col.character_maximum_length &&
|
||||||
|
col.character_maximum_length !== 'null'
|
||||||
|
? { characterMaximumLength: col.character_maximum_length }
|
||||||
|
: {}),
|
||||||
|
...(col.precision?.precision
|
||||||
|
? { precision: col.precision.precision }
|
||||||
|
: {}),
|
||||||
|
...(col.precision?.scale ? { scale: col.precision.scale } : {}),
|
||||||
|
...(col.default ? { default: col.default } : {}),
|
||||||
|
...(col.collation ? { collation: col.collation } : {}),
|
||||||
|
...(col.is_identity !== undefined
|
||||||
|
? { increment: col.is_identity }
|
||||||
|
: {}),
|
||||||
|
...(col.is_array !== undefined ? { isArray: col.is_array } : {}),
|
||||||
|
createdAt: Date.now(),
|
||||||
|
comments: col.comment ? col.comment : undefined,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
82
src/lib/data/import-metadata/import/index.ts
Normal file
82
src/lib/data/import-metadata/import/index.ts
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
import type { DatabaseEdition, Diagram } from '@/lib/domain';
|
||||||
|
import { adjustTablePositions, DatabaseType } from '@/lib/domain';
|
||||||
|
import { generateDiagramId } from '@/lib/utils';
|
||||||
|
import type { DatabaseMetadata } from '../metadata-types/database-metadata';
|
||||||
|
import { createCustomTypesFromMetadata } from './custom-types';
|
||||||
|
import { createRelationshipsFromMetadata } from './relationships';
|
||||||
|
import { createTablesFromMetadata } from './tables';
|
||||||
|
import { createDependenciesFromMetadata } from './dependencies';
|
||||||
|
|
||||||
|
export const loadFromDatabaseMetadata = async ({
|
||||||
|
databaseType,
|
||||||
|
databaseMetadata,
|
||||||
|
diagramNumber,
|
||||||
|
databaseEdition,
|
||||||
|
}: {
|
||||||
|
databaseType: DatabaseType;
|
||||||
|
databaseMetadata: DatabaseMetadata;
|
||||||
|
diagramNumber?: number;
|
||||||
|
databaseEdition?: DatabaseEdition;
|
||||||
|
}): Promise<Diagram> => {
|
||||||
|
const {
|
||||||
|
fk_info: foreignKeys,
|
||||||
|
views: views,
|
||||||
|
custom_types: customTypes,
|
||||||
|
} = databaseMetadata;
|
||||||
|
|
||||||
|
const tables = createTablesFromMetadata({
|
||||||
|
databaseMetadata,
|
||||||
|
databaseType,
|
||||||
|
});
|
||||||
|
|
||||||
|
const relationships = createRelationshipsFromMetadata({
|
||||||
|
foreignKeys,
|
||||||
|
tables,
|
||||||
|
});
|
||||||
|
|
||||||
|
const dependencies = await createDependenciesFromMetadata({
|
||||||
|
views,
|
||||||
|
tables,
|
||||||
|
databaseType,
|
||||||
|
});
|
||||||
|
|
||||||
|
const dbCustomTypes = customTypes
|
||||||
|
? createCustomTypesFromMetadata({
|
||||||
|
customTypes,
|
||||||
|
})
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const adjustedTables = adjustTablePositions({
|
||||||
|
tables,
|
||||||
|
relationships,
|
||||||
|
mode: 'perSchema',
|
||||||
|
});
|
||||||
|
|
||||||
|
const sortedTables = adjustedTables.sort((a, b) => {
|
||||||
|
if (a.isView === b.isView) {
|
||||||
|
// Both are either tables or views, so sort alphabetically by name
|
||||||
|
return a.name.localeCompare(b.name);
|
||||||
|
}
|
||||||
|
// If one is a view and the other is not, put tables first
|
||||||
|
return a.isView ? 1 : -1;
|
||||||
|
});
|
||||||
|
|
||||||
|
const diagram: Diagram = {
|
||||||
|
id: generateDiagramId(),
|
||||||
|
name: databaseMetadata.database_name
|
||||||
|
? `${databaseMetadata.database_name}`
|
||||||
|
: diagramNumber
|
||||||
|
? `Diagram ${diagramNumber}`
|
||||||
|
: 'New Diagram',
|
||||||
|
databaseType: databaseType ?? DatabaseType.GENERIC,
|
||||||
|
databaseEdition,
|
||||||
|
tables: sortedTables,
|
||||||
|
relationships,
|
||||||
|
dependencies,
|
||||||
|
customTypes: dbCustomTypes,
|
||||||
|
createdAt: new Date(),
|
||||||
|
updatedAt: new Date(),
|
||||||
|
};
|
||||||
|
|
||||||
|
return diagram;
|
||||||
|
};
|
||||||
24
src/lib/data/import-metadata/import/indexes.ts
Normal file
24
src/lib/data/import-metadata/import/indexes.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import type { DBField, DBIndex, IndexType } from '@/lib/domain';
|
||||||
|
import type { AggregatedIndexInfo } from '../metadata-types/index-info';
|
||||||
|
import { generateId } from '@/lib/utils';
|
||||||
|
|
||||||
|
export const createIndexesFromMetadata = ({
|
||||||
|
aggregatedIndexes,
|
||||||
|
fields,
|
||||||
|
}: {
|
||||||
|
aggregatedIndexes: AggregatedIndexInfo[];
|
||||||
|
fields: DBField[];
|
||||||
|
}): DBIndex[] =>
|
||||||
|
aggregatedIndexes.map(
|
||||||
|
(idx): DBIndex => ({
|
||||||
|
id: generateId(),
|
||||||
|
name: idx.name,
|
||||||
|
unique: Boolean(idx.unique),
|
||||||
|
fieldIds: idx.columns
|
||||||
|
.sort((a, b) => a.position - b.position)
|
||||||
|
.map((c) => fields.find((f) => f.name === c.name)?.id)
|
||||||
|
.filter((id): id is string => id !== undefined),
|
||||||
|
createdAt: Date.now(),
|
||||||
|
type: idx.index_type?.toLowerCase() as IndexType,
|
||||||
|
})
|
||||||
|
);
|
||||||
85
src/lib/data/import-metadata/import/relationships.ts
Normal file
85
src/lib/data/import-metadata/import/relationships.ts
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
import type {
|
||||||
|
Cardinality,
|
||||||
|
DBField,
|
||||||
|
DBRelationship,
|
||||||
|
DBTable,
|
||||||
|
} from '@/lib/domain';
|
||||||
|
import { schemaNameToDomainSchemaName } from '@/lib/domain';
|
||||||
|
import type { ForeignKeyInfo } from '../metadata-types/foreign-key-info';
|
||||||
|
import { generateId } from '@/lib/utils';
|
||||||
|
|
||||||
|
const determineCardinality = (
|
||||||
|
field: DBField,
|
||||||
|
isTablePKComplex: boolean
|
||||||
|
): Cardinality => {
|
||||||
|
return field.unique || (field.primaryKey && !isTablePKComplex)
|
||||||
|
? 'one'
|
||||||
|
: 'many';
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createRelationshipsFromMetadata = ({
|
||||||
|
foreignKeys,
|
||||||
|
tables,
|
||||||
|
}: {
|
||||||
|
foreignKeys: ForeignKeyInfo[];
|
||||||
|
tables: DBTable[];
|
||||||
|
}): DBRelationship[] => {
|
||||||
|
return foreignKeys
|
||||||
|
.map((fk: ForeignKeyInfo): DBRelationship | null => {
|
||||||
|
const schema = schemaNameToDomainSchemaName(fk.schema);
|
||||||
|
const sourceTable = tables.find(
|
||||||
|
(table) => table.name === fk.table && table.schema === schema
|
||||||
|
);
|
||||||
|
|
||||||
|
const targetSchema = schemaNameToDomainSchemaName(
|
||||||
|
fk.reference_schema
|
||||||
|
);
|
||||||
|
|
||||||
|
const targetTable = tables.find(
|
||||||
|
(table) =>
|
||||||
|
table.name === fk.reference_table &&
|
||||||
|
table.schema === targetSchema
|
||||||
|
);
|
||||||
|
const sourceField = sourceTable?.fields.find(
|
||||||
|
(field) => field.name === fk.column
|
||||||
|
);
|
||||||
|
const targetField = targetTable?.fields.find(
|
||||||
|
(field) => field.name === fk.reference_column
|
||||||
|
);
|
||||||
|
|
||||||
|
const isSourceTablePKComplex =
|
||||||
|
(sourceTable?.fields.filter((field) => field.primaryKey) ?? [])
|
||||||
|
.length > 1;
|
||||||
|
const isTargetTablePKComplex =
|
||||||
|
(targetTable?.fields.filter((field) => field.primaryKey) ?? [])
|
||||||
|
.length > 1;
|
||||||
|
|
||||||
|
if (sourceTable && targetTable && sourceField && targetField) {
|
||||||
|
const sourceCardinality = determineCardinality(
|
||||||
|
sourceField,
|
||||||
|
isSourceTablePKComplex
|
||||||
|
);
|
||||||
|
const targetCardinality = determineCardinality(
|
||||||
|
targetField,
|
||||||
|
isTargetTablePKComplex
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: generateId(),
|
||||||
|
name: fk.foreign_key_name,
|
||||||
|
sourceSchema: schema,
|
||||||
|
targetSchema: targetSchema,
|
||||||
|
sourceTableId: sourceTable.id,
|
||||||
|
targetTableId: targetTable.id,
|
||||||
|
sourceFieldId: sourceField.id,
|
||||||
|
targetFieldId: targetField.id,
|
||||||
|
sourceCardinality,
|
||||||
|
targetCardinality,
|
||||||
|
createdAt: Date.now(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
.filter((rel) => rel !== null) as DBRelationship[];
|
||||||
|
};
|
||||||
228
src/lib/data/import-metadata/import/tables.ts
Normal file
228
src/lib/data/import-metadata/import/tables.ts
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
import type { DBIndex, DBTable } from '@/lib/domain';
|
||||||
|
import {
|
||||||
|
DatabaseType,
|
||||||
|
generateTableKey,
|
||||||
|
schemaNameToDomainSchemaName,
|
||||||
|
} from '@/lib/domain';
|
||||||
|
import type { DatabaseMetadata } from '../metadata-types/database-metadata';
|
||||||
|
import type { TableInfo } from '../metadata-types/table-info';
|
||||||
|
import { createAggregatedIndexes } from '../metadata-types/index-info';
|
||||||
|
import {
|
||||||
|
decodeBase64ToUtf16LE,
|
||||||
|
decodeBase64ToUtf8,
|
||||||
|
generateId,
|
||||||
|
} from '@/lib/utils';
|
||||||
|
import {
|
||||||
|
defaultTableColor,
|
||||||
|
materializedViewColor,
|
||||||
|
viewColor,
|
||||||
|
} from '@/lib/colors';
|
||||||
|
import { createFieldsFromMetadata } from './fields';
|
||||||
|
import { createIndexesFromMetadata } from './indexes';
|
||||||
|
|
||||||
|
export const decodeViewDefinition = (
|
||||||
|
databaseType: DatabaseType,
|
||||||
|
viewDefinition?: string
|
||||||
|
): string => {
|
||||||
|
if (!viewDefinition) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
let decodedViewDefinition: string;
|
||||||
|
if (databaseType === DatabaseType.SQL_SERVER) {
|
||||||
|
decodedViewDefinition = decodeBase64ToUtf16LE(viewDefinition);
|
||||||
|
} else {
|
||||||
|
decodedViewDefinition = decodeBase64ToUtf8(viewDefinition);
|
||||||
|
}
|
||||||
|
|
||||||
|
return decodedViewDefinition;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createTablesFromMetadata = ({
|
||||||
|
databaseMetadata,
|
||||||
|
databaseType,
|
||||||
|
}: {
|
||||||
|
databaseMetadata: DatabaseMetadata;
|
||||||
|
databaseType: DatabaseType;
|
||||||
|
}): DBTable[] => {
|
||||||
|
const {
|
||||||
|
tables: tableInfos,
|
||||||
|
pk_info: primaryKeys,
|
||||||
|
columns,
|
||||||
|
indexes,
|
||||||
|
views: views,
|
||||||
|
} = databaseMetadata;
|
||||||
|
|
||||||
|
// Pre-compute view names for faster lookup if there are views
|
||||||
|
const viewNamesSet = new Set<string>();
|
||||||
|
const materializedViewNamesSet = new Set<string>();
|
||||||
|
|
||||||
|
if (views && views.length > 0) {
|
||||||
|
views.forEach((view) => {
|
||||||
|
const key = generateTableKey({
|
||||||
|
schemaName: view.schema,
|
||||||
|
tableName: view.view_name,
|
||||||
|
});
|
||||||
|
viewNamesSet.add(key);
|
||||||
|
|
||||||
|
if (
|
||||||
|
view.view_definition &&
|
||||||
|
decodeViewDefinition(databaseType, view.view_definition)
|
||||||
|
.toLowerCase()
|
||||||
|
.includes('materialized')
|
||||||
|
) {
|
||||||
|
materializedViewNamesSet.add(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pre-compute lookup maps for better performance
|
||||||
|
const columnsByTable = new Map<string, (typeof columns)[0][]>();
|
||||||
|
const indexesByTable = new Map<string, (typeof indexes)[0][]>();
|
||||||
|
const primaryKeysByTable = new Map<string, (typeof primaryKeys)[0][]>();
|
||||||
|
|
||||||
|
// Group columns by table
|
||||||
|
columns.forEach((col) => {
|
||||||
|
const key = generateTableKey({
|
||||||
|
schemaName: col.schema,
|
||||||
|
tableName: col.table,
|
||||||
|
});
|
||||||
|
if (!columnsByTable.has(key)) {
|
||||||
|
columnsByTable.set(key, []);
|
||||||
|
}
|
||||||
|
columnsByTable.get(key)!.push(col);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Group indexes by table
|
||||||
|
indexes.forEach((idx) => {
|
||||||
|
const key = generateTableKey({
|
||||||
|
schemaName: idx.schema,
|
||||||
|
tableName: idx.table,
|
||||||
|
});
|
||||||
|
if (!indexesByTable.has(key)) {
|
||||||
|
indexesByTable.set(key, []);
|
||||||
|
}
|
||||||
|
indexesByTable.get(key)!.push(idx);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Group primary keys by table
|
||||||
|
primaryKeys.forEach((pk) => {
|
||||||
|
const key = generateTableKey({
|
||||||
|
schemaName: pk.schema,
|
||||||
|
tableName: pk.table,
|
||||||
|
});
|
||||||
|
if (!primaryKeysByTable.has(key)) {
|
||||||
|
primaryKeysByTable.set(key, []);
|
||||||
|
}
|
||||||
|
primaryKeysByTable.get(key)!.push(pk);
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = tableInfos.map((tableInfo: TableInfo) => {
|
||||||
|
const tableSchema = schemaNameToDomainSchemaName(tableInfo.schema);
|
||||||
|
const tableKey = generateTableKey({
|
||||||
|
schemaName: tableInfo.schema,
|
||||||
|
tableName: tableInfo.table,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Use pre-computed lookups instead of filtering entire arrays
|
||||||
|
const tableIndexes = indexesByTable.get(tableKey) || [];
|
||||||
|
const tablePrimaryKeys = primaryKeysByTable.get(tableKey) || [];
|
||||||
|
const tableColumns = columnsByTable.get(tableKey) || [];
|
||||||
|
|
||||||
|
// Aggregate indexes with multiple columns
|
||||||
|
const aggregatedIndexes = createAggregatedIndexes({
|
||||||
|
tableInfo,
|
||||||
|
tableSchema,
|
||||||
|
tableIndexes,
|
||||||
|
});
|
||||||
|
|
||||||
|
const fields = createFieldsFromMetadata({
|
||||||
|
aggregatedIndexes,
|
||||||
|
tableColumns,
|
||||||
|
tablePrimaryKeys,
|
||||||
|
tableInfo,
|
||||||
|
tableSchema,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check for composite primary key and find matching index name
|
||||||
|
const primaryKeyFields = fields.filter((f) => f.primaryKey);
|
||||||
|
let pkMatchingIndexName: string | undefined;
|
||||||
|
let pkIndex: DBIndex | undefined;
|
||||||
|
|
||||||
|
if (primaryKeyFields.length >= 1) {
|
||||||
|
// We have a composite primary key, look for an index that matches all PK columns
|
||||||
|
const pkFieldNames = primaryKeyFields.map((f) => f.name).sort();
|
||||||
|
|
||||||
|
// Find an index that matches the primary key columns exactly
|
||||||
|
const matchingIndex = aggregatedIndexes.find((index) => {
|
||||||
|
const indexColumnNames = index.columns
|
||||||
|
.map((c) => c.name)
|
||||||
|
.sort();
|
||||||
|
return (
|
||||||
|
indexColumnNames.length === pkFieldNames.length &&
|
||||||
|
indexColumnNames.every((col, i) => col === pkFieldNames[i])
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (matchingIndex) {
|
||||||
|
pkMatchingIndexName = matchingIndex.name;
|
||||||
|
// Create a special PK index
|
||||||
|
pkIndex = {
|
||||||
|
id: generateId(),
|
||||||
|
name: matchingIndex.name,
|
||||||
|
unique: true,
|
||||||
|
fieldIds: primaryKeyFields.map((f) => f.id),
|
||||||
|
createdAt: Date.now(),
|
||||||
|
isPrimaryKey: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter out the index that matches the composite PK (to avoid duplication)
|
||||||
|
const filteredAggregatedIndexes = pkMatchingIndexName
|
||||||
|
? aggregatedIndexes.filter(
|
||||||
|
(idx) => idx.name !== pkMatchingIndexName
|
||||||
|
)
|
||||||
|
: aggregatedIndexes;
|
||||||
|
|
||||||
|
const dbIndexes = createIndexesFromMetadata({
|
||||||
|
aggregatedIndexes: filteredAggregatedIndexes,
|
||||||
|
fields,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add the PK index if it exists
|
||||||
|
if (pkIndex) {
|
||||||
|
dbIndexes.push(pkIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine if the current table is a view by checking against pre-computed sets
|
||||||
|
const viewKey = generateTableKey({
|
||||||
|
schemaName: tableSchema,
|
||||||
|
tableName: tableInfo.table,
|
||||||
|
});
|
||||||
|
const isView = viewNamesSet.has(viewKey);
|
||||||
|
const isMaterializedView = materializedViewNamesSet.has(viewKey);
|
||||||
|
|
||||||
|
// Initial random positions; these will be adjusted later
|
||||||
|
return {
|
||||||
|
id: generateId(),
|
||||||
|
name: tableInfo.table,
|
||||||
|
schema: tableSchema,
|
||||||
|
x: Math.random() * 1000, // Placeholder X
|
||||||
|
y: Math.random() * 800, // Placeholder Y
|
||||||
|
fields,
|
||||||
|
indexes: dbIndexes,
|
||||||
|
color: isMaterializedView
|
||||||
|
? materializedViewColor
|
||||||
|
: isView
|
||||||
|
? viewColor
|
||||||
|
: defaultTableColor,
|
||||||
|
isView: isView,
|
||||||
|
isMaterializedView: isMaterializedView,
|
||||||
|
createdAt: Date.now(),
|
||||||
|
comments: tableInfo.comment ? tableInfo.comment : undefined,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
@@ -15,6 +15,8 @@ export interface ColumnInfo {
|
|||||||
default?: string | null; // Default value for the column, nullable
|
default?: string | null; // Default value for the column, nullable
|
||||||
collation?: string | null;
|
collation?: string | null;
|
||||||
comment?: string | null;
|
comment?: string | null;
|
||||||
|
is_identity?: boolean | null; // Indicates if the column is auto-increment/identity
|
||||||
|
is_array?: boolean | null; // Indicates if the column is an array type
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ColumnInfoSchema: z.ZodType<ColumnInfo> = z.object({
|
export const ColumnInfoSchema: z.ZodType<ColumnInfo> = z.object({
|
||||||
@@ -35,4 +37,6 @@ export const ColumnInfoSchema: z.ZodType<ColumnInfo> = z.object({
|
|||||||
default: z.string().nullable().optional(),
|
default: z.string().nullable().optional(),
|
||||||
collation: z.string().nullable().optional(),
|
collation: z.string().nullable().optional(),
|
||||||
comment: z.string().nullable().optional(),
|
comment: z.string().nullable().optional(),
|
||||||
|
is_identity: z.boolean().nullable().optional(),
|
||||||
|
is_array: z.boolean().nullable().optional(),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -127,7 +127,13 @@ cols AS (
|
|||||||
',"default":"', null,
|
',"default":"', null,
|
||||||
'","collation":"', COALESCE(cols.COLLATION_NAME::TEXT, ''),
|
'","collation":"', COALESCE(cols.COLLATION_NAME::TEXT, ''),
|
||||||
'","comment":"', COALESCE(replace(replace(dsc.description::TEXT, '"', '\\"'), '\\x', '\\\\x'), ''),
|
'","comment":"', COALESCE(replace(replace(dsc.description::TEXT, '"', '\\"'), '\\x', '\\\\x'), ''),
|
||||||
'"}')), ',') AS cols_metadata
|
'","is_identity":', CASE
|
||||||
|
WHEN cols.is_identity = 'YES' THEN 'true'
|
||||||
|
WHEN cols.column_default IS NOT NULL AND cols.column_default LIKE 'nextval(%' THEN 'true'
|
||||||
|
WHEN cols.column_default LIKE 'unique_rowid()%' THEN 'true'
|
||||||
|
ELSE 'false'
|
||||||
|
END,
|
||||||
|
'}')), ',') AS cols_metadata
|
||||||
FROM information_schema.columns cols
|
FROM information_schema.columns cols
|
||||||
LEFT JOIN pg_catalog.pg_class c
|
LEFT JOIN pg_catalog.pg_class c
|
||||||
ON c.relname = cols.table_name
|
ON c.relname = cols.table_name
|
||||||
|
|||||||
@@ -69,7 +69,9 @@ SELECT CAST(CONCAT(
|
|||||||
',"ordinal_position":', cols.ordinal_position,
|
',"ordinal_position":', cols.ordinal_position,
|
||||||
',"nullable":', IF(cols.is_nullable = 'YES', 'true', 'false'),
|
',"nullable":', IF(cols.is_nullable = 'YES', 'true', 'false'),
|
||||||
',"default":"', ${withExtras ? withDefault : withoutDefault},
|
',"default":"', ${withExtras ? withDefault : withoutDefault},
|
||||||
'","collation":"', IFNULL(cols.collation_name, ''), '"}')
|
'","collation":"', IFNULL(cols.collation_name, ''),
|
||||||
|
'","is_identity":', IF(cols.extra LIKE '%auto_increment%', 'true', 'false'),
|
||||||
|
'"}')
|
||||||
) FROM (
|
) FROM (
|
||||||
SELECT cols.table_schema,
|
SELECT cols.table_schema,
|
||||||
cols.table_name,
|
cols.table_name,
|
||||||
@@ -81,7 +83,8 @@ SELECT CAST(CONCAT(
|
|||||||
cols.ordinal_position,
|
cols.ordinal_position,
|
||||||
cols.is_nullable,
|
cols.is_nullable,
|
||||||
cols.column_default,
|
cols.column_default,
|
||||||
cols.collation_name
|
cols.collation_name,
|
||||||
|
cols.extra
|
||||||
FROM information_schema.columns cols
|
FROM information_schema.columns cols
|
||||||
WHERE cols.table_schema = DATABASE()
|
WHERE cols.table_schema = DATABASE()
|
||||||
) AS cols), ''),
|
) AS cols), ''),
|
||||||
|
|||||||
@@ -92,7 +92,9 @@ export const getMySQLQuery = (
|
|||||||
',"ordinal_position":', cols.ordinal_position,
|
',"ordinal_position":', cols.ordinal_position,
|
||||||
',"nullable":', IF(cols.is_nullable = 'YES', 'true', 'false'),
|
',"nullable":', IF(cols.is_nullable = 'YES', 'true', 'false'),
|
||||||
',"default":"', ${withExtras ? withDefault : withoutDefault},
|
',"default":"', ${withExtras ? withDefault : withoutDefault},
|
||||||
'","collation":"', IFNULL(cols.collation_name, ''), '"}'
|
'","collation":"', IFNULL(cols.collation_name, ''),
|
||||||
|
'","is_identity":', IF(cols.extra LIKE '%auto_increment%', 'true', 'false'),
|
||||||
|
'}'
|
||||||
)))))
|
)))))
|
||||||
), indexes as (
|
), indexes as (
|
||||||
(SELECT (@indexes:=NULL),
|
(SELECT (@indexes:=NULL),
|
||||||
|
|||||||
@@ -194,7 +194,12 @@ cols AS (
|
|||||||
',"default":"', ${withExtras ? withDefault : withoutDefault},
|
',"default":"', ${withExtras ? withDefault : withoutDefault},
|
||||||
'","collation":"', COALESCE(cols.COLLATION_NAME, ''),
|
'","collation":"', COALESCE(cols.COLLATION_NAME, ''),
|
||||||
'","comment":"', ${withExtras ? withComments : withoutComments},
|
'","comment":"', ${withExtras ? withComments : withoutComments},
|
||||||
'"}')), ',') AS cols_metadata
|
'","is_identity":', CASE
|
||||||
|
WHEN cols.is_identity = 'YES' THEN 'true'
|
||||||
|
WHEN cols.column_default IS NOT NULL AND cols.column_default LIKE 'nextval(%' THEN 'true'
|
||||||
|
ELSE 'false'
|
||||||
|
END,
|
||||||
|
'}')), ',') AS cols_metadata
|
||||||
FROM information_schema.columns cols
|
FROM information_schema.columns cols
|
||||||
LEFT JOIN pg_catalog.pg_class c
|
LEFT JOIN pg_catalog.pg_class c
|
||||||
ON c.relname = cols.table_name
|
ON c.relname = cols.table_name
|
||||||
|
|||||||
@@ -119,7 +119,13 @@ WITH fk_info AS (
|
|||||||
END
|
END
|
||||||
ELSE null
|
ELSE null
|
||||||
END,
|
END,
|
||||||
'default', ${withExtras ? withDefault : withoutDefault}
|
'default', ${withExtras ? withDefault : withoutDefault},
|
||||||
|
'is_identity',
|
||||||
|
CASE
|
||||||
|
WHEN p.pk = 1 AND LOWER(p.type) LIKE '%int%' THEN json('true')
|
||||||
|
WHEN LOWER((SELECT sql FROM sqlite_master WHERE name = m.name)) LIKE '%' || p.name || '%autoincrement%' THEN json('true')
|
||||||
|
ELSE json('false')
|
||||||
|
END
|
||||||
)
|
)
|
||||||
) AS cols_metadata
|
) AS cols_metadata
|
||||||
FROM
|
FROM
|
||||||
@@ -292,7 +298,13 @@ WITH fk_info AS (
|
|||||||
END
|
END
|
||||||
ELSE null
|
ELSE null
|
||||||
END,
|
END,
|
||||||
'default', ${withExtras ? withDefault : withoutDefault}
|
'default', ${withExtras ? withDefault : withoutDefault},
|
||||||
|
'is_identity',
|
||||||
|
CASE
|
||||||
|
WHEN p.pk = 1 AND LOWER(p.type) LIKE '%int%' THEN json('true')
|
||||||
|
WHEN LOWER((SELECT sql FROM sqlite_master WHERE name = m.name)) LIKE '%' || p.name || '%autoincrement%' THEN json('true')
|
||||||
|
ELSE json('false')
|
||||||
|
END
|
||||||
)
|
)
|
||||||
) AS cols_metadata
|
) AS cols_metadata
|
||||||
FROM
|
FROM
|
||||||
|
|||||||
@@ -91,6 +91,11 @@ cols AS (
|
|||||||
WHEN cols.COLLATION_NAME IS NULL THEN 'null'
|
WHEN cols.COLLATION_NAME IS NULL THEN 'null'
|
||||||
ELSE '"' + STRING_ESCAPE(cols.COLLATION_NAME, 'json') + '"'
|
ELSE '"' + STRING_ESCAPE(cols.COLLATION_NAME, 'json') + '"'
|
||||||
END +
|
END +
|
||||||
|
', "is_identity": ' + CASE
|
||||||
|
WHEN COLUMNPROPERTY(OBJECT_ID(cols.TABLE_SCHEMA + '.' + cols.TABLE_NAME), cols.COLUMN_NAME, 'IsIdentity') = 1
|
||||||
|
THEN 'true'
|
||||||
|
ELSE 'false'
|
||||||
|
END +
|
||||||
N'}') COLLATE DATABASE_DEFAULT
|
N'}') COLLATE DATABASE_DEFAULT
|
||||||
), N','
|
), N','
|
||||||
) +
|
) +
|
||||||
|
|||||||
356
src/lib/data/sql-export/__tests__/array-fields.test.ts
Normal file
356
src/lib/data/sql-export/__tests__/array-fields.test.ts
Normal file
@@ -0,0 +1,356 @@
|
|||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import { generateId } from '@/lib/utils';
|
||||||
|
import { exportBaseSQL } from '../export-sql-script';
|
||||||
|
import { DatabaseType } from '@/lib/domain/database-type';
|
||||||
|
import type { Diagram } from '@/lib/domain/diagram';
|
||||||
|
|
||||||
|
describe('SQL Export - Array Fields (Fantasy RPG Theme)', () => {
|
||||||
|
it('should export array fields for magical spell components', () => {
|
||||||
|
const diagram: Diagram = {
|
||||||
|
id: 'test-diagram',
|
||||||
|
name: 'Magical Spell System',
|
||||||
|
databaseType: DatabaseType.POSTGRESQL,
|
||||||
|
tables: [
|
||||||
|
{
|
||||||
|
id: generateId(),
|
||||||
|
name: 'spells',
|
||||||
|
schema: '',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
id: generateId(),
|
||||||
|
name: 'id',
|
||||||
|
type: { id: 'uuid', name: 'uuid' },
|
||||||
|
primaryKey: true,
|
||||||
|
unique: true,
|
||||||
|
nullable: false,
|
||||||
|
createdAt: Date.now(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: generateId(),
|
||||||
|
name: 'name',
|
||||||
|
type: { id: 'varchar', name: 'varchar' },
|
||||||
|
primaryKey: false,
|
||||||
|
unique: false,
|
||||||
|
nullable: false,
|
||||||
|
createdAt: Date.now(),
|
||||||
|
characterMaximumLength: '200',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: generateId(),
|
||||||
|
name: 'components',
|
||||||
|
type: { id: 'text', name: 'text' },
|
||||||
|
primaryKey: false,
|
||||||
|
unique: false,
|
||||||
|
nullable: true,
|
||||||
|
createdAt: Date.now(),
|
||||||
|
isArray: true,
|
||||||
|
comments: 'Magical components needed for the spell',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: generateId(),
|
||||||
|
name: 'elemental_types',
|
||||||
|
type: { id: 'varchar', name: 'varchar' },
|
||||||
|
primaryKey: false,
|
||||||
|
unique: false,
|
||||||
|
nullable: true,
|
||||||
|
createdAt: Date.now(),
|
||||||
|
characterMaximumLength: '50',
|
||||||
|
isArray: true,
|
||||||
|
comments:
|
||||||
|
'Elements involved: fire, water, earth, air',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
indexes: [],
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
color: '#3b82f6',
|
||||||
|
isView: false,
|
||||||
|
createdAt: Date.now(),
|
||||||
|
order: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
relationships: [],
|
||||||
|
createdAt: new Date(),
|
||||||
|
updatedAt: new Date(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const sql = exportBaseSQL({
|
||||||
|
diagram,
|
||||||
|
targetDatabaseType: DatabaseType.POSTGRESQL,
|
||||||
|
isDBMLFlow: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(sql).toContain('CREATE TABLE "spells"');
|
||||||
|
expect(sql).toContain('"components" text[]');
|
||||||
|
expect(sql).toContain('"elemental_types" varchar(50)[]');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should export array fields for hero inventory system', () => {
|
||||||
|
const diagram: Diagram = {
|
||||||
|
id: 'test-diagram',
|
||||||
|
name: 'RPG Inventory System',
|
||||||
|
databaseType: DatabaseType.POSTGRESQL,
|
||||||
|
tables: [
|
||||||
|
{
|
||||||
|
id: generateId(),
|
||||||
|
name: 'heroes',
|
||||||
|
schema: 'game',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
id: generateId(),
|
||||||
|
name: 'id',
|
||||||
|
type: { id: 'bigint', name: 'bigint' },
|
||||||
|
primaryKey: true,
|
||||||
|
unique: true,
|
||||||
|
nullable: false,
|
||||||
|
createdAt: Date.now(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: generateId(),
|
||||||
|
name: 'name',
|
||||||
|
type: { id: 'varchar', name: 'varchar' },
|
||||||
|
primaryKey: false,
|
||||||
|
unique: false,
|
||||||
|
nullable: false,
|
||||||
|
createdAt: Date.now(),
|
||||||
|
characterMaximumLength: '100',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: generateId(),
|
||||||
|
name: 'abilities',
|
||||||
|
type: { id: 'varchar', name: 'varchar' },
|
||||||
|
primaryKey: false,
|
||||||
|
unique: false,
|
||||||
|
nullable: true,
|
||||||
|
createdAt: Date.now(),
|
||||||
|
characterMaximumLength: '100',
|
||||||
|
isArray: true,
|
||||||
|
comments:
|
||||||
|
'Special abilities like Stealth, Fireball, etc',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: generateId(),
|
||||||
|
name: 'inventory_slots',
|
||||||
|
type: { id: 'integer', name: 'integer' },
|
||||||
|
primaryKey: false,
|
||||||
|
unique: false,
|
||||||
|
nullable: true,
|
||||||
|
createdAt: Date.now(),
|
||||||
|
isArray: true,
|
||||||
|
comments: 'Item IDs in inventory',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: generateId(),
|
||||||
|
name: 'skill_levels',
|
||||||
|
type: { id: 'decimal', name: 'decimal' },
|
||||||
|
primaryKey: false,
|
||||||
|
unique: false,
|
||||||
|
nullable: true,
|
||||||
|
createdAt: Date.now(),
|
||||||
|
precision: 5,
|
||||||
|
scale: 2,
|
||||||
|
isArray: true,
|
||||||
|
comments: 'Skill proficiency levels',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
indexes: [],
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
color: '#ef4444',
|
||||||
|
isView: false,
|
||||||
|
createdAt: Date.now(),
|
||||||
|
order: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
relationships: [],
|
||||||
|
createdAt: new Date(),
|
||||||
|
updatedAt: new Date(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const sql = exportBaseSQL({
|
||||||
|
diagram,
|
||||||
|
targetDatabaseType: DatabaseType.POSTGRESQL,
|
||||||
|
isDBMLFlow: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(sql).toContain('CREATE TABLE "game"."heroes"');
|
||||||
|
expect(sql).toContain('"abilities" varchar(100)[]');
|
||||||
|
expect(sql).toContain('"inventory_slots" integer[]');
|
||||||
|
expect(sql).toContain('"skill_levels" decimal(5, 2)[]');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should export non-array fields normally when isArray is false or undefined', () => {
|
||||||
|
const diagram: Diagram = {
|
||||||
|
id: 'test-diagram',
|
||||||
|
name: 'Quest System',
|
||||||
|
databaseType: DatabaseType.POSTGRESQL,
|
||||||
|
tables: [
|
||||||
|
{
|
||||||
|
id: generateId(),
|
||||||
|
name: 'quests',
|
||||||
|
schema: '',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
id: generateId(),
|
||||||
|
name: 'id',
|
||||||
|
type: { id: 'uuid', name: 'uuid' },
|
||||||
|
primaryKey: true,
|
||||||
|
unique: true,
|
||||||
|
nullable: false,
|
||||||
|
createdAt: Date.now(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: generateId(),
|
||||||
|
name: 'title',
|
||||||
|
type: { id: 'varchar', name: 'varchar' },
|
||||||
|
primaryKey: false,
|
||||||
|
unique: false,
|
||||||
|
nullable: false,
|
||||||
|
createdAt: Date.now(),
|
||||||
|
characterMaximumLength: '200',
|
||||||
|
isArray: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: generateId(),
|
||||||
|
name: 'description',
|
||||||
|
type: { id: 'text', name: 'text' },
|
||||||
|
primaryKey: false,
|
||||||
|
unique: false,
|
||||||
|
nullable: true,
|
||||||
|
createdAt: Date.now(),
|
||||||
|
// isArray is undefined - should not be treated as array
|
||||||
|
},
|
||||||
|
],
|
||||||
|
indexes: [],
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
color: '#8b5cf6',
|
||||||
|
isView: false,
|
||||||
|
createdAt: Date.now(),
|
||||||
|
order: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
relationships: [],
|
||||||
|
createdAt: new Date(),
|
||||||
|
updatedAt: new Date(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const sql = exportBaseSQL({
|
||||||
|
diagram,
|
||||||
|
targetDatabaseType: DatabaseType.POSTGRESQL,
|
||||||
|
isDBMLFlow: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(sql).toContain('"title" varchar(200)');
|
||||||
|
expect(sql).not.toContain('"title" varchar(200)[]');
|
||||||
|
expect(sql).toContain('"description" text');
|
||||||
|
expect(sql).not.toContain('"description" text[]');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle mixed array and non-array fields in magical creatures table', () => {
|
||||||
|
const diagram: Diagram = {
|
||||||
|
id: 'test-diagram',
|
||||||
|
name: 'Bestiary System',
|
||||||
|
databaseType: DatabaseType.POSTGRESQL,
|
||||||
|
tables: [
|
||||||
|
{
|
||||||
|
id: generateId(),
|
||||||
|
name: 'magical_creatures',
|
||||||
|
schema: 'bestiary',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
id: generateId(),
|
||||||
|
name: 'id',
|
||||||
|
type: { id: 'bigint', name: 'bigint' },
|
||||||
|
primaryKey: true,
|
||||||
|
unique: true,
|
||||||
|
nullable: false,
|
||||||
|
createdAt: Date.now(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: generateId(),
|
||||||
|
name: 'species_name',
|
||||||
|
type: { id: 'varchar', name: 'varchar' },
|
||||||
|
primaryKey: false,
|
||||||
|
unique: false,
|
||||||
|
nullable: false,
|
||||||
|
createdAt: Date.now(),
|
||||||
|
characterMaximumLength: '100',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: generateId(),
|
||||||
|
name: 'habitats',
|
||||||
|
type: { id: 'varchar', name: 'varchar' },
|
||||||
|
primaryKey: false,
|
||||||
|
unique: false,
|
||||||
|
nullable: true,
|
||||||
|
createdAt: Date.now(),
|
||||||
|
characterMaximumLength: '80',
|
||||||
|
isArray: true,
|
||||||
|
comments:
|
||||||
|
'Preferred habitats: forest, mountain, swamp',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: generateId(),
|
||||||
|
name: 'danger_level',
|
||||||
|
type: { id: 'integer', name: 'integer' },
|
||||||
|
primaryKey: false,
|
||||||
|
unique: false,
|
||||||
|
nullable: false,
|
||||||
|
createdAt: Date.now(),
|
||||||
|
default: '1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: generateId(),
|
||||||
|
name: 'resistances',
|
||||||
|
type: { id: 'varchar', name: 'varchar' },
|
||||||
|
primaryKey: false,
|
||||||
|
unique: false,
|
||||||
|
nullable: true,
|
||||||
|
createdAt: Date.now(),
|
||||||
|
characterMaximumLength: '50',
|
||||||
|
isArray: true,
|
||||||
|
comments: 'Damage resistances',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: generateId(),
|
||||||
|
name: 'is_tameable',
|
||||||
|
type: { id: 'boolean', name: 'boolean' },
|
||||||
|
primaryKey: false,
|
||||||
|
unique: false,
|
||||||
|
nullable: false,
|
||||||
|
createdAt: Date.now(),
|
||||||
|
default: 'false',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
indexes: [],
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
color: '#10b981',
|
||||||
|
isView: false,
|
||||||
|
createdAt: Date.now(),
|
||||||
|
order: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
relationships: [],
|
||||||
|
createdAt: new Date(),
|
||||||
|
updatedAt: new Date(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const sql = exportBaseSQL({
|
||||||
|
diagram,
|
||||||
|
targetDatabaseType: DatabaseType.POSTGRESQL,
|
||||||
|
isDBMLFlow: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(sql).toContain('CREATE TABLE "bestiary"."magical_creatures"');
|
||||||
|
expect(sql).toContain('"species_name" varchar(100)');
|
||||||
|
expect(sql).not.toContain('"species_name" varchar(100)[]');
|
||||||
|
expect(sql).toContain('"habitats" varchar(80)[]');
|
||||||
|
expect(sql).toContain('"danger_level" integer');
|
||||||
|
expect(sql).not.toContain('"danger_level" integer[]');
|
||||||
|
expect(sql).toContain('"resistances" varchar(50)[]');
|
||||||
|
expect(sql).toContain('"is_tameable" boolean');
|
||||||
|
expect(sql).not.toContain('"is_tameable" boolean[]');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,20 +1,10 @@
|
|||||||
import { describe, it, expect, vi } from 'vitest';
|
import { describe, it, expect } from 'vitest';
|
||||||
import { exportBaseSQL } from '../export-sql-script';
|
import { exportBaseSQL } from '../export-sql-script';
|
||||||
import { DatabaseType } from '@/lib/domain/database-type';
|
import { DatabaseType } from '@/lib/domain/database-type';
|
||||||
import type { Diagram } from '@/lib/domain/diagram';
|
import type { Diagram } from '@/lib/domain/diagram';
|
||||||
import type { DBTable } from '@/lib/domain/db-table';
|
import type { DBTable } from '@/lib/domain/db-table';
|
||||||
import type { DBField } from '@/lib/domain/db-field';
|
import type { DBField } from '@/lib/domain/db-field';
|
||||||
|
|
||||||
// Mock the dbml/core importer
|
|
||||||
vi.mock('@dbml/core', () => ({
|
|
||||||
importer: {
|
|
||||||
import: vi.fn((sql: string) => {
|
|
||||||
// Return a simplified DBML for testing
|
|
||||||
return sql;
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('DBML Export - SQL Generation Tests', () => {
|
describe('DBML Export - SQL Generation Tests', () => {
|
||||||
// Helper to generate test IDs and timestamps
|
// Helper to generate test IDs and timestamps
|
||||||
let idCounter = 0;
|
let idCounter = 0;
|
||||||
@@ -116,7 +106,7 @@ describe('DBML Export - SQL Generation Tests', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Should contain composite primary key syntax
|
// Should contain composite primary key syntax
|
||||||
expect(sql).toContain('PRIMARY KEY (spell_id, component_id)');
|
expect(sql).toContain('PRIMARY KEY ("spell_id", "component_id")');
|
||||||
// Should NOT contain individual PRIMARY KEY constraints
|
// Should NOT contain individual PRIMARY KEY constraints
|
||||||
expect(sql).not.toMatch(/spell_id\s+uuid\s+NOT NULL\s+PRIMARY KEY/);
|
expect(sql).not.toMatch(/spell_id\s+uuid\s+NOT NULL\s+PRIMARY KEY/);
|
||||||
expect(sql).not.toMatch(
|
expect(sql).not.toMatch(
|
||||||
@@ -124,6 +114,96 @@ describe('DBML Export - SQL Generation Tests', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not create duplicate index for composite primary key', () => {
|
||||||
|
const tableId = testId();
|
||||||
|
const field1Id = testId();
|
||||||
|
const field2Id = testId();
|
||||||
|
const field3Id = testId();
|
||||||
|
|
||||||
|
const diagram: Diagram = createDiagram({
|
||||||
|
id: testId(),
|
||||||
|
name: 'Landlord System',
|
||||||
|
databaseType: DatabaseType.POSTGRESQL,
|
||||||
|
tables: [
|
||||||
|
createTable({
|
||||||
|
id: tableId,
|
||||||
|
name: 'users_master_table',
|
||||||
|
schema: 'landlord',
|
||||||
|
fields: [
|
||||||
|
createField({
|
||||||
|
id: field1Id,
|
||||||
|
name: 'master_user_id',
|
||||||
|
type: { id: 'bigint', name: 'bigint' },
|
||||||
|
primaryKey: true,
|
||||||
|
nullable: false,
|
||||||
|
unique: false,
|
||||||
|
}),
|
||||||
|
createField({
|
||||||
|
id: field2Id,
|
||||||
|
name: 'tenant_id',
|
||||||
|
type: { id: 'bigint', name: 'bigint' },
|
||||||
|
primaryKey: true,
|
||||||
|
nullable: false,
|
||||||
|
unique: false,
|
||||||
|
}),
|
||||||
|
createField({
|
||||||
|
id: field3Id,
|
||||||
|
name: 'tenant_user_id',
|
||||||
|
type: { id: 'bigint', name: 'bigint' },
|
||||||
|
primaryKey: true,
|
||||||
|
nullable: false,
|
||||||
|
unique: false,
|
||||||
|
}),
|
||||||
|
createField({
|
||||||
|
id: testId(),
|
||||||
|
name: 'enabled',
|
||||||
|
type: { id: 'boolean', name: 'boolean' },
|
||||||
|
primaryKey: false,
|
||||||
|
nullable: true,
|
||||||
|
unique: false,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
indexes: [
|
||||||
|
{
|
||||||
|
id: testId(),
|
||||||
|
name: 'idx_users_master_table_master_user_id_tenant_id_tenant_user_id',
|
||||||
|
unique: false,
|
||||||
|
fieldIds: [field1Id, field2Id, field3Id],
|
||||||
|
createdAt: testTime,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: testId(),
|
||||||
|
name: 'index_1',
|
||||||
|
unique: true,
|
||||||
|
fieldIds: [field2Id, field3Id],
|
||||||
|
createdAt: testTime,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
relationships: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
const sql = exportBaseSQL({
|
||||||
|
diagram,
|
||||||
|
targetDatabaseType: DatabaseType.POSTGRESQL,
|
||||||
|
isDBMLFlow: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Should contain composite primary key constraint
|
||||||
|
expect(sql).toContain(
|
||||||
|
'PRIMARY KEY ("master_user_id", "tenant_id", "tenant_user_id")'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Should NOT contain the duplicate index for the primary key fields
|
||||||
|
expect(sql).not.toContain(
|
||||||
|
'CREATE INDEX idx_users_master_table_master_user_id_tenant_id_tenant_user_id'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Should still contain the unique index on subset of fields
|
||||||
|
expect(sql).toContain('CREATE UNIQUE INDEX index_1');
|
||||||
|
});
|
||||||
|
|
||||||
it('should handle single primary keys inline', () => {
|
it('should handle single primary keys inline', () => {
|
||||||
const diagram: Diagram = createDiagram({
|
const diagram: Diagram = createDiagram({
|
||||||
id: testId(),
|
id: testId(),
|
||||||
@@ -165,7 +245,7 @@ describe('DBML Export - SQL Generation Tests', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Should contain inline PRIMARY KEY
|
// Should contain inline PRIMARY KEY
|
||||||
expect(sql).toMatch(/id\s+uuid\s+NOT NULL\s+PRIMARY KEY/);
|
expect(sql).toMatch(/"id"\s+uuid\s+NOT NULL\s+PRIMARY KEY/);
|
||||||
// Should NOT contain separate PRIMARY KEY constraint
|
// Should NOT contain separate PRIMARY KEY constraint
|
||||||
expect(sql).not.toContain('PRIMARY KEY (id)');
|
expect(sql).not.toContain('PRIMARY KEY (id)');
|
||||||
});
|
});
|
||||||
@@ -226,8 +306,8 @@ describe('DBML Export - SQL Generation Tests', () => {
|
|||||||
expect(sql).not.toContain('DEFAULT has default');
|
expect(sql).not.toContain('DEFAULT has default');
|
||||||
expect(sql).not.toContain('DEFAULT DEFAULT has default');
|
expect(sql).not.toContain('DEFAULT DEFAULT has default');
|
||||||
// The fields should still be in the table
|
// The fields should still be in the table
|
||||||
expect(sql).toContain('is_active boolean');
|
expect(sql).toContain('"is_active" boolean');
|
||||||
expect(sql).toContain('stock_count integer NOT NULL'); // integer gets simplified to int
|
expect(sql).toContain('"stock_count" integer NOT NULL'); // integer gets simplified to int
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle valid default values correctly', () => {
|
it('should handle valid default values correctly', () => {
|
||||||
@@ -349,8 +429,8 @@ describe('DBML Export - SQL Generation Tests', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Should convert NOW to NOW() and ('now') to now()
|
// Should convert NOW to NOW() and ('now') to now()
|
||||||
expect(sql).toContain('created_at timestamp DEFAULT NOW');
|
expect(sql).toContain('"created_at" timestamp DEFAULT NOW');
|
||||||
expect(sql).toContain('updated_at timestamp DEFAULT now()');
|
expect(sql).toContain('"updated_at" timestamp DEFAULT now()');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -405,9 +485,9 @@ describe('DBML Export - SQL Generation Tests', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Should handle char with explicit length
|
// Should handle char with explicit length
|
||||||
expect(sql).toContain('element_code char(2)');
|
expect(sql).toContain('"element_code" char(2)');
|
||||||
// Should add default length for char without length
|
// Should add default length for char without length
|
||||||
expect(sql).toContain('status char(1)');
|
expect(sql).toContain('"status" char(1)');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not have spaces between char and parentheses', () => {
|
it('should not have spaces between char and parentheses', () => {
|
||||||
@@ -516,7 +596,7 @@ describe('DBML Export - SQL Generation Tests', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Should create a valid table without primary key
|
// Should create a valid table without primary key
|
||||||
expect(sql).toContain('CREATE TABLE experiment_logs');
|
expect(sql).toContain('CREATE TABLE "experiment_logs"');
|
||||||
expect(sql).not.toContain('PRIMARY KEY');
|
expect(sql).not.toContain('PRIMARY KEY');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -631,11 +711,11 @@ describe('DBML Export - SQL Generation Tests', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Should create both tables
|
// Should create both tables
|
||||||
expect(sql).toContain('CREATE TABLE guilds');
|
expect(sql).toContain('CREATE TABLE "guilds"');
|
||||||
expect(sql).toContain('CREATE TABLE guild_members');
|
expect(sql).toContain('CREATE TABLE "guild_members"');
|
||||||
// Should create foreign key
|
// Should create foreign key
|
||||||
expect(sql).toContain(
|
expect(sql).toContain(
|
||||||
'ALTER TABLE guild_members ADD CONSTRAINT fk_guild_members_guild FOREIGN KEY (guild_id) REFERENCES guilds (id)'
|
'ALTER TABLE "guild_members" ADD CONSTRAINT fk_guild_members_guild FOREIGN KEY ("guild_id") REFERENCES "guilds" ("id");'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -709,12 +789,9 @@ describe('DBML Export - SQL Generation Tests', () => {
|
|||||||
isDBMLFlow: true,
|
isDBMLFlow: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Should create schemas
|
|
||||||
expect(sql).toContain('CREATE SCHEMA IF NOT EXISTS transportation');
|
|
||||||
expect(sql).toContain('CREATE SCHEMA IF NOT EXISTS magic');
|
|
||||||
// Should use schema-qualified table names
|
// Should use schema-qualified table names
|
||||||
expect(sql).toContain('CREATE TABLE transportation.portals');
|
expect(sql).toContain('CREATE TABLE "transportation"."portals"');
|
||||||
expect(sql).toContain('CREATE TABLE magic.spells');
|
expect(sql).toContain('CREATE TABLE "magic"."spells"');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -761,7 +838,7 @@ describe('DBML Export - SQL Generation Tests', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Should still create table structure
|
// Should still create table structure
|
||||||
expect(sql).toContain('CREATE TABLE empty_table');
|
expect(sql).toContain('CREATE TABLE "empty_table"');
|
||||||
expect(sql).toContain('(\n\n)');
|
expect(sql).toContain('(\n\n)');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -862,9 +939,9 @@ describe('DBML Export - SQL Generation Tests', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Should include precision and scale
|
// Should include precision and scale
|
||||||
expect(sql).toContain('amount numeric(15, 2)');
|
expect(sql).toContain('"amount" numeric(15, 2)');
|
||||||
// Should include precision only when scale is not provided
|
// Should include precision only when scale is not provided
|
||||||
expect(sql).toContain('interest_rate numeric(5)');
|
expect(sql).toContain('"interest_rate" numeric(5)');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user