admindb 2.3.1 → 2.4.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.
- package/README.md +16 -16
- package/dist/app.js +119 -171
- package/dist/public/css/app.css +1 -1
- package/dist/public/js/browse.js +1 -1
- package/dist/public/js/databases.js +1 -1
- package/dist/public/js/designer.js +1 -1
- package/dist/public/js/forms.js +1 -1
- package/dist/public/js/home.js +1 -1
- package/dist/public/js/inspector.js +1 -1
- package/dist/public/js/query.js +1 -1
- package/dist/public/js/schema.js +1 -1
- package/dist/public/js/seed.js +1 -1
- package/dist/routes/api/erd.js +2 -2
- package/dist/routes/api/helpers.d.ts +1 -1
- package/dist/routes/api/import-export.js +8 -6
- package/dist/routes/api/index.js +16 -9
- package/dist/routes/api/query.js +8 -5
- package/dist/routes/api/rows.js +36 -21
- package/dist/routes/api/seed.js +29 -16
- package/dist/routes/api/tables.js +32 -18
- package/dist/routes/databases.js +2 -0
- package/dist/routes/pages.d.ts +1 -1
- package/dist/routes/pages.js +137 -43
- package/dist/views/layouts/main.hbs +1 -1
- package/dist/views/pages/databases.hbs +1 -1
- package/dist/views/pages/erd.hbs +1 -1
- package/dist/views/pages/form.hbs +1 -1
- package/dist/views/pages/home.hbs +1 -1
- package/dist/views/pages/info.hbs +1 -1
- package/dist/views/pages/schema.hbs +1 -1
- package/dist/views/pages/seed-select.hbs +6 -5
- package/dist/views/pages/seed.hbs +3 -3
- package/dist/views/pages/table.hbs +1 -1
- package/dist/views/partials/navbar.hbs +1 -1
- package/dist/views/partials/sidebar.hbs +1 -1
- package/docs/API.md +60 -55
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
<div class="space-y-6">
|
|
2
1
|
<div class="page-header">
|
|
3
2
|
<div class="page-header-row">
|
|
4
3
|
<div class="flex items-center gap-3">
|
|
5
4
|
<a href="{{basePath}}/tables/{{table}}" class="btn btn-circle btn-ghost btn-sm" aria-label="Back to table">
|
|
6
5
|
{{{icon "arrowLeft" class="h-4 w-4"}}}
|
|
7
6
|
</a>
|
|
8
7
|
<div>
|
|
9
8
|
<div class="page-pretitle">Schema Editor</div>
|
|
10
9
|
<h1 class="page-title font-mono text-xl sm:text-2xl">{{table}}</h1>
|
|
11
10
|
</div>
|
|
12
11
|
</div>
|
|
13
12
|
<div class="page-actions">
|
|
14
13
|
<a href="{{basePath}}/tables/{{table}}" class="btn btn-ghost btn-sm gap-1.5">
|
|
15
14
|
{{{icon "table" class="h-4 w-4"}}}
|
|
16
15
|
View data
|
|
17
16
|
</a>
|
|
18
17
|
<a href="{{basePath}}/query" class="btn btn-outline btn-sm gap-1.5">
|
|
19
18
|
{{{icon "query" class="h-4 w-4"}}}
|
|
20
19
|
Query
|
|
21
20
|
</a>
|
|
22
21
|
</div>
|
|
23
22
|
</div>
|
|
24
23
|
</div>
|
|
25
24
|
<div class="app-card p-5">
|
|
26
25
|
<div class="flex flex-wrap items-end gap-3">
|
|
27
26
|
<div class="w-full max-w-sm">
|
|
28
27
|
<label class="field-label" for="table-rename-input">Table name</label>
|
|
29
28
|
<input id="table-rename-input" class="field-input font-mono" value="{{table}}" spellcheck="false" autocomplete="off" {{#if isInternal}}disabled{{/if}} {{#if readonly}}disabled{{/if}} />
|
|
30
29
|
</div>
|
|
31
30
|
<button id="rename-table" type="button" class="btn btn-primary btn-sm mb-0.5" {{#if isInternal}}disabled{{/if}} {{#if readonly}}disabled{{/if}}>
|
|
32
31
|
{{{icon "edit" class="h-4 w-4"}}}
|
|
33
32
|
Rename table
|
|
34
33
|
</button>
|
|
35
34
|
{{#if isInternal}}<span class="mb-1 text-xs text-base-content/45">Internal tables cannot be renamed.</span>{{/if}}
|
|
36
35
|
</div>
|
|
37
36
|
</div>
|
|
38
37
|
<div class="app-card overflow-hidden">
|
|
39
38
|
<div class="flex flex-wrap items-center justify-between gap-2 border-b border-base-200 px-5 py-4">
|
|
40
39
|
<h2 class="text-sm font-semibold uppercase tracking-wide text-base-content/45">
|
|
41
40
|
Columns
|
|
42
41
|
<span class="ml-1 rounded-md bg-base-200 px-1.5 py-0.5 text-[10px] text-base-content/50">{{schema.columns.length}}</span>
|
|
43
42
|
</h2>
|
|
44
43
|
<button id="add-column" type="button" class="btn btn-outline btn-sm gap-1.5" {{#if readonly}}disabled{{/if}}>
|
|
45
44
|
{{{icon "plus" class="h-4 w-4"}}}
|
|
46
45
|
Add column
|
|
47
46
|
</button>
|
|
48
47
|
</div>
|
|
49
48
|
<div id="add-column-panel" class="hidden border-b border-base-200 p-4">
|
|
50
49
|
<div class="mb-2 text-xs font-bold uppercase tracking-wider text-primary">Add New Column</div>
|
|
51
50
|
<div id="add-column-form" class="grid grid-cols-2 items-end gap-2.5 sm:grid-cols-12"></div>
|
|
52
51
|
<div id="add-column-error" class="hidden mt-3"></div>
|
|
53
52
|
<div class="mt-3.5 flex gap-2">
|
|
54
53
|
<button id="add-column-submit" type="button" class="btn btn-primary btn-sm">Add column</button>
|
|
55
54
|
<button id="add-column-cancel" type="button" class="btn btn-ghost btn-sm">Cancel</button>
|
|
56
55
|
</div>
|
|
57
56
|
</div>
|
|
58
57
|
<div id="edit-column-panel" class="hidden border-b border-base-200 bg-base-200/30 p-4">
|
|
59
58
|
<div class="mb-2.5 flex items-center justify-between">
|
|
60
59
|
<div class="flex items-center gap-2">
|
|
61
60
|
<span class="text-xs font-bold uppercase tracking-wider text-primary">Edit Column:</span>
|
|
62
61
|
<span id="edit-column-target" class="font-mono text-sm font-bold text-base-content"></span>
|
|
63
62
|
</div>
|
|
64
63
|
<span class="text-[11px] text-base-content/45">Change data type, default value, primary key, nullability, uniqueness, or foreign key.</span>
|
|
65
64
|
</div>
|
|
66
65
|
<div id="edit-column-form" class="grid grid-cols-2 items-end gap-2.5 sm:grid-cols-12"></div>
|
|
67
66
|
<div id="edit-column-error" class="hidden mt-3"></div>
|
|
68
67
|
<div class="mt-3.5 flex gap-2">
|
|
69
68
|
<button id="edit-column-submit" type="button" class="btn btn-primary btn-sm">Save Changes</button>
|
|
70
69
|
<button id="edit-column-cancel" type="button" class="btn btn-ghost btn-sm">Cancel</button>
|
|
71
70
|
</div>
|
|
72
71
|
</div>
|
|
73
72
|
<div class="overflow-x-auto">
|
|
74
73
|
<table class="table table-sm">
|
|
75
74
|
<thead>
|
|
76
75
|
<tr class="bg-base-200/70 text-xs uppercase tracking-wide text-base-content/50">
|
|
77
76
|
<th class="font-semibold">Name</th>
|
|
78
77
|
<th class="font-semibold">Type</th>
|
|
79
78
|
<th class="font-semibold">Constraints</th>
|
|
80
79
|
<th class="font-semibold">Default</th>
|
|
81
80
|
<th class="text-right font-semibold">Actions</th>
|
|
82
81
|
</tr>
|
|
83
82
|
</thead>
|
|
84
83
|
<tbody>
|
|
85
84
|
{{#each schema.columns}}
|
|
86
85
|
<tr class="hover:bg-primary/[0.04]">
|
|
87
86
|
<td class="col-name-cell align-middle" data-column="{{this.name}}">
|
|
88
87
|
<span class="name-display font-mono text-[13px] font-semibold">{{this.name}}</span>
|
|
89
88
|
</td>
|
|
90
89
|
<td class="align-middle"><span class="chip chip-type">{{this.type}}</span></td>
|
|
91
90
|
<td class="align-middle">
|
|
92
91
|
<div class="flex flex-wrap gap-1">
|
|
93
92
|
{{#if (gt this.pk 0)}}<span class="chip chip-pk">PK</span>{{/if}}
|
|
94
93
|
{{#if this.notnull}}<span class="chip chip-type">NOT NULL</span>{{/if}}
|
|
95
94
|
{{#if this.unique}}{{#unless (gt this.pk 0)}}<span class="chip chip-type">UNIQUE</span>{{/unless}}{{/if}}
|
|
96
95
|
{{#if this.fk}}<span class="chip chip-type">FK → {{this.fk.table}}.{{this.fk.to}}</span>{{/if}}
|
|
97
96
|
{{#if this.indexed}}<span class="chip chip-type">INDEX</span>{{/if}}
|
|
98
97
|
{{#if this.referencedBy.length}}<span class="chip chip-null" title="Referenced by {{this.referencedBy.length}} table(s)">REF × {{this.referencedBy.length}}</span>{{/if}}
|
|
99
98
|
</div>
|
|
100
99
|
</td>
|
|
101
100
|
<td class="align-middle font-mono text-[13px] text-base-content/60">
|
|
102
101
|
{{#if this.dflt_value}}{{this.dflt_value}}{{else}}<span class="text-base-content/25">—</span>{{/if}}
|
|
103
102
|
</td>
|
|
104
103
|
<td class="whitespace-nowrap text-right align-middle">
|
|
105
104
|
<div class="flex items-center justify-end gap-1">
|
|
106
105
|
<button type="button" class="btn btn-ghost btn-xs gap-1 text-base-content/70 hover:text-primary col-edit"
|
|
107
106
|
data-column="{{this.name}}"
|
|
108
107
|
data-type="{{this.type}}"
|
|
109
108
|
data-pk="{{#if (gt this.pk 0)}}true{{else}}false{{/if}}"
|
|
110
109
|
data-notnull="{{#if this.notnull}}true{{else}}false{{/if}}"
|
|
111
110
|
data-unique="{{#if this.unique}}true{{else}}false{{/if}}"
|
|
112
111
|
data-default="{{this.dflt_value}}"
|
|
113
112
|
data-fk-table="{{this.fk.table}}"
|
|
114
113
|
data-fk-to="{{this.fk.to}}"
|
|
115
114
|
{{#if ../readonly}}disabled{{/if}}>
|
|
116
115
|
{{{icon "edit" class="h-3.5 w-3.5"}}}
|
|
117
116
|
Edit
|
|
118
117
|
</button>
|
|
119
118
|
<button type="button" class="btn btn-ghost btn-xs gap-1 text-error col-drop" {{#if ../readonly}}disabled{{/if}} {{#unless this.canDrop}}disabled title="{{join this.dropBlockers "; "}}"{{/unless}}>
|
|
120
119
|
{{{icon "trash" class="h-3.5 w-3.5"}}}
|
|
121
120
|
Delete
|
|
122
121
|
</button>
|
|
123
122
|
</div>
|
|
124
123
|
</td>
|
|
125
124
|
</tr>
|
|
126
125
|
{{/each}}
|
|
127
126
|
</tbody>
|
|
128
127
|
</table>
|
|
129
128
|
</div>
|
|
130
129
|
</div>
|
|
131
130
|
{{#if schema.foreignKeys.length}}
|
|
132
131
|
<div class="app-card overflow-hidden">
|
|
133
132
|
<div class="border-b border-base-200 px-5 py-4">
|
|
134
133
|
<h2 class="text-sm font-semibold uppercase tracking-wide text-base-content/45">Foreign keys</h2>
|
|
135
134
|
</div>
|
|
136
135
|
<div class="overflow-x-auto">
|
|
137
136
|
<table class="table table-sm">
|
|
138
137
|
<thead>
|
|
139
138
|
<tr class="bg-base-200/70 text-xs uppercase tracking-wide text-base-content/50">
|
|
140
139
|
<th class="font-semibold">Column</th>
|
|
141
140
|
<th class="font-semibold">References</th>
|
|
142
141
|
<th class="font-semibold">On delete</th>
|
|
143
142
|
<th class="font-semibold">On update</th>
|
|
144
143
|
</tr>
|
|
145
144
|
</thead>
|
|
146
145
|
<tbody>
|
|
147
146
|
{{#each schema.foreignKeys}}
|
|
148
147
|
<tr class="hover:bg-primary/[0.04]">
|
|
149
148
|
<td class="align-middle font-mono text-[13px]">{{this.from}}</td>
|
|
150
149
|
<td class="align-middle font-mono text-[13px] text-primary">{{this.table}}.{{this.to}}</td>
|
|
151
150
|
<td class="align-middle text-[13px] text-base-content/60">{{this.on_delete}}</td>
|
|
152
151
|
<td class="align-middle text-[13px] text-base-content/60">{{this.on_update}}</td>
|
|
153
152
|
</tr>
|
|
154
153
|
{{/each}}
|
|
155
154
|
</tbody>
|
|
156
155
|
</table>
|
|
157
156
|
</div>
|
|
158
157
|
</div>
|
|
159
158
|
{{/if}}
|
|
160
159
|
<div class="app-card overflow-hidden">
|
|
161
160
|
<div class="flex flex-wrap items-center justify-between gap-2 border-b border-base-200 px-5 py-4">
|
|
162
161
|
<h2 class="text-sm font-semibold uppercase tracking-wide text-base-content/45">
|
|
163
162
|
Indexes
|
|
164
163
|
<span class="ml-1 rounded-md bg-base-200 px-1.5 py-0.5 text-[10px] text-base-content/50">{{schema.indexes.length}}</span>
|
|
165
164
|
</h2>
|
|
166
165
|
<button id="add-index" type="button" class="btn btn-outline btn-sm gap-1.5" {{#if readonly}}disabled{{/if}}>
|
|
167
166
|
{{{icon "plus" class="h-4 w-4"}}}
|
|
168
167
|
Add index
|
|
169
168
|
</button>
|
|
170
169
|
</div>
|
|
171
170
|
<div id="add-index-panel" class="hidden border-b border-base-200 p-4">
|
|
172
171
|
<div class="grid grid-cols-1 gap-3 sm:grid-cols-3">
|
|
173
172
|
<div>
|
|
174
173
|
<label class="field-label" for="index-name">Index name <span class="text-base-content/40">(optional)</span></label>
|
|
175
174
|
<input id="index-name" class="field-input font-mono" placeholder="idx_users_email" spellcheck="false" autocomplete="off" />
|
|
176
175
|
</div>
|
|
177
176
|
<div class="flex items-end pb-1">
|
|
178
177
|
<label class="flex cursor-pointer items-center gap-2">
|
|
179
178
|
<input id="index-unique" type="checkbox" class="checkbox checkbox-primary checkbox-sm" />
|
|
180
179
|
<span class="text-sm font-medium text-base-content/70">Unique index</span>
|
|
181
180
|
</label>
|
|
182
181
|
</div>
|
|
183
182
|
<div class="sm:col-span-3">
|
|
184
183
|
<label class="field-label">Columns <span class="text-base-content/40">— click in the order they should appear</span></label>
|
|
185
184
|
<div id="index-columns" class="flex flex-wrap gap-2">
|
|
186
185
|
{{#each schema.columns}}
|
|
187
186
|
<button type="button" class="index-col-btn rounded-lg border border-base-300 px-2.5 py-1.5 text-[13px] font-mono hover:border-primary/50" data-column="{{this.name}}">
|
|
188
187
|
<span class="index-col-num mr-1 hidden rounded bg-primary/15 px-1 text-[10px] font-bold text-primary">1</span>{{this.name}}
|
|
189
188
|
</button>
|
|
190
189
|
{{/each}}
|
|
191
190
|
</div>
|
|
192
191
|
</div>
|
|
193
192
|
</div>
|
|
194
193
|
<div id="index-preview" class="mt-3 hidden">
|
|
195
194
|
<div class="rounded-xl bg-base-200/60 p-3">
|
|
196
195
|
<div class="mb-1 text-[10px] font-semibold uppercase tracking-wider text-base-content/40">Generated SQL</div>
|
|
197
196
|
<pre class="code-panel !m-0"><code id="index-preview-code"></code></pre>
|
|
198
197
|
</div>
|
|
199
198
|
</div>
|
|
200
199
|
<div id="add-index-error" class="mt-3 hidden"></div>
|
|
201
200
|
<div class="mt-3 flex gap-2">
|
|
202
201
|
<button id="add-index-submit" type="button" class="btn btn-primary btn-sm">Create index</button>
|
|
203
202
|
<button id="add-index-cancel" type="button" class="btn btn-ghost btn-sm">Cancel</button>
|
|
204
203
|
</div>
|
|
205
204
|
</div>
|
|
206
205
|
<div class="overflow-x-auto">
|
|
207
206
|
{{#if schema.indexes.length}}
|
|
208
207
|
<table class="table table-sm">
|
|
209
208
|
<thead>
|
|
210
209
|
<tr class="bg-base-200/70 text-xs uppercase tracking-wide text-base-content/50">
|
|
211
210
|
<th class="font-semibold">Name</th>
|
|
212
211
|
<th class="font-semibold">Columns</th>
|
|
213
212
|
<th class="font-semibold">Kind</th>
|
|
214
213
|
<th class="font-semibold">SQL</th>
|
|
215
214
|
<th class="text-right font-semibold">Actions</th>
|
|
216
215
|
</tr>
|
|
217
216
|
</thead>
|
|
218
217
|
<tbody>
|
|
219
218
|
{{#each schema.indexes}}
|
|
220
219
|
<tr class="hover:bg-primary/[0.04]">
|
|
221
220
|
<td class="align-middle font-mono text-[13px]">{{this.name}}</td>
|
|
222
221
|
<td class="align-middle font-mono text-[13px] text-base-content/70">{{join this.columns ", "}}</td>
|
|
223
222
|
<td class="align-middle">
|
|
224
223
|
<div class="flex flex-wrap gap-1">
|
|
225
224
|
{{#if this.unique}}<span class="chip chip-type">UNIQUE</span>{{else}}<span class="chip chip-type">INDEX</span>{{/if}}
|
|
226
225
|
{{#unless (eq this.origin 'c')}}<span class="chip chip-null" title="Automatic index (created by SQLite)">AUTO</span>{{/unless}}
|
|
227
226
|
</div>
|
|
228
227
|
</td>
|
|
229
228
|
<td class="align-middle">
|
|
230
229
|
{{#if this.sql}}
|
|
231
230
|
<details class="group">
|
|
232
231
|
<summary class="cursor-pointer text-xs font-medium text-primary/80 hover:text-primary">View SQL</summary>
|
|
233
232
|
<pre class="mt-1 max-w-[26rem] overflow-x-auto rounded-lg bg-base-200/70 p-2 font-mono text-[11px] leading-relaxed text-base-content/70">{{this.sql}}</pre>
|
|
234
233
|
</details>
|
|
235
234
|
{{else}}
|
|
236
235
|
<span class="text-base-content/30">—</span>
|
|
237
236
|
{{/if}}
|
|
238
237
|
</td>
|
|
239
238
|
<td class="whitespace-nowrap text-right align-middle">
|
|
240
239
|
<button type="button" class="btn btn-ghost btn-xs gap-1 text-error index-drop"
|
|
241
240
|
data-index="{{this.name}}" {{#if ../readonly}}disabled{{/if}} {{#unless (eq this.origin 'c')}}disabled title="Automatic index — cannot be dropped"{{/unless}}>
|
|
242
241
|
{{{icon "trash" class="h-3.5 w-3.5"}}}
|
|
243
242
|
Drop
|
|
244
243
|
</button>
|
|
245
244
|
</td>
|
|
246
245
|
</tr>
|
|
247
246
|
{{/each}}
|
|
248
247
|
</tbody>
|
|
249
248
|
</table>
|
|
250
249
|
{{else}}
|
|
251
250
|
<div class="px-5 py-8 text-center text-sm text-base-content/45">No indexes on this table yet.</div>
|
|
252
251
|
{{/if}}
|
|
253
252
|
</div>
|
|
254
253
|
</div>
|
|
255
254
|
{{#if schema.references.length}}
|
|
256
255
|
<div class="app-card overflow-hidden">
|
|
257
256
|
<div class="border-b border-base-200 px-5 py-4">
|
|
258
257
|
<h2 class="text-sm font-semibold uppercase tracking-wide text-base-content/45">Referenced by</h2>
|
|
259
258
|
</div>
|
|
260
259
|
<div class="overflow-x-auto">
|
|
261
260
|
<table class="table table-sm">
|
|
262
261
|
<thead>
|
|
263
262
|
<tr class="bg-base-200/70 text-xs uppercase tracking-wide text-base-content/50">
|
|
264
263
|
<th class="font-semibold">Table</th>
|
|
265
264
|
<th class="font-semibold">Via</th>
|
|
266
265
|
</tr>
|
|
267
266
|
</thead>
|
|
268
267
|
<tbody>
|
|
269
268
|
{{#each schema.references}}
|
|
270
269
|
<tr class="hover:bg-primary/[0.04]">
|
|
271
270
|
<td class="align-middle font-mono text-[13px]">{{this.table}}</td>
|
|
272
271
|
<td class="align-middle font-mono text-[13px] text-base-content/70">{{this.from}} → {{this.to}}</td>
|
|
273
272
|
</tr>
|
|
274
273
|
{{/each}}
|
|
275
274
|
</tbody>
|
|
276
275
|
</table>
|
|
277
276
|
</div>
|
|
278
277
|
</div>
|
|
279
278
|
{{/if}}
|
|
280
279
|
{{#unless isInternal}}
|
|
281
280
|
<div class="rounded-2xl border border-error/25 bg-error/[0.03] p-5">
|
|
282
281
|
<h2 class="flex items-center gap-2 text-sm font-semibold uppercase tracking-wide text-error">
|
|
283
282
|
{{{icon "warning" class="h-4 w-4"}}}
|
|
284
283
|
Danger zone
|
|
285
284
|
</h2>
|
|
286
285
|
<p class="mt-1 text-sm text-base-content/55">Dropping this table permanently removes its schema and all of its data.</p>
|
|
287
286
|
<div class="mt-3 flex flex-wrap items-center gap-3">
|
|
288
287
|
<button id="drop-table" type="button" class="btn btn-error btn-sm" {{#if readonly}}disabled{{/if}} {{#if schema.references.length}}disabled title="Referenced by {{schema.references.length}} table(s)"{{/if}}>
|
|
289
288
|
Drop table
|
|
290
289
|
</button>
|
|
291
290
|
{{#if readonly}}<span class="text-xs text-base-content/50">Read-only — table changes are disabled.</span>{{/if}}
|
|
292
291
|
{{#if schema.references.length}}
|
|
293
292
|
<span class="text-xs text-base-content/50">Cannot drop — referenced by {{schema.references.length}} table(s).</span>
|
|
294
293
|
{{/if}}
|
|
295
294
|
</div>
|
|
296
295
|
</div>
|
|
297
296
|
{{/unless}}
|
|
297
|
+
<div class="space-y-6">
|
|
298
298
|
<div class="page-header">
|
|
299
299
|
<div class="page-header-row">
|
|
300
300
|
<div class="flex items-center gap-3">
|
|
301
301
|
<a href="{{basePath}}/tables/{{encodeUri dbId}}/{{encodeUri table}}" class="btn btn-circle btn-ghost btn-sm" aria-label="Back to table">
|
|
302
302
|
{{{icon "arrowLeft" class="h-4 w-4"}}}
|
|
303
303
|
</a>
|
|
304
304
|
<div>
|
|
305
305
|
<div class="page-pretitle">Schema Editor</div>
|
|
306
306
|
<h1 class="page-title font-mono text-xl sm:text-2xl">{{table}}</h1>
|
|
307
307
|
</div>
|
|
308
308
|
</div>
|
|
309
309
|
<div class="page-actions">
|
|
310
310
|
<a href="{{basePath}}/tables/{{encodeUri dbId}}/{{encodeUri table}}" class="btn btn-ghost btn-sm gap-1.5">
|
|
311
311
|
{{{icon "table" class="h-4 w-4"}}}
|
|
312
312
|
View data
|
|
313
313
|
</a>
|
|
314
314
|
<a href="{{basePath}}/query/{{encodeUri dbId}}" class="btn btn-outline btn-sm gap-1.5">
|
|
315
315
|
{{{icon "query" class="h-4 w-4"}}}
|
|
316
316
|
Query
|
|
317
317
|
</a>
|
|
318
318
|
</div>
|
|
319
319
|
</div>
|
|
320
320
|
</div>
|
|
321
321
|
<div class="app-card p-5">
|
|
322
322
|
<div class="flex flex-wrap items-end gap-3">
|
|
323
323
|
<div class="w-full max-w-sm">
|
|
324
324
|
<label class="field-label" for="table-rename-input">Table name</label>
|
|
325
325
|
<input id="table-rename-input" class="field-input font-mono" value="{{table}}" spellcheck="false" autocomplete="off" {{#if isInternal}}disabled{{/if}} {{#if readonly}}disabled{{/if}} />
|
|
326
326
|
</div>
|
|
327
327
|
<button id="rename-table" type="button" class="btn btn-primary btn-sm mb-0.5" {{#if isInternal}}disabled{{/if}} {{#if readonly}}disabled{{/if}}>
|
|
328
328
|
{{{icon "edit" class="h-4 w-4"}}}
|
|
329
329
|
Rename table
|
|
330
330
|
</button>
|
|
331
331
|
{{#if isInternal}}<span class="mb-1 text-xs text-base-content/45">Internal tables cannot be renamed.</span>{{/if}}
|
|
332
332
|
</div>
|
|
333
333
|
</div>
|
|
334
334
|
<div class="app-card overflow-hidden">
|
|
335
335
|
<div class="flex flex-wrap items-center justify-between gap-2 border-b border-base-200 px-5 py-4">
|
|
336
336
|
<h2 class="text-sm font-semibold uppercase tracking-wide text-base-content/45">
|
|
337
337
|
Columns
|
|
338
338
|
<span class="ml-1 rounded-md bg-base-200 px-1.5 py-0.5 text-[10px] text-base-content/50">{{schema.columns.length}}</span>
|
|
339
339
|
</h2>
|
|
340
340
|
<button id="add-column" type="button" class="btn btn-outline btn-sm gap-1.5" {{#if readonly}}disabled{{/if}}>
|
|
341
341
|
{{{icon "plus" class="h-4 w-4"}}}
|
|
342
342
|
Add column
|
|
343
343
|
</button>
|
|
344
344
|
</div>
|
|
345
345
|
<div id="add-column-panel" class="hidden border-b border-base-200 p-4">
|
|
346
346
|
<div class="mb-2 text-xs font-bold uppercase tracking-wider text-primary">Add New Column</div>
|
|
347
347
|
<div id="add-column-form" class="grid grid-cols-2 items-end gap-2.5 sm:grid-cols-12"></div>
|
|
348
348
|
<div id="add-column-error" class="hidden mt-3"></div>
|
|
349
349
|
<div class="mt-3.5 flex gap-2">
|
|
350
350
|
<button id="add-column-submit" type="button" class="btn btn-primary btn-sm">Add column</button>
|
|
351
351
|
<button id="add-column-cancel" type="button" class="btn btn-ghost btn-sm">Cancel</button>
|
|
352
352
|
</div>
|
|
353
353
|
</div>
|
|
354
354
|
<div id="edit-column-panel" class="hidden border-b border-base-200 bg-base-200/30 p-4">
|
|
355
355
|
<div class="mb-2.5 flex items-center justify-between">
|
|
356
356
|
<div class="flex items-center gap-2">
|
|
357
357
|
<span class="text-xs font-bold uppercase tracking-wider text-primary">Edit Column:</span>
|
|
358
358
|
<span id="edit-column-target" class="font-mono text-sm font-bold text-base-content"></span>
|
|
359
359
|
</div>
|
|
360
360
|
<span class="text-[11px] text-base-content/45">Change data type, default value, primary key, nullability, uniqueness, or foreign key.</span>
|
|
361
361
|
</div>
|
|
362
362
|
<div id="edit-column-form" class="grid grid-cols-2 items-end gap-2.5 sm:grid-cols-12"></div>
|
|
363
363
|
<div id="edit-column-error" class="hidden mt-3"></div>
|
|
364
364
|
<div class="mt-3.5 flex gap-2">
|
|
365
365
|
<button id="edit-column-submit" type="button" class="btn btn-primary btn-sm">Save Changes</button>
|
|
366
366
|
<button id="edit-column-cancel" type="button" class="btn btn-ghost btn-sm">Cancel</button>
|
|
367
367
|
</div>
|
|
368
368
|
</div>
|
|
369
369
|
<div class="overflow-x-auto">
|
|
370
370
|
<table class="table table-sm">
|
|
371
371
|
<thead>
|
|
372
372
|
<tr class="bg-base-200/70 text-xs uppercase tracking-wide text-base-content/50">
|
|
373
373
|
<th class="font-semibold">Name</th>
|
|
374
374
|
<th class="font-semibold">Type</th>
|
|
375
375
|
<th class="font-semibold">Constraints</th>
|
|
376
376
|
<th class="font-semibold">Default</th>
|
|
377
377
|
<th class="text-right font-semibold">Actions</th>
|
|
378
378
|
</tr>
|
|
379
379
|
</thead>
|
|
380
380
|
<tbody>
|
|
381
381
|
{{#each schema.columns}}
|
|
382
382
|
<tr class="hover:bg-primary/[0.04]">
|
|
383
383
|
<td class="col-name-cell align-middle" data-column="{{this.name}}">
|
|
384
384
|
<span class="name-display font-mono text-[13px] font-semibold">{{this.name}}</span>
|
|
385
385
|
</td>
|
|
386
386
|
<td class="align-middle"><span class="chip chip-type">{{this.type}}</span></td>
|
|
387
387
|
<td class="align-middle">
|
|
388
388
|
<div class="flex flex-wrap gap-1">
|
|
389
389
|
{{#if (gt this.pk 0)}}<span class="chip chip-pk">PK</span>{{/if}}
|
|
390
390
|
{{#if this.notnull}}<span class="chip chip-type">NOT NULL</span>{{/if}}
|
|
391
391
|
{{#if this.unique}}{{#unless (gt this.pk 0)}}<span class="chip chip-type">UNIQUE</span>{{/unless}}{{/if}}
|
|
392
392
|
{{#if this.fk}}<span class="chip chip-type">FK → {{this.fk.table}}.{{this.fk.to}}</span>{{/if}}
|
|
393
393
|
{{#if this.indexed}}<span class="chip chip-type">INDEX</span>{{/if}}
|
|
394
394
|
{{#if this.referencedBy.length}}<span class="chip chip-null" title="Referenced by {{this.referencedBy.length}} table(s)">REF × {{this.referencedBy.length}}</span>{{/if}}
|
|
395
395
|
</div>
|
|
396
396
|
</td>
|
|
397
397
|
<td class="align-middle font-mono text-[13px] text-base-content/60">
|
|
398
398
|
{{#if this.dflt_value}}{{this.dflt_value}}{{else}}<span class="text-base-content/25">—</span>{{/if}}
|
|
399
399
|
</td>
|
|
400
400
|
<td class="whitespace-nowrap text-right align-middle">
|
|
401
401
|
<div class="flex items-center justify-end gap-1">
|
|
402
402
|
<button type="button" class="btn btn-ghost btn-xs gap-1 text-base-content/70 hover:text-primary col-edit"
|
|
403
403
|
data-column="{{this.name}}"
|
|
404
404
|
data-type="{{this.type}}"
|
|
405
405
|
data-pk="{{#if (gt this.pk 0)}}true{{else}}false{{/if}}"
|
|
406
406
|
data-notnull="{{#if this.notnull}}true{{else}}false{{/if}}"
|
|
407
407
|
data-unique="{{#if this.unique}}true{{else}}false{{/if}}"
|
|
408
408
|
data-default="{{this.dflt_value}}"
|
|
409
409
|
data-fk-table="{{this.fk.table}}"
|
|
410
410
|
data-fk-to="{{this.fk.to}}"
|
|
411
411
|
{{#if ../readonly}}disabled{{/if}}>
|
|
412
412
|
{{{icon "edit" class="h-3.5 w-3.5"}}}
|
|
413
413
|
Edit
|
|
414
414
|
</button>
|
|
415
415
|
<button type="button" class="btn btn-ghost btn-xs gap-1 text-error col-drop" {{#if ../readonly}}disabled{{/if}} {{#unless this.canDrop}}disabled title="{{join this.dropBlockers "; "}}"{{/unless}}>
|
|
416
416
|
{{{icon "trash" class="h-3.5 w-3.5"}}}
|
|
417
417
|
Delete
|
|
418
418
|
</button>
|
|
419
419
|
</div>
|
|
420
420
|
</td>
|
|
421
421
|
</tr>
|
|
422
422
|
{{/each}}
|
|
423
423
|
</tbody>
|
|
424
424
|
</table>
|
|
425
425
|
</div>
|
|
426
426
|
</div>
|
|
427
427
|
{{#if schema.foreignKeys.length}}
|
|
428
428
|
<div class="app-card overflow-hidden">
|
|
429
429
|
<div class="border-b border-base-200 px-5 py-4">
|
|
430
430
|
<h2 class="text-sm font-semibold uppercase tracking-wide text-base-content/45">Foreign keys</h2>
|
|
431
431
|
</div>
|
|
432
432
|
<div class="overflow-x-auto">
|
|
433
433
|
<table class="table table-sm">
|
|
434
434
|
<thead>
|
|
435
435
|
<tr class="bg-base-200/70 text-xs uppercase tracking-wide text-base-content/50">
|
|
436
436
|
<th class="font-semibold">Column</th>
|
|
437
437
|
<th class="font-semibold">References</th>
|
|
438
438
|
<th class="font-semibold">On delete</th>
|
|
439
439
|
<th class="font-semibold">On update</th>
|
|
440
440
|
</tr>
|
|
441
441
|
</thead>
|
|
442
442
|
<tbody>
|
|
443
443
|
{{#each schema.foreignKeys}}
|
|
444
444
|
<tr class="hover:bg-primary/[0.04]">
|
|
445
445
|
<td class="align-middle font-mono text-[13px]">{{this.from}}</td>
|
|
446
446
|
<td class="align-middle font-mono text-[13px] text-primary">{{this.table}}.{{this.to}}</td>
|
|
447
447
|
<td class="align-middle text-[13px] text-base-content/60">{{this.on_delete}}</td>
|
|
448
448
|
<td class="align-middle text-[13px] text-base-content/60">{{this.on_update}}</td>
|
|
449
449
|
</tr>
|
|
450
450
|
{{/each}}
|
|
451
451
|
</tbody>
|
|
452
452
|
</table>
|
|
453
453
|
</div>
|
|
454
454
|
</div>
|
|
455
455
|
{{/if}}
|
|
456
456
|
<div class="app-card overflow-hidden">
|
|
457
457
|
<div class="flex flex-wrap items-center justify-between gap-2 border-b border-base-200 px-5 py-4">
|
|
458
458
|
<h2 class="text-sm font-semibold uppercase tracking-wide text-base-content/45">
|
|
459
459
|
Indexes
|
|
460
460
|
<span class="ml-1 rounded-md bg-base-200 px-1.5 py-0.5 text-[10px] text-base-content/50">{{schema.indexes.length}}</span>
|
|
461
461
|
</h2>
|
|
462
462
|
<button id="add-index" type="button" class="btn btn-outline btn-sm gap-1.5" {{#if readonly}}disabled{{/if}}>
|
|
463
463
|
{{{icon "plus" class="h-4 w-4"}}}
|
|
464
464
|
Add index
|
|
465
465
|
</button>
|
|
466
466
|
</div>
|
|
467
467
|
<div id="add-index-panel" class="hidden border-b border-base-200 p-4">
|
|
468
468
|
<div class="grid grid-cols-1 gap-3 sm:grid-cols-3">
|
|
469
469
|
<div>
|
|
470
470
|
<label class="field-label" for="index-name">Index name <span class="text-base-content/40">(optional)</span></label>
|
|
471
471
|
<input id="index-name" class="field-input font-mono" placeholder="idx_users_email" spellcheck="false" autocomplete="off" />
|
|
472
472
|
</div>
|
|
473
473
|
<div class="flex items-end pb-1">
|
|
474
474
|
<label class="flex cursor-pointer items-center gap-2">
|
|
475
475
|
<input id="index-unique" type="checkbox" class="checkbox checkbox-primary checkbox-sm" />
|
|
476
476
|
<span class="text-sm font-medium text-base-content/70">Unique index</span>
|
|
477
477
|
</label>
|
|
478
478
|
</div>
|
|
479
479
|
<div class="sm:col-span-3">
|
|
480
480
|
<label class="field-label">Columns <span class="text-base-content/40">— click in the order they should appear</span></label>
|
|
481
481
|
<div id="index-columns" class="flex flex-wrap gap-2">
|
|
482
482
|
{{#each schema.columns}}
|
|
483
483
|
<button type="button" class="index-col-btn rounded-lg border border-base-300 px-2.5 py-1.5 text-[13px] font-mono hover:border-primary/50" data-column="{{this.name}}">
|
|
484
484
|
<span class="index-col-num mr-1 hidden rounded bg-primary/15 px-1 text-[10px] font-bold text-primary">1</span>{{this.name}}
|
|
485
485
|
</button>
|
|
486
486
|
{{/each}}
|
|
487
487
|
</div>
|
|
488
488
|
</div>
|
|
489
489
|
</div>
|
|
490
490
|
<div id="index-preview" class="mt-3 hidden">
|
|
491
491
|
<div class="rounded-xl bg-base-200/60 p-3">
|
|
492
492
|
<div class="mb-1 text-[10px] font-semibold uppercase tracking-wider text-base-content/40">Generated SQL</div>
|
|
493
493
|
<pre class="code-panel !m-0"><code id="index-preview-code"></code></pre>
|
|
494
494
|
</div>
|
|
495
495
|
</div>
|
|
496
496
|
<div id="add-index-error" class="mt-3 hidden"></div>
|
|
497
497
|
<div class="mt-3 flex gap-2">
|
|
498
498
|
<button id="add-index-submit" type="button" class="btn btn-primary btn-sm">Create index</button>
|
|
499
499
|
<button id="add-index-cancel" type="button" class="btn btn-ghost btn-sm">Cancel</button>
|
|
500
500
|
</div>
|
|
501
501
|
</div>
|
|
502
502
|
<div class="overflow-x-auto">
|
|
503
503
|
{{#if schema.indexes.length}}
|
|
504
504
|
<table class="table table-sm">
|
|
505
505
|
<thead>
|
|
506
506
|
<tr class="bg-base-200/70 text-xs uppercase tracking-wide text-base-content/50">
|
|
507
507
|
<th class="font-semibold">Name</th>
|
|
508
508
|
<th class="font-semibold">Columns</th>
|
|
509
509
|
<th class="font-semibold">Kind</th>
|
|
510
510
|
<th class="font-semibold">SQL</th>
|
|
511
511
|
<th class="text-right font-semibold">Actions</th>
|
|
512
512
|
</tr>
|
|
513
513
|
</thead>
|
|
514
514
|
<tbody>
|
|
515
515
|
{{#each schema.indexes}}
|
|
516
516
|
<tr class="hover:bg-primary/[0.04]">
|
|
517
517
|
<td class="align-middle font-mono text-[13px]">{{this.name}}</td>
|
|
518
518
|
<td class="align-middle font-mono text-[13px] text-base-content/70">{{join this.columns ", "}}</td>
|
|
519
519
|
<td class="align-middle">
|
|
520
520
|
<div class="flex flex-wrap gap-1">
|
|
521
521
|
{{#if this.unique}}<span class="chip chip-type">UNIQUE</span>{{else}}<span class="chip chip-type">INDEX</span>{{/if}}
|
|
522
522
|
{{#unless (eq this.origin 'c')}}<span class="chip chip-null" title="Automatic index (created by SQLite)">AUTO</span>{{/unless}}
|
|
523
523
|
</div>
|
|
524
524
|
</td>
|
|
525
525
|
<td class="align-middle">
|
|
526
526
|
{{#if this.sql}}
|
|
527
527
|
<details class="group">
|
|
528
528
|
<summary class="cursor-pointer text-xs font-medium text-primary/80 hover:text-primary">View SQL</summary>
|
|
529
529
|
<pre class="mt-1 max-w-[26rem] overflow-x-auto rounded-lg bg-base-200/70 p-2 font-mono text-[11px] leading-relaxed text-base-content/70">{{this.sql}}</pre>
|
|
530
530
|
</details>
|
|
531
531
|
{{else}}
|
|
532
532
|
<span class="text-base-content/30">—</span>
|
|
533
533
|
{{/if}}
|
|
534
534
|
</td>
|
|
535
535
|
<td class="whitespace-nowrap text-right align-middle">
|
|
536
536
|
<button type="button" class="btn btn-ghost btn-xs gap-1 text-error index-drop"
|
|
537
537
|
data-index="{{this.name}}" {{#if ../readonly}}disabled{{/if}} {{#unless (eq this.origin 'c')}}disabled title="Automatic index — cannot be dropped"{{/unless}}>
|
|
538
538
|
{{{icon "trash" class="h-3.5 w-3.5"}}}
|
|
539
539
|
Drop
|
|
540
540
|
</button>
|
|
541
541
|
</td>
|
|
542
542
|
</tr>
|
|
543
543
|
{{/each}}
|
|
544
544
|
</tbody>
|
|
545
545
|
</table>
|
|
546
546
|
{{else}}
|
|
547
547
|
<div class="px-5 py-8 text-center text-sm text-base-content/45">No indexes on this table yet.</div>
|
|
548
548
|
{{/if}}
|
|
549
549
|
</div>
|
|
550
550
|
</div>
|
|
551
551
|
{{#if schema.references.length}}
|
|
552
552
|
<div class="app-card overflow-hidden">
|
|
553
553
|
<div class="border-b border-base-200 px-5 py-4">
|
|
554
554
|
<h2 class="text-sm font-semibold uppercase tracking-wide text-base-content/45">Referenced by</h2>
|
|
555
555
|
</div>
|
|
556
556
|
<div class="overflow-x-auto">
|
|
557
557
|
<table class="table table-sm">
|
|
558
558
|
<thead>
|
|
559
559
|
<tr class="bg-base-200/70 text-xs uppercase tracking-wide text-base-content/50">
|
|
560
560
|
<th class="font-semibold">Table</th>
|
|
561
561
|
<th class="font-semibold">Via</th>
|
|
562
562
|
</tr>
|
|
563
563
|
</thead>
|
|
564
564
|
<tbody>
|
|
565
565
|
{{#each schema.references}}
|
|
566
566
|
<tr class="hover:bg-primary/[0.04]">
|
|
567
567
|
<td class="align-middle font-mono text-[13px]">{{this.table}}</td>
|
|
568
568
|
<td class="align-middle font-mono text-[13px] text-base-content/70">{{this.from}} → {{this.to}}</td>
|
|
569
569
|
</tr>
|
|
570
570
|
{{/each}}
|
|
571
571
|
</tbody>
|
|
572
572
|
</table>
|
|
573
573
|
</div>
|
|
574
574
|
</div>
|
|
575
575
|
{{/if}}
|
|
576
576
|
{{#unless isInternal}}
|
|
577
577
|
<div class="rounded-2xl border border-error/25 bg-error/[0.03] p-5">
|
|
578
578
|
<h2 class="flex items-center gap-2 text-sm font-semibold uppercase tracking-wide text-error">
|
|
579
579
|
{{{icon "warning" class="h-4 w-4"}}}
|
|
580
580
|
Danger zone
|
|
581
581
|
</h2>
|
|
582
582
|
<p class="mt-1 text-sm text-base-content/55">Dropping this table permanently removes its schema and all of its data.</p>
|
|
583
583
|
<div class="mt-3 flex flex-wrap items-center gap-3">
|
|
584
584
|
<button id="drop-table" type="button" class="btn btn-error btn-sm" {{#if readonly}}disabled{{/if}} {{#if schema.references.length}}disabled title="Referenced by {{schema.references.length}} table(s)"{{/if}}>
|
|
585
585
|
Drop table
|
|
586
586
|
</button>
|
|
587
587
|
{{#if readonly}}<span class="text-xs text-base-content/50">Read-only — table changes are disabled.</span>{{/if}}
|
|
588
588
|
{{#if schema.references.length}}
|
|
589
589
|
<span class="text-xs text-base-content/50">Cannot drop — referenced by {{schema.references.length}} table(s).</span>
|
|
590
590
|
{{/if}}
|
|
591
591
|
</div>
|
|
592
592
|
</div>
|
|
593
593
|
{{/unless}}
|