admindb 2.4.0 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/LICENSE +20 -20
  2. package/dist/app.js +36 -25
  3. package/dist/core/context.d.ts +1 -0
  4. package/dist/core/context.js +2 -0
  5. package/dist/core/errors.d.ts +1 -0
  6. package/dist/core/errors.js +39 -0
  7. package/dist/core/index.d.ts +1 -0
  8. package/dist/core/index.js +20 -0
  9. package/dist/core/result.d.ts +1 -0
  10. package/dist/core/result.js +10 -0
  11. package/dist/core/router.d.ts +1 -0
  12. package/dist/core/router.js +37 -0
  13. package/dist/data/chain.js +1 -1
  14. package/dist/data/engine.js +1 -1
  15. package/dist/db/database.d.ts +1 -1
  16. package/dist/db/database.js +9 -31
  17. package/dist/db/dialects/index.d.ts +1 -0
  18. package/dist/db/dialects/index.js +58 -0
  19. package/dist/db/dialects/postgres/ddl.d.ts +1 -0
  20. package/dist/db/dialects/postgres/ddl.js +107 -0
  21. package/dist/db/dialects/postgres/dialect.d.ts +1 -0
  22. package/dist/db/dialects/postgres/dialect.js +28 -0
  23. package/dist/db/dialects/postgres/introspector.d.ts +1 -0
  24. package/dist/db/dialects/postgres/introspector.js +265 -0
  25. package/dist/db/dialects/postgres/types.d.ts +1 -0
  26. package/dist/db/dialects/postgres/types.js +83 -0
  27. package/dist/db/dialects/sqlite/ddl.d.ts +1 -0
  28. package/dist/db/dialects/sqlite/ddl.js +244 -0
  29. package/dist/db/dialects/sqlite/dialect.d.ts +1 -0
  30. package/dist/db/dialects/sqlite/dialect.js +25 -0
  31. package/dist/db/dialects/sqlite/introspector.d.ts +1 -0
  32. package/dist/db/dialects/sqlite/introspector.js +214 -0
  33. package/dist/db/dialects/sqlite/types.d.ts +1 -0
  34. package/dist/db/dialects/sqlite/types.js +69 -0
  35. package/dist/db/dialects/types.d.ts +1 -0
  36. package/dist/db/dialects/types.js +15 -0
  37. package/dist/db/export.js +1 -1
  38. package/dist/db/index.d.ts +1 -1
  39. package/dist/db/index.js +1 -1
  40. package/dist/db/manager.d.ts +1 -1
  41. package/dist/db/manager.js +101 -21
  42. package/dist/db/postgres.d.ts +1 -1
  43. package/dist/db/postgres.js +20 -284
  44. package/dist/db/types.d.ts +1 -1
  45. package/dist/db/types.js +0 -4
  46. package/dist/index.d.ts +1 -1
  47. package/dist/index.js +4 -1
  48. package/dist/modules/databases/databases.routes.d.ts +1 -0
  49. package/dist/modules/databases/databases.routes.js +150 -0
  50. package/dist/modules/databases/databases.service.d.ts +1 -0
  51. package/dist/modules/databases/databases.service.js +134 -0
  52. package/dist/modules/databases/index.d.ts +1 -0
  53. package/dist/{routes → modules/databases}/index.js +2 -3
  54. package/dist/modules/erd/erd.routes.d.ts +1 -0
  55. package/dist/modules/erd/erd.routes.js +32 -0
  56. package/dist/modules/erd/erd.service.d.ts +1 -0
  57. package/dist/modules/erd/erd.service.js +39 -0
  58. package/dist/modules/erd/index.d.ts +1 -0
  59. package/dist/modules/erd/index.js +18 -0
  60. package/dist/modules/index.d.ts +1 -0
  61. package/dist/modules/index.js +62 -0
  62. package/dist/modules/query/index.d.ts +1 -0
  63. package/dist/modules/query/index.js +18 -0
  64. package/dist/modules/query/query.routes.d.ts +1 -0
  65. package/dist/modules/query/query.routes.js +54 -0
  66. package/dist/modules/query/query.service.d.ts +1 -0
  67. package/dist/modules/query/query.service.js +95 -0
  68. package/dist/modules/rows/blob.helper.d.ts +1 -0
  69. package/dist/modules/rows/blob.helper.js +40 -0
  70. package/dist/modules/rows/display.helper.d.ts +1 -0
  71. package/dist/modules/rows/display.helper.js +73 -0
  72. package/dist/modules/rows/index.d.ts +1 -0
  73. package/dist/modules/rows/index.js +20 -0
  74. package/dist/modules/rows/rows.routes.d.ts +1 -0
  75. package/dist/modules/rows/rows.routes.js +555 -0
  76. package/dist/modules/rows/rows.service.d.ts +1 -0
  77. package/dist/modules/rows/rows.service.js +433 -0
  78. package/dist/modules/schema/index.d.ts +1 -0
  79. package/dist/modules/schema/index.js +18 -0
  80. package/dist/modules/schema/schema.routes.d.ts +1 -0
  81. package/dist/modules/schema/schema.routes.js +171 -0
  82. package/dist/modules/schema/schema.service.d.ts +1 -0
  83. package/dist/modules/schema/schema.service.js +58 -0
  84. package/dist/modules/seed/index.d.ts +1 -0
  85. package/dist/modules/seed/index.js +18 -0
  86. package/dist/modules/seed/seed.routes.d.ts +1 -0
  87. package/dist/modules/seed/seed.routes.js +212 -0
  88. package/dist/modules/seed/seed.service.d.ts +1 -0
  89. package/dist/{routes/api/helpers.js → modules/seed/seed.service.js} +86 -109
  90. package/dist/modules/tables/index.d.ts +1 -0
  91. package/dist/modules/tables/index.js +18 -0
  92. package/dist/modules/tables/tables.routes.d.ts +1 -0
  93. package/dist/modules/tables/tables.routes.js +194 -0
  94. package/dist/modules/tables/tables.service.d.ts +1 -0
  95. package/dist/modules/tables/tables.service.js +218 -0
  96. package/dist/public/js/browse.js +1 -1
  97. package/dist/public/js/query.js +1 -1
  98. package/dist/types/api.d.ts +1 -1
  99. package/dist/utils/common.d.ts +1 -1
  100. package/dist/utils/common.js +1 -3
  101. package/dist/utils/stream.d.ts +1 -0
  102. package/dist/utils/stream.js +131 -0
  103. package/dist/views/pages/error.hbs +10 -1
  104. package/dist/views/pages/home.hbs +1 -1
  105. package/dist/views/pages/query.hbs +1 -1
  106. package/dist/views/pages/seed-select.hbs +1 -252
  107. package/dist/views/pages/seed.hbs +1 -307
  108. package/dist/views/pages/table.hbs +1 -1
  109. package/dist/views/partials/sidebar.hbs +1 -1
  110. package/docs/API.md +4 -17
  111. package/docs/EXAMPLES.md +579 -579
  112. package/docs/SECURITY.md +265 -265
  113. package/package.json +4 -2
  114. package/dist/db/introspection.d.ts +0 -1
  115. package/dist/db/introspection.js +0 -164
  116. package/dist/routes/api/erd.d.ts +0 -1
  117. package/dist/routes/api/erd.js +0 -45
  118. package/dist/routes/api/helpers.d.ts +0 -1
  119. package/dist/routes/api/import-export.d.ts +0 -1
  120. package/dist/routes/api/import-export.js +0 -84
  121. package/dist/routes/api/index.d.ts +0 -1
  122. package/dist/routes/api/index.js +0 -55
  123. package/dist/routes/api/query.d.ts +0 -1
  124. package/dist/routes/api/query.js +0 -77
  125. package/dist/routes/api/rows.d.ts +0 -1
  126. package/dist/routes/api/rows.js +0 -373
  127. package/dist/routes/api/seed.d.ts +0 -1
  128. package/dist/routes/api/seed.js +0 -180
  129. package/dist/routes/api/tables.d.ts +0 -1
  130. package/dist/routes/api/tables.js +0 -230
  131. package/dist/routes/databases.d.ts +0 -1
  132. package/dist/routes/databases.js +0 -212
  133. package/dist/routes/index.d.ts +0 -1
  134. package/dist/routes/pages.d.ts +0 -1
  135. package/dist/routes/pages.js +0 -540
@@ -1,252 +1 @@
1
- <div class="space-y-6 max-w-5xl mx-auto pb-16">
2
- <div class="page-header">
3
- <div class="page-header-row">
4
- <div>
5
- <div class="page-pretitle">Developer Tools</div>
6
- <h1 class="page-title">Seed Data Generator</h1>
7
- <p class="mt-0.5 text-xs text-base-content/60">
8
- Generate realistic mock datasets with column heuristics or populate complete relational entity graphs.
9
- </p>
10
- </div>
11
- </div>
12
- </div>
13
- {{#if hasTables}}
14
- <div class="grid gap-6 lg:grid-cols-[1fr_380px]">
15
- <div class="space-y-4">
16
- <div class="app-card overflow-hidden">
17
- <div class="p-4 sm:p-5 border-b border-base-200 bg-base-200/30 flex flex-wrap items-center justify-between gap-3">
18
- <div>
19
- <h2 class="text-sm font-bold text-base-content flex items-center gap-2">
20
- <span class="grid h-5 w-5 place-items-center rounded-md bg-primary/15 text-primary text-xs font-bold font-mono">1</span>
21
- Choose Target Table
22
- </h2>
23
- <p class="text-[11px] text-base-content/50 mt-0.5">Select the root table to generate rows for.</p>
24
- </div>
25
- <div class="w-full sm:w-56">
26
- <div class="relative">
27
- <span class="pointer-events-none absolute left-2.5 top-1/2 -translate-y-1/2 text-base-content/40">
28
- {{{icon "search" class="h-3.5 w-3.5"}}}
29
- </span>
30
- <input id="table-search-input" type="text" placeholder="Filter tables…" autocomplete="off"
31
- class="field-input text-xs !py-1 !pl-8 w-full" />
32
- </div>
33
- </div>
34
- </div>
35
- <div id="table-cards-list" class="p-3 sm:p-4 space-y-2 max-h-[480px] overflow-y-auto">
36
- {{#each (or seedTables tables)}}
37
- <label class="table-option-card block cursor-pointer select-none rounded-xl border p-3 transition-all {{#if (eq this.name ../defaultTable)}}border-primary bg-primary/[0.04] shadow-sm{{else}}border-base-200 bg-base-100 hover:border-base-300 hover:bg-base-200/40{{/if}}"
38
- data-table="{{this.name}}">
39
- <div class="flex items-center justify-between gap-3">
40
- <div class="flex items-center gap-3 min-w-0">
41
- <input type="radio" name="selected_table" value="{{this.name}}" class="radio radio-primary radio-xs"
42
- {{#if (eq this.name ../defaultTable)}}checked{{/if}} />
43
- <div class="min-w-0">
44
- <div class="font-mono font-bold text-sm text-base-content truncate flex items-center gap-2">
45
- {{{icon "table" class="h-3.5 w-3.5 text-base-content/50 shrink-0"}}}
46
- <span>{{this.name}}</span>
47
- </div>
48
- <div class="flex items-center gap-2 mt-0.5 text-[11px] text-base-content/50">
49
- <span>{{this.cols}} cols</span>
50
- <span>·</span>
51
- <span>{{this.rowCount}} existing rows</span>
52
- {{#if this.fks}}
53
- <span>·</span>
54
- <span class="text-amber-500 font-medium">{{this.fks}} FK refs</span>
55
- {{/if}}
56
- </div>
57
- </div>
58
- </div>
59
- <div class="flex items-center gap-1 shrink-0">
60
- <button type="button" class="btn btn-ghost btn-xs text-[11px] text-base-content/70 hover:text-primary quick-seed-btn"
61
- data-table="{{this.name}}" data-mode="single" title="Seed this table directly">
62
- Single
63
- </button>
64
- <button type="button" class="btn btn-ghost btn-xs text-[11px] text-emerald-600 hover:text-emerald-500 quick-seed-btn"
65
- data-table="{{this.name}}" data-mode="chain" title="Seed relational ER chain directly">
66
- ER Chain
67
- </button>
68
- </div>
69
- </div>
70
- </label>
71
- {{/each}}
72
- </div>
73
- </div>
74
- </div>
75
- <div class="space-y-4 lg:sticky lg:top-20 self-start">
76
- <div class="app-card overflow-hidden">
77
- <div class="p-4 border-b border-base-200 bg-base-200/30">
78
- <h2 class="text-sm font-bold text-base-content flex items-center gap-2">
79
- <span class="grid h-5 w-5 place-items-center rounded-md bg-primary/15 text-primary text-xs font-bold font-mono">2</span>
80
- Choose Generation Mode
81
- </h2>
82
- <p class="text-[11px] text-base-content/50 mt-0.5">Select how rows should be populated.</p>
83
- </div>
84
- <div class="p-3.5 space-y-2.5">
85
- <label class="mode-option-card block cursor-pointer select-none rounded-xl border p-3.5 transition-all {{#if (eq defaultMode 'single')}}border-primary bg-primary/[0.04] shadow-sm{{else}}border-base-200 bg-base-100 hover:border-base-300 hover:bg-base-200/40{{/if}}"
86
- data-mode="single">
87
- <div class="flex items-start gap-3">
88
- <input type="radio" name="selected_mode" value="single" class="radio radio-primary radio-xs mt-0.5"
89
- {{#if (eq defaultMode 'single')}}checked{{/if}} />
90
- <div class="space-y-1">
91
- <div class="flex items-center gap-2">
92
- <span class="font-bold text-xs text-base-content">Single Table Seeder</span>
93
- <span class="badge badge-xs badge-ghost text-[10px] font-mono">Standalone</span>
94
- </div>
95
- <p class="text-[11px] text-base-content/60 leading-relaxed">
96
- Generates realistic rows for the selected table with automated column heuristics (names, emails, dates, addresses, UUIDs) and live formula templates.
97
- </p>
98
- </div>
99
- </div>
100
- </label>
101
- <label class="mode-option-card block cursor-pointer select-none rounded-xl border p-3.5 transition-all {{#if (eq defaultMode 'chain')}}border-primary bg-primary/[0.04] shadow-sm{{else}}border-base-200 bg-base-100 hover:border-base-300 hover:bg-base-200/40{{/if}}"
102
- data-mode="chain">
103
- <div class="flex items-start gap-3">
104
- <input type="radio" name="selected_mode" value="chain" class="radio radio-primary radio-xs mt-0.5"
105
- {{#if (eq defaultMode 'chain')}}checked{{/if}} />
106
- <div class="space-y-1">
107
- <div class="flex items-center gap-2">
108
- <span class="font-bold text-xs text-base-content">Relational ER Chain Seeder</span>
109
- <span class="badge badge-xs badge-outline badge-success text-[10px] font-mono">Full Graph</span>
110
- </div>
111
- <p class="text-[11px] text-base-content/60 leading-relaxed">
112
- Analyzes schema graph foreign keys and seeds related parent & child entities in topological order to guarantee full relational integrity.
113
- </p>
114
- </div>
115
- </div>
116
- </label>
117
- </div>
118
- <div class="p-3.5 border-t border-base-200 bg-base-200/20">
119
- <button id="btn-launch-seed" type="button" class="btn btn-primary btn-sm w-full gap-2 font-bold shadow-sm">
120
- {{{icon "seed" class="h-4 w-4"}}}
121
- <span id="launch-btn-label">Open Seed Generator</span>
122
- {{{icon "arrowRight" class="h-3.5 w-3.5"}}}
123
- </button>
124
- </div>
125
- </div>
126
- <div class="p-3.5 rounded-xl bg-base-200/50 border border-base-300 text-xs space-y-1 text-base-content/70">
127
- <div class="font-semibold text-base-content flex items-center gap-1.5 text-[11px]">
128
- {{{icon "info" class="h-3.5 w-3.5 text-primary"}}}
129
- Deterministic Generation
130
- </div>
131
- <p class="text-[11px] leading-relaxed text-base-content/60">
132
- You can specify an exact numeric seed for deterministic reproducible data, or import/export seed profiles.
133
- </p>
134
- </div>
135
- </div>
136
- </div>
137
- {{else}}
138
- <div class="empty-state">
139
- <span class="empty-icon">{{{icon "table" class="h-7 w-7"}}}</span>
140
- <p class="text-sm font-medium text-base-content/70">No tables available in the database</p>
141
- <p class="text-xs text-base-content/50 mt-1 max-w-sm">Create a table using the visual designer or run SQL migrations before generating seed data.</p>
142
- <div class="mt-4 flex gap-2">
143
- <a href="{{basePath}}/designer/{{encodeUri dbId}}" class="btn btn-primary btn-sm gap-1.5">
144
- {{{icon "plus" class="h-4 w-4"}}}
145
- Create table
146
- </a>
147
- <a href="{{basePath}}/query/{{encodeUri dbId}}" class="btn btn-outline btn-sm gap-1.5">
148
- {{{icon "query" class="h-4 w-4"}}}
149
- Query editor
150
- </a>
151
- </div>
152
- </div>
153
- {{/if}}
154
- </div>
155
- <script>
156
- document.addEventListener('DOMContentLoaded', () => {
157
- const BASE = '{{basePath}}';
158
- const DB_ID = '{{dbId}}';
159
- let selectedTable = '{{defaultTable}}';
160
- let selectedMode = '{{defaultMode}}' || 'single';
161
- const tableCards = document.querySelectorAll('.table-option-card');
162
- const modeCards = document.querySelectorAll('.mode-option-card');
163
- const launchBtn = document.getElementById('btn-launch-seed');
164
- const launchLabel = document.getElementById('launch-btn-label');
165
- const searchInput = document.getElementById('table-search-input');
166
- function updateLaunchLabel() {
167
- if (launchLabel) {
168
- launchLabel.textContent = selectedTable
169
- ? `Seed "${selectedTable}" (${selectedMode === 'chain' ? 'ER Chain' : 'Single Table'})`
170
- : 'Open Seed Generator';
171
- }
172
- }
173
- function setSelectedTable(tableName) {
174
- selectedTable = tableName;
175
- tableCards.forEach(card => {
176
- const isMatch = card.dataset.table === tableName;
177
- const radio = card.querySelector('input[type="radio"]');
178
- if (radio) radio.checked = isMatch;
179
- if (isMatch) {
180
- card.classList.add('border-primary', 'bg-primary/[0.04]', 'shadow-sm');
181
- card.classList.remove('border-base-200', 'bg-base-100');
182
- } else {
183
- card.classList.remove('border-primary', 'bg-primary/[0.04]', 'shadow-sm');
184
- card.classList.add('border-base-200', 'bg-base-100');
185
- }
186
- });
187
- updateLaunchLabel();
188
- }
189
- function setSelectedMode(mode) {
190
- selectedMode = mode;
191
- modeCards.forEach(card => {
192
- const isMatch = card.dataset.mode === mode;
193
- const radio = card.querySelector('input[type="radio"]');
194
- if (radio) radio.checked = isMatch;
195
- if (isMatch) {
196
- card.classList.add('border-primary', 'bg-primary/[0.04]', 'shadow-sm');
197
- card.classList.remove('border-base-200', 'bg-base-100');
198
- } else {
199
- card.classList.remove('border-primary', 'bg-primary/[0.04]', 'shadow-sm');
200
- card.classList.add('border-base-200', 'bg-base-100');
201
- }
202
- });
203
- updateLaunchLabel();
204
- }
205
- tableCards.forEach(card => {
206
- card.addEventListener('click', (e) => {
207
- if (e.target.closest('.quick-seed-btn')) return;
208
- const table = card.dataset.table;
209
- if (table) setSelectedTable(table);
210
- });
211
- });
212
- modeCards.forEach(card => {
213
- card.addEventListener('click', () => {
214
- const mode = card.dataset.mode;
215
- if (mode) setSelectedMode(mode);
216
- });
217
- });
218
- // Direct quick-seed buttons
219
- document.querySelectorAll('.quick-seed-btn').forEach(btn => {
220
- btn.addEventListener('click', (e) => {
221
- e.stopPropagation();
222
- const table = btn.dataset.table;
223
- const mode = btn.dataset.mode || 'single';
224
- if (table) {
225
- window.location.href = `${BASE}/seed/${encodeURIComponent(DB_ID)}/${encodeURIComponent(table)}?mode=${encodeURIComponent(mode)}`;
226
- }
227
- });
228
- });
229
- // Launch button
230
- if (launchBtn) {
231
- launchBtn.addEventListener('click', () => {
232
- if (!selectedTable) return;
233
- window.location.href = `${BASE}/seed/${encodeURIComponent(DB_ID)}/${encodeURIComponent(selectedTable)}?mode=${encodeURIComponent(selectedMode)}`;
234
- });
235
- }
236
- // Real-time table search filter
237
- if (searchInput) {
238
- searchInput.addEventListener('input', () => {
239
- const q = searchInput.value.trim().toLowerCase();
240
- tableCards.forEach(card => {
241
- const name = (card.dataset.table || '').toLowerCase();
242
- if (!q || name.includes(q)) {
243
- card.classList.remove('hidden');
244
- } else {
245
- card.classList.add('hidden');
246
- }
247
- });
248
- });
249
- }
250
- updateLaunchLabel();
251
- });
252
- </script>
1
+ <div class="space-y-6 max-w-5xl mx-auto pb-16">
253
2
  <div class="page-header">
254
3
  <div class="page-header-row">
255
4
  <div>
256
5
  <div class="page-pretitle">Developer Tools</div>
257
6
  <h1 class="page-title">Seed Data Generator</h1>
258
7
  <p class="mt-0.5 text-xs text-base-content/60">
259
8
  Generate realistic mock datasets with column heuristics or populate complete relational entity graphs.
260
9
  </p>
261
10
  </div>
262
11
  </div>
263
12
  </div>
264
13
  {{#if hasTables}}
265
14
  <div class="grid gap-6 lg:grid-cols-[1fr_380px]">
266
15
  <div class="space-y-4">
267
16
  <div class="app-card overflow-hidden">
268
17
  <div class="p-4 sm:p-5 border-b border-base-200 bg-base-200/30 flex flex-wrap items-center justify-between gap-3">
269
18
  <div>
270
19
  <h2 class="text-sm font-bold text-base-content flex items-center gap-2">
271
20
  <span class="grid h-5 w-5 place-items-center rounded-md bg-primary/15 text-primary text-xs font-bold font-mono">1</span>
272
21
  Choose Target Table
273
22
  </h2>
274
23
  <p class="text-[11px] text-base-content/50 mt-0.5">Select the root table to generate rows for.</p>
275
24
  </div>
276
25
  <div class="w-full sm:w-56">
277
26
  <div class="relative">
278
27
  <span class="pointer-events-none absolute left-2.5 top-1/2 -translate-y-1/2 text-base-content/40">
279
28
  {{{icon "search" class="h-3.5 w-3.5"}}}
280
29
  </span>
281
30
  <input id="table-search-input" type="text" placeholder="Filter tables…" autocomplete="off"
282
31
  class="field-input text-xs !py-1 !pl-8 w-full" />
283
32
  </div>
284
33
  </div>
285
34
  </div>
286
35
  <div id="table-cards-list" class="p-3 sm:p-4 space-y-2 max-h-[480px] overflow-y-auto">
287
36
  {{#each (or seedTables tables)}}
288
37
  <label class="table-option-card block cursor-pointer select-none rounded-xl border p-3 transition-all {{#if (eq this.name ../defaultTable)}}border-primary bg-primary/[0.04] shadow-sm{{else}}border-base-200 bg-base-100 hover:border-base-300 hover:bg-base-200/40{{/if}}"
289
38
  data-table="{{this.name}}">
290
39
  <div class="flex items-center justify-between gap-3">
291
40
  <div class="flex items-center gap-3 min-w-0">
292
41
  <input type="radio" name="selected_table" value="{{this.name}}" class="radio radio-primary radio-xs"
293
42
  {{#if (eq this.name ../defaultTable)}}checked{{/if}} />
294
43
  <div class="min-w-0">
295
44
  <div class="font-mono font-bold text-sm text-base-content truncate flex items-center gap-2">
296
45
  {{{icon "table" class="h-3.5 w-3.5 text-base-content/50 shrink-0"}}}
297
46
  <span>{{this.name}}</span>
298
47
  </div>
299
48
  <div class="flex items-center gap-2 mt-0.5 text-[11px] text-base-content/50">
300
49
  <span>{{this.cols}} cols</span>
301
50
  <span>·</span>
302
51
  <span>{{this.rowCount}} existing rows</span>
303
52
  {{#if this.fks}}
304
53
  <span>·</span>
305
54
  <span class="text-amber-500 font-medium">{{this.fks}} FK refs</span>
306
55
  {{/if}}
307
56
  </div>
308
57
  </div>
309
58
  </div>
310
59
  <div class="flex items-center gap-1 shrink-0">
311
60
  <button type="button" class="btn btn-ghost btn-xs text-[11px] text-base-content/70 hover:text-primary quick-seed-btn"
312
61
  data-table="{{this.name}}" data-mode="single" title="Seed this table directly">
313
62
  Single
314
63
  </button>
315
64
  <button type="button" class="btn btn-ghost btn-xs text-[11px] text-emerald-600 hover:text-emerald-500 quick-seed-btn"
316
65
  data-table="{{this.name}}" data-mode="chain" title="Seed relational ER chain directly">
317
66
  ER Chain
318
67
  </button>
319
68
  </div>
320
69
  </div>
321
70
  </label>
322
71
  {{/each}}
323
72
  </div>
324
73
  </div>
325
74
  </div>
326
75
  <div class="space-y-4 lg:sticky lg:top-20 self-start">
327
76
  <div class="app-card overflow-hidden">
328
77
  <div class="p-4 border-b border-base-200 bg-base-200/30">
329
78
  <h2 class="text-sm font-bold text-base-content flex items-center gap-2">
330
79
  <span class="grid h-5 w-5 place-items-center rounded-md bg-primary/15 text-primary text-xs font-bold font-mono">2</span>
331
80
  Choose Generation Mode
332
81
  </h2>
333
82
  <p class="text-[11px] text-base-content/50 mt-0.5">Select how rows should be populated.</p>
334
83
  </div>
335
84
  <div class="p-3.5 space-y-2.5">
336
85
  <label class="mode-option-card block cursor-pointer select-none rounded-xl border p-3.5 transition-all {{#if (eq defaultMode 'single')}}border-primary bg-primary/[0.04] shadow-sm{{else}}border-base-200 bg-base-100 hover:border-base-300 hover:bg-base-200/40{{/if}}"
337
86
  data-mode="single">
338
87
  <div class="flex items-start gap-3">
339
88
  <input type="radio" name="selected_mode" value="single" class="radio radio-primary radio-xs mt-0.5"
340
89
  {{#if (eq defaultMode 'single')}}checked{{/if}} />
341
90
  <div class="space-y-1">
342
91
  <div class="flex items-center gap-2">
343
92
  <span class="font-bold text-xs text-base-content">Single Table Seeder</span>
344
93
  <span class="badge badge-xs badge-ghost text-[10px] font-mono">Standalone</span>
345
94
  </div>
346
95
  <p class="text-[11px] text-base-content/60 leading-relaxed">
347
96
  Generates realistic rows for the selected table with automated column heuristics (names, emails, dates, addresses, UUIDs) and live formula templates.
348
97
  </p>
349
98
  </div>
350
99
  </div>
351
100
  </label>
352
101
  <label class="mode-option-card block cursor-pointer select-none rounded-xl border p-3.5 transition-all {{#if (eq defaultMode 'chain')}}border-primary bg-primary/[0.04] shadow-sm{{else}}border-base-200 bg-base-100 hover:border-base-300 hover:bg-base-200/40{{/if}}"
353
102
  data-mode="chain">
354
103
  <div class="flex items-start gap-3">
355
104
  <input type="radio" name="selected_mode" value="chain" class="radio radio-primary radio-xs mt-0.5"
356
105
  {{#if (eq defaultMode 'chain')}}checked{{/if}} />
357
106
  <div class="space-y-1">
358
107
  <div class="flex items-center gap-2">
359
108
  <span class="font-bold text-xs text-base-content">Relational ER Chain Seeder</span>
360
109
  <span class="badge badge-xs badge-outline badge-success text-[10px] font-mono">Full Graph</span>
361
110
  </div>
362
111
  <p class="text-[11px] text-base-content/60 leading-relaxed">
363
112
  Analyzes schema graph foreign keys and seeds related parent & child entities in topological order to guarantee full relational integrity.
364
113
  </p>
365
114
  </div>
366
115
  </div>
367
116
  </label>
368
117
  </div>
369
118
  <div class="p-3.5 border-t border-base-200 bg-base-200/20">
370
119
  <button id="btn-launch-seed" type="button" class="btn btn-primary btn-sm w-full gap-2 font-bold shadow-sm">
371
120
  {{{icon "seed" class="h-4 w-4"}}}
372
121
  <span id="launch-btn-label">Open Seed Generator</span>
373
122
  {{{icon "arrowRight" class="h-3.5 w-3.5"}}}
374
123
  </button>
375
124
  </div>
376
125
  </div>
377
126
  <div class="p-3.5 rounded-xl bg-base-200/50 border border-base-300 text-xs space-y-1 text-base-content/70">
378
127
  <div class="font-semibold text-base-content flex items-center gap-1.5 text-[11px]">
379
128
  {{{icon "info" class="h-3.5 w-3.5 text-primary"}}}
380
129
  Deterministic Generation
381
130
  </div>
382
131
  <p class="text-[11px] leading-relaxed text-base-content/60">
383
132
  You can specify an exact numeric seed for deterministic reproducible data, or import/export seed profiles.
384
133
  </p>
385
134
  </div>
386
135
  </div>
387
136
  </div>
388
137
  {{else}}
389
138
  <div class="empty-state">
390
139
  <span class="empty-icon">{{{icon "table" class="h-7 w-7"}}}</span>
391
140
  <p class="text-sm font-medium text-base-content/70">No tables available in the database</p>
392
141
  <p class="text-xs text-base-content/50 mt-1 max-w-sm">Create a table using the visual designer or run SQL migrations before generating seed data.</p>
393
142
  <div class="mt-4 flex gap-2">
394
143
  <a href="{{basePath}}/designer/{{encodeUri dbId}}" class="btn btn-primary btn-sm gap-1.5">
395
144
  {{{icon "plus" class="h-4 w-4"}}}
396
145
  Create table
397
146
  </a>
398
147
  <a href="{{basePath}}/query/{{encodeUri dbId}}" class="btn btn-outline btn-sm gap-1.5">
399
148
  {{{icon "query" class="h-4 w-4"}}}
400
149
  Query editor
401
150
  </a>
402
151
  </div>
403
152
  </div>
404
153
  {{/if}}
405
154
  const BASE = '{{basePath}}';
406
155
  const DB_ID = '{{dbId}}';
407
156
  let selectedTable = '{{defaultTable}}';
408
157
  let selectedMode = '{{defaultMode}}' || 'single';
409
158
  const tableCards = document.querySelectorAll('.table-option-card');
410
159
  const modeCards = document.querySelectorAll('.mode-option-card');
411
160
  const launchBtn = document.getElementById('btn-launch-seed');
412
161
  const launchLabel = document.getElementById('launch-btn-label');
413
162
  const searchInput = document.getElementById('table-search-input');
414
163
  function updateLaunchLabel() {
415
164
  if (launchLabel) {
416
165
  launchLabel.textContent = selectedTable
417
166
  ? `Seed "${selectedTable}" (${selectedMode === 'chain' ? 'ER Chain' : 'Single Table'})`
418
167
  : 'Open Seed Generator';
419
168
  }
420
169
  }
421
170
  function setSelectedTable(tableName) {
422
171
  selectedTable = tableName;
423
172
  tableCards.forEach(card => {
424
173
  const isMatch = card.dataset.table === tableName;
425
174
  const radio = card.querySelector('input[type="radio"]');
426
175
  if (radio) radio.checked = isMatch;
427
176
  if (isMatch) {
428
177
  card.classList.add('border-primary', 'bg-primary/[0.04]', 'shadow-sm');
429
178
  card.classList.remove('border-base-200', 'bg-base-100');
430
179
  } else {
431
180
  card.classList.remove('border-primary', 'bg-primary/[0.04]', 'shadow-sm');
432
181
  card.classList.add('border-base-200', 'bg-base-100');
433
182
  }
434
183
  });
435
184
  updateLaunchLabel();
436
185
  }
437
186
  function setSelectedMode(mode) {
438
187
  selectedMode = mode;
439
188
  modeCards.forEach(card => {
440
189
  const isMatch = card.dataset.mode === mode;
441
190
  const radio = card.querySelector('input[type="radio"]');
442
191
  if (radio) radio.checked = isMatch;
443
192
  if (isMatch) {
444
193
  card.classList.add('border-primary', 'bg-primary/[0.04]', 'shadow-sm');
445
194
  card.classList.remove('border-base-200', 'bg-base-100');
446
195
  } else {
447
196
  card.classList.remove('border-primary', 'bg-primary/[0.04]', 'shadow-sm');
448
197
  card.classList.add('border-base-200', 'bg-base-100');
449
198
  }
450
199
  });
451
200
  updateLaunchLabel();
452
201
  }
453
202
  tableCards.forEach(card => {
454
203
  card.addEventListener('click', (e) => {
455
204
  if (e.target.closest('.quick-seed-btn')) return;
456
205
  const table = card.dataset.table;
457
206
  if (table) setSelectedTable(table);
458
207
  });
459
208
  });
460
209
  modeCards.forEach(card => {
461
210
  card.addEventListener('click', () => {
462
211
  const mode = card.dataset.mode;
463
212
  if (mode) setSelectedMode(mode);
464
213
  });
465
214
  });
466
215
  // Direct quick-seed buttons
467
216
  document.querySelectorAll('.quick-seed-btn').forEach(btn => {
468
217
  btn.addEventListener('click', (e) => {
469
218
  e.stopPropagation();
470
219
  const table = btn.dataset.table;
471
220
  const mode = btn.dataset.mode || 'single';
472
221
  if (table) {
473
222
  window.location.href = `${BASE}/seed/${encodeURIComponent(DB_ID)}/${encodeURIComponent(table)}?mode=${encodeURIComponent(mode)}`;
474
223
  }
475
224
  });
476
225
  });
477
226
  // Launch button
478
227
  if (launchBtn) {
479
228
  launchBtn.addEventListener('click', () => {
480
229
  if (!selectedTable) return;
481
230
  window.location.href = `${BASE}/seed/${encodeURIComponent(DB_ID)}/${encodeURIComponent(selectedTable)}?mode=${encodeURIComponent(selectedMode)}`;
482
231
  });
483
232
  }
484
233
  // Real-time table search filter
485
234
  if (searchInput) {
486
235
  searchInput.addEventListener('input', () => {
487
236
  const q = searchInput.value.trim().toLowerCase();
488
237
  tableCards.forEach(card => {
489
238
  const name = (card.dataset.table || '').toLowerCase();
490
239
  if (!q || name.includes(q)) {
491
240
  card.classList.remove('hidden');
492
241
  } else {
493
242
  card.classList.add('hidden');
494
243
  }
495
244
  });
496
245
  });
497
246
  }
498
247
  updateLaunchLabel();