admindb 2.3.0 → 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 +17 -17
- package/dist/app.js +123 -163
- package/dist/cli/args.d.ts +1 -1
- package/dist/cli/args.js +5 -3
- package/dist/cli/config.d.ts +1 -1
- package/dist/cli/config.js +9 -2
- package/dist/cli/runner.js +9 -6
- package/dist/db/manager.d.ts +1 -1
- package/dist/db/manager.js +21 -2
- 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/serverless.js +16 -4
- 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 +3 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
<div class="mx-auto max-w-3xl space-y-5">
|
|
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">{{#if (eq mode 'edit')}}Record Editor{{else}}Record Inserter{{/if}}</div>
|
|
10
9
|
<h1 class="page-title text-xl sm:text-2xl">
|
|
11
10
|
{{#if (eq mode 'edit')}}Edit row{{else}}New row{{/if}}
|
|
12
11
|
<span class="text-base-content/40 font-normal">in</span> <span class="font-mono text-primary">{{table}}</span>
|
|
13
12
|
</h1>
|
|
14
13
|
<p class="text-xs text-base-content/55 mt-0.5">{{infoSummary}}</p>
|
|
15
14
|
</div>
|
|
16
15
|
</div>
|
|
17
16
|
</div>
|
|
18
17
|
</div>
|
|
19
18
|
<div class="app-card">
|
|
20
19
|
<div class="border-b border-base-200 px-6 py-4">
|
|
21
20
|
<h2 class="text-sm font-semibold uppercase tracking-wide text-base-content/45">Row fields</h2>
|
|
22
21
|
</div>
|
|
23
22
|
<div class="p-6">
|
|
24
23
|
<div id="row-fields" class="grid grid-cols-1 gap-x-6 gap-y-5 md:grid-cols-2"></div>
|
|
25
24
|
<div id="form-error" class="hidden"></div>
|
|
26
25
|
<div class="mt-6 flex flex-wrap items-center gap-2 border-t border-base-200 pt-5">
|
|
27
26
|
{{#unless readonly}}
|
|
28
27
|
<button id="row-submit" type="button" class="btn btn-primary">
|
|
29
28
|
{{#if (eq mode 'edit')}}Update row{{else}}Insert row{{/if}}
|
|
30
29
|
</button>
|
|
31
30
|
<button id="row-generate" type="button" class="btn btn-outline gap-1.5">
|
|
32
31
|
{{{icon "sql" class="h-4 w-4"}}}
|
|
33
32
|
Get query
|
|
34
33
|
</button>
|
|
35
34
|
{{else}}
|
|
36
35
|
<span class="text-sm font-medium text-warning">This database is read-only — rows cannot be {{#if (eq mode 'edit')}}updated{{else}}inserted{{/if}}.</span>
|
|
37
36
|
{{/unless}}
|
|
38
37
|
<a href="{{basePath}}/tables/{{table}}" class="btn btn-ghost">Cancel</a>
|
|
39
38
|
</div>
|
|
40
39
|
<p class="mt-3 text-xs text-base-content/40">
|
|
41
40
|
Columns with defaults are pre-filled from the schema. Empty fields are treated as "not set" (DB defaults
|
|
42
41
|
apply). <code class="font-mono">0</code> is a valid value. "Get query" only previews the SQL — it never executes.
|
|
43
42
|
</p>
|
|
44
43
|
</div>
|
|
45
44
|
</div>
|
|
46
45
|
<div id="generated-sql" class="hidden">
|
|
47
46
|
<div class="app-card">
|
|
48
47
|
<div class="flex items-center justify-between border-b border-base-200 px-6 py-4">
|
|
49
48
|
<h2 class="flex items-center gap-2 text-sm font-semibold uppercase tracking-wide text-base-content/45">
|
|
50
49
|
<span class="grid h-6 w-6 place-items-center rounded-md bg-primary/10 text-primary">
|
|
51
50
|
{{{icon "sql" class="h-4 w-4"}}}
|
|
52
51
|
</span>
|
|
53
52
|
Generated SQL
|
|
54
53
|
</h2>
|
|
55
54
|
<button id="copy-sql" type="button" class="btn btn-ghost btn-xs gap-1">Copy</button>
|
|
56
55
|
</div>
|
|
57
56
|
<div class="p-4">
|
|
58
57
|
<pre class="code-panel"><code id="generated-sql-code"></code></pre>
|
|
59
58
|
</div>
|
|
60
59
|
</div>
|
|
61
60
|
</div>
|
|
61
|
+
<div class="mx-auto max-w-3xl space-y-5">
|
|
62
62
|
<div class="page-header">
|
|
63
63
|
<div class="page-header-row">
|
|
64
64
|
<div class="flex items-center gap-3">
|
|
65
65
|
<a href="{{basePath}}/tables/{{encodeUri dbId}}/{{encodeUri table}}" class="btn btn-circle btn-ghost btn-sm" aria-label="Back to table">
|
|
66
66
|
{{{icon "arrowLeft" class="h-4 w-4"}}}
|
|
67
67
|
</a>
|
|
68
68
|
<div>
|
|
69
69
|
<div class="page-pretitle">{{#if (eq mode 'edit')}}Record Editor{{else}}Record Inserter{{/if}}</div>
|
|
70
70
|
<h1 class="page-title text-xl sm:text-2xl">
|
|
71
71
|
{{#if (eq mode 'edit')}}Edit row{{else}}New row{{/if}}
|
|
72
72
|
<span class="text-base-content/40 font-normal">in</span> <span class="font-mono text-primary">{{table}}</span>
|
|
73
73
|
</h1>
|
|
74
74
|
<p class="text-xs text-base-content/55 mt-0.5">{{infoSummary}}</p>
|
|
75
75
|
</div>
|
|
76
76
|
</div>
|
|
77
77
|
</div>
|
|
78
78
|
</div>
|
|
79
79
|
<div class="app-card">
|
|
80
80
|
<div class="border-b border-base-200 px-6 py-4">
|
|
81
81
|
<h2 class="text-sm font-semibold uppercase tracking-wide text-base-content/45">Row fields</h2>
|
|
82
82
|
</div>
|
|
83
83
|
<div class="p-6">
|
|
84
84
|
<div id="row-fields" class="grid grid-cols-1 gap-x-6 gap-y-5 md:grid-cols-2"></div>
|
|
85
85
|
<div id="form-error" class="hidden"></div>
|
|
86
86
|
<div class="mt-6 flex flex-wrap items-center gap-2 border-t border-base-200 pt-5">
|
|
87
87
|
{{#unless readonly}}
|
|
88
88
|
<button id="row-submit" type="button" class="btn btn-primary">
|
|
89
89
|
{{#if (eq mode 'edit')}}Update row{{else}}Insert row{{/if}}
|
|
90
90
|
</button>
|
|
91
91
|
<button id="row-generate" type="button" class="btn btn-outline gap-1.5">
|
|
92
92
|
{{{icon "sql" class="h-4 w-4"}}}
|
|
93
93
|
Get query
|
|
94
94
|
</button>
|
|
95
95
|
{{else}}
|
|
96
96
|
<span class="text-sm font-medium text-warning">This database is read-only — rows cannot be {{#if (eq mode 'edit')}}updated{{else}}inserted{{/if}}.</span>
|
|
97
97
|
{{/unless}}
|
|
98
98
|
<a href="{{basePath}}/tables/{{encodeUri dbId}}/{{encodeUri table}}" class="btn btn-ghost">Cancel</a>
|
|
99
99
|
</div>
|
|
100
100
|
<p class="mt-3 text-xs text-base-content/40">
|
|
101
101
|
Columns with defaults are pre-filled from the schema. Empty fields are treated as "not set" (DB defaults
|
|
102
102
|
apply). <code class="font-mono">0</code> is a valid value. "Get query" only previews the SQL — it never executes.
|
|
103
103
|
</p>
|
|
104
104
|
</div>
|
|
105
105
|
</div>
|
|
106
106
|
<div id="generated-sql" class="hidden">
|
|
107
107
|
<div class="app-card">
|
|
108
108
|
<div class="flex items-center justify-between border-b border-base-200 px-6 py-4">
|
|
109
109
|
<h2 class="flex items-center gap-2 text-sm font-semibold uppercase tracking-wide text-base-content/45">
|
|
110
110
|
<span class="grid h-6 w-6 place-items-center rounded-md bg-primary/10 text-primary">
|
|
111
111
|
{{{icon "sql" class="h-4 w-4"}}}
|
|
112
112
|
</span>
|
|
113
113
|
Generated SQL
|
|
114
114
|
</h2>
|
|
115
115
|
<button id="copy-sql" type="button" class="btn btn-ghost btn-xs gap-1">Copy</button>
|
|
116
116
|
</div>
|
|
117
117
|
<div class="p-4">
|
|
118
118
|
<pre class="code-panel"><code id="generated-sql-code"></code></pre>
|
|
119
119
|
</div>
|
|
120
120
|
</div>
|
|
121
121
|
</div>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<div class="space-y-8">
|
|
2
1
|
<div class="page-header">
|
|
3
2
|
<div class="page-header-row">
|
|
4
3
|
<div>
|
|
5
4
|
<div class="page-pretitle">Overview</div>
|
|
6
5
|
<h1 class="page-title">Database</h1>
|
|
7
6
|
<p class="mt-0.5 text-xs text-base-content/60">Browse, edit, and query the connected database from your browser.</p>
|
|
8
7
|
</div>
|
|
9
8
|
<div class="page-actions">
|
|
10
9
|
<a href="{{basePath}}/query" class="btn btn-ghost btn-sm gap-1.5">
|
|
11
10
|
{{{icon "query" class="h-4 w-4"}}}
|
|
12
11
|
Query
|
|
13
12
|
</a>
|
|
14
13
|
{{#unless readonly}}
|
|
15
14
|
<a href="{{basePath}}/designer" class="btn btn-primary btn-sm gap-1.5">
|
|
16
15
|
{{{icon "plus" class="h-4 w-4"}}}
|
|
17
16
|
New table
|
|
18
17
|
</a>
|
|
19
18
|
{{/unless}}
|
|
20
19
|
</div>
|
|
21
20
|
</div>
|
|
22
21
|
</div>
|
|
23
22
|
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4">
|
|
24
23
|
<div class="stat-card">
|
|
25
24
|
<span class="stat-icon bg-primary/10 text-primary">
|
|
26
25
|
{{{icon "table" class="h-6 w-6"}}}
|
|
27
26
|
</span>
|
|
28
27
|
<div>
|
|
29
28
|
<div class="text-2xl font-extrabold tabular-nums">{{tables.length}}</div>
|
|
30
29
|
<div class="text-[13px] text-base-content/50">Tables</div>
|
|
31
30
|
</div>
|
|
32
31
|
</div>
|
|
33
32
|
<div class="stat-card">
|
|
34
33
|
<span class="stat-icon bg-secondary/10 text-secondary">
|
|
35
34
|
{{{icon "stats" class="h-6 w-6"}}}
|
|
36
35
|
</span>
|
|
37
36
|
<div>
|
|
38
37
|
<div class="text-2xl font-extrabold tabular-nums">{{totalRows}}</div>
|
|
39
38
|
<div class="text-[13px] text-base-content/50">Total rows</div>
|
|
40
39
|
</div>
|
|
41
40
|
</div>
|
|
42
41
|
<div class="stat-card">
|
|
43
42
|
<span class="stat-icon bg-accent/10 text-accent">
|
|
44
43
|
{{{icon "seed" class="h-6 w-6"}}}
|
|
45
44
|
</span>
|
|
46
45
|
<div>
|
|
47
46
|
<div class="text-2xl font-extrabold tabular-nums">{{savedQueries}}</div>
|
|
48
47
|
<div class="text-[13px] text-base-content/50">Saved queries</div>
|
|
49
48
|
</div>
|
|
50
49
|
</div>
|
|
51
50
|
<div class="stat-card">
|
|
52
51
|
<span class="stat-icon {{#if isPostgres}}bg-info/10 text-info{{else}}bg-emerald-500/10 text-emerald-500{{/if}}">
|
|
53
52
|
{{{icon "database" class="h-6 w-6"}}}
|
|
54
53
|
</span>
|
|
55
54
|
<div>
|
|
56
55
|
<div class="flex items-center gap-1.5">
|
|
57
56
|
<span class="text-xl font-extrabold">{{dialectName}}</span>
|
|
58
57
|
{{#if readonly}}
|
|
59
58
|
<span class="rounded bg-warning/20 px-1.5 py-0.5 text-[10px] font-bold uppercase text-warning">Read-only</span>
|
|
60
59
|
{{else}}
|
|
61
60
|
<span class="rounded bg-success/20 px-1.5 py-0.5 text-[10px] font-bold uppercase text-success">Writable</span>
|
|
62
61
|
{{/if}}
|
|
63
62
|
</div>
|
|
64
63
|
<div class="truncate font-mono text-xs text-base-content/50" title="{{dbPath}}">{{dbPath}}</div>
|
|
65
64
|
</div>
|
|
66
65
|
</div>
|
|
67
66
|
</div>
|
|
68
67
|
<div>
|
|
69
68
|
<div class="mb-3 flex items-center justify-between">
|
|
70
69
|
<h2 class="text-lg font-bold">Tables</h2>
|
|
71
70
|
<span class="text-sm text-base-content/45">{{tables.length}} total</span>
|
|
72
71
|
</div>
|
|
73
72
|
{{#if tables.length}}
|
|
74
73
|
{{#unless readonly}}
|
|
75
74
|
<div id="home-bulk-toolbar"
|
|
76
75
|
style="max-height: 0; overflow: hidden; transition: max-height 0.22s cubic-bezier(0.4,0,0.2,1), opacity 0.18s ease; opacity: 0;"
|
|
77
76
|
aria-hidden="true">
|
|
78
77
|
<div class="mb-3 flex flex-wrap items-center gap-3 rounded-xl border border-base-300 bg-base-100 px-4 py-3 shadow-soft">
|
|
79
78
|
<span id="home-bulk-count"
|
|
80
79
|
class="inline-flex items-center gap-1.5 rounded-full bg-primary/10 px-3 py-1 text-xs font-bold text-primary">
|
|
81
80
|
<span id="home-bulk-count-num">0</span> selected
|
|
82
81
|
</span>
|
|
83
82
|
<div class="h-4 w-px bg-base-300"></div>
|
|
84
83
|
<div class="dropdown dropdown-bottom" id="home-dd-clear">
|
|
85
84
|
<button tabindex="0" id="home-btn-clear"
|
|
86
85
|
class="btn btn-sm btn-warning gap-1.5 font-semibold">
|
|
87
86
|
{{{icon "refresh" class="h-3.5 w-3.5"}}}
|
|
88
87
|
Clear Rows
|
|
89
88
|
{{{icon "chevronDown" class="h-3 w-3 opacity-60"}}}
|
|
90
89
|
</button>
|
|
91
90
|
<ul tabindex="0"
|
|
92
91
|
class="dropdown-content menu bg-base-100 rounded-xl border border-base-300 shadow-lift z-30 mt-1 w-64 p-2 text-sm">
|
|
93
92
|
<li>
|
|
94
93
|
<button id="home-clear-normal" class="flex items-start gap-2.5 rounded-lg px-3 py-2.5 hover:bg-base-200 text-left w-full">
|
|
95
94
|
<span class="mt-0.5 text-warning shrink-0">{{{icon "warning" class="h-4 w-4"}}}</span>
|
|
96
95
|
<span>
|
|
97
96
|
<span class="block font-semibold">Clear rows</span>
|
|
98
97
|
<span class="block text-xs text-base-content/50">Deletes all rows; fails if FK references exist</span>
|
|
99
98
|
</span>
|
|
100
99
|
</button>
|
|
101
100
|
</li>
|
|
102
101
|
<li>
|
|
103
102
|
<button id="home-clear-force" class="flex items-start gap-2.5 rounded-lg px-3 py-2.5 hover:bg-error/10 text-left w-full text-error">
|
|
104
103
|
<span class="mt-0.5 shrink-0">{{{icon "warning" class="h-4 w-4"}}}</span>
|
|
105
104
|
<span>
|
|
106
105
|
<span class="block font-semibold">Force Clear <span class="text-[10px] font-bold uppercase tracking-wide rounded bg-error/15 px-1 py-0.5 ml-1">ignore FK</span></span>
|
|
107
106
|
<span class="block text-xs text-error/60">Disables FK checks; deletes regardless of references</span>
|
|
108
107
|
</span>
|
|
109
108
|
</button>
|
|
110
109
|
</li>
|
|
111
110
|
</ul>
|
|
112
111
|
</div>
|
|
113
112
|
<div class="dropdown dropdown-bottom" id="home-dd-drop">
|
|
114
113
|
<button tabindex="0" id="home-btn-drop"
|
|
115
114
|
class="btn btn-sm btn-error gap-1.5 font-semibold text-white">
|
|
116
115
|
{{{icon "warning" class="h-3.5 w-3.5"}}}
|
|
117
116
|
Drop Tables
|
|
118
117
|
{{{icon "chevronDown" class="h-3 w-3 opacity-60"}}}
|
|
119
118
|
</button>
|
|
120
119
|
<ul tabindex="0"
|
|
121
120
|
class="dropdown-content menu bg-base-100 rounded-xl border border-base-300 shadow-lift z-30 mt-1 w-64 p-2 text-sm">
|
|
122
121
|
<li>
|
|
123
122
|
<button id="home-drop-normal" class="flex items-start gap-2.5 rounded-lg px-3 py-2.5 hover:bg-base-200 text-left w-full">
|
|
124
123
|
<span class="mt-0.5 text-error shrink-0">{{{icon "warning" class="h-4 w-4"}}}</span>
|
|
125
124
|
<span>
|
|
126
125
|
<span class="block font-semibold">Drop tables</span>
|
|
127
126
|
<span class="block text-xs text-base-content/50">Permanently deletes tables and all data</span>
|
|
128
127
|
</span>
|
|
129
128
|
</button>
|
|
130
129
|
</li>
|
|
131
130
|
<li>
|
|
132
131
|
<button id="home-drop-force" class="flex items-start gap-2.5 rounded-lg px-3 py-2.5 hover:bg-error/10 text-left w-full text-error">
|
|
133
132
|
<span class="mt-0.5 shrink-0">{{{icon "warning" class="h-4 w-4"}}}</span>
|
|
134
133
|
<span>
|
|
135
134
|
<span class="block font-semibold">Force Drop <span class="text-[10px] font-bold uppercase tracking-wide rounded bg-error/15 px-1 py-0.5 ml-1">ignore FK</span></span>
|
|
136
135
|
<span class="block text-xs text-error/60">Drops with FK checks disabled; cascades on Postgres</span>
|
|
137
136
|
</span>
|
|
138
137
|
</button>
|
|
139
138
|
</li>
|
|
140
139
|
</ul>
|
|
141
140
|
</div>
|
|
142
141
|
<div class="ml-auto">
|
|
143
142
|
<button id="home-bulk-deselect"
|
|
144
143
|
class="btn btn-ghost btn-sm gap-1 text-base-content/50 hover:text-base-content">
|
|
145
144
|
{{{icon "x" class="h-3.5 w-3.5"}}}
|
|
146
145
|
Deselect all
|
|
147
146
|
</button>
|
|
148
147
|
</div>
|
|
149
148
|
</div>
|
|
150
149
|
</div>
|
|
151
150
|
{{/unless}}
|
|
152
151
|
<div class="app-card overflow-hidden">
|
|
153
152
|
<div class="overflow-x-auto">
|
|
154
153
|
<table class="table table-sm" id="home-tables-table">
|
|
155
154
|
<thead>
|
|
156
155
|
<tr class="bg-base-200/70 text-xs uppercase tracking-wide text-base-content/50">
|
|
157
156
|
{{#unless readonly}}
|
|
158
157
|
<th class="w-10">
|
|
159
158
|
<input type="checkbox" id="home-select-all"
|
|
160
159
|
class="checkbox checkbox-sm checkbox-primary"
|
|
161
160
|
title="Select all tables" />
|
|
162
161
|
</th>
|
|
163
162
|
{{/unless}}
|
|
164
163
|
<th class="font-semibold">Table</th>
|
|
165
164
|
<th class="text-right font-semibold">Columns</th>
|
|
166
165
|
<th class="text-right font-semibold">Rows</th>
|
|
167
166
|
<th class="text-right font-semibold">Actions</th>
|
|
168
167
|
</tr>
|
|
169
168
|
</thead>
|
|
170
169
|
<tbody>
|
|
171
170
|
{{#each tables}}
|
|
172
171
|
<tr class="group hover:bg-base-200/50 home-table-row" data-table="{{this}}">
|
|
173
172
|
{{#unless ../readonly}}
|
|
174
173
|
<td class="w-10">
|
|
175
174
|
<input type="checkbox"
|
|
176
175
|
class="checkbox checkbox-sm checkbox-primary home-table-checkbox"
|
|
177
176
|
data-table="{{this}}"
|
|
178
177
|
title="Select {{this}}" />
|
|
179
178
|
</td>
|
|
180
179
|
{{/unless}}
|
|
181
180
|
<td>
|
|
182
181
|
<a href="{{../basePath}}/tables/{{this}}" class="inline-flex items-center gap-2.5 font-medium">
|
|
183
182
|
<span class="grid h-8 w-8 place-items-center rounded-lg bg-primary/10 text-primary transition-colors group-hover:bg-primary/20">
|
|
184
183
|
{{{icon "table" class="h-4 w-4"}}}
|
|
185
184
|
</span>
|
|
186
185
|
<span class="font-mono text-[13.5px]">{{this}}</span>
|
|
187
186
|
</a>
|
|
188
187
|
</td>
|
|
189
188
|
<td class="text-right tabular-nums text-base-content/60">{{lookup ../cols this}}</td>
|
|
190
189
|
<td class="text-right tabular-nums text-base-content/60">{{lookup ../counts this}}</td>
|
|
191
190
|
<td class="text-right">
|
|
192
191
|
<a href="{{../basePath}}/tables/{{this}}" class="btn btn-ghost btn-xs gap-1 text-base-content/70 hover:text-primary">
|
|
193
192
|
Browse
|
|
194
193
|
{{{icon "arrowRight" class="h-3.5 w-3.5"}}}
|
|
195
194
|
</a>
|
|
196
195
|
</td>
|
|
197
196
|
</tr>
|
|
198
197
|
{{/each}}
|
|
199
198
|
</tbody>
|
|
200
199
|
</table>
|
|
201
200
|
</div>
|
|
202
201
|
</div>
|
|
203
202
|
{{else}}
|
|
204
203
|
<div class="empty-state">
|
|
205
204
|
<span class="empty-icon">
|
|
206
205
|
{{{icon "table" class="h-7 w-7"}}}
|
|
207
206
|
</span>
|
|
208
207
|
<p class="text-sm font-medium text-base-content/70">No tables yet</p>
|
|
209
208
|
<p class="max-w-sm text-sm text-base-content/45">Create one with the table designer, or run a SQL statement in the query editor to get started.</p>
|
|
210
209
|
<div class="mt-2 flex gap-2">
|
|
211
210
|
{{#unless readonly}}
|
|
212
211
|
<a href="{{basePath}}/designer" class="btn btn-primary btn-sm">New table</a>
|
|
213
212
|
{{/unless}}
|
|
214
213
|
<a href="{{basePath}}/query" class="btn btn-outline btn-sm">Query editor</a>
|
|
215
214
|
</div>
|
|
216
215
|
</div>
|
|
217
216
|
{{/if}}
|
|
218
217
|
</div>
|
|
218
|
+
<div class="space-y-8">
|
|
219
219
|
<div class="page-header">
|
|
220
220
|
<div class="page-header-row">
|
|
221
221
|
<div>
|
|
222
222
|
<div class="page-pretitle">Overview</div>
|
|
223
223
|
<h1 class="page-title">Database</h1>
|
|
224
224
|
<p class="mt-0.5 text-xs text-base-content/60">Browse, edit, and query the connected database from your browser.</p>
|
|
225
225
|
</div>
|
|
226
226
|
<div class="page-actions">
|
|
227
227
|
<a href="{{basePath}}/query/{{encodeUri dbId}}" class="btn btn-ghost btn-sm gap-1.5">
|
|
228
228
|
{{{icon "query" class="h-4 w-4"}}}
|
|
229
229
|
Query
|
|
230
230
|
</a>
|
|
231
231
|
{{#unless readonly}}
|
|
232
232
|
<a href="{{basePath}}/designer/{{encodeUri dbId}}" class="btn btn-primary btn-sm gap-1.5">
|
|
233
233
|
{{{icon "plus" class="h-4 w-4"}}}
|
|
234
234
|
New table
|
|
235
235
|
</a>
|
|
236
236
|
{{/unless}}
|
|
237
237
|
</div>
|
|
238
238
|
</div>
|
|
239
239
|
</div>
|
|
240
240
|
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4">
|
|
241
241
|
<div class="stat-card">
|
|
242
242
|
<span class="stat-icon bg-primary/10 text-primary">
|
|
243
243
|
{{{icon "table" class="h-6 w-6"}}}
|
|
244
244
|
</span>
|
|
245
245
|
<div>
|
|
246
246
|
<div class="text-2xl font-extrabold tabular-nums">{{tables.length}}</div>
|
|
247
247
|
<div class="text-[13px] text-base-content/50">Tables</div>
|
|
248
248
|
</div>
|
|
249
249
|
</div>
|
|
250
250
|
<div class="stat-card">
|
|
251
251
|
<span class="stat-icon bg-secondary/10 text-secondary">
|
|
252
252
|
{{{icon "stats" class="h-6 w-6"}}}
|
|
253
253
|
</span>
|
|
254
254
|
<div>
|
|
255
255
|
<div class="text-2xl font-extrabold tabular-nums">{{totalRows}}</div>
|
|
256
256
|
<div class="text-[13px] text-base-content/50">Total rows</div>
|
|
257
257
|
</div>
|
|
258
258
|
</div>
|
|
259
259
|
<div class="stat-card">
|
|
260
260
|
<span class="stat-icon bg-accent/10 text-accent">
|
|
261
261
|
{{{icon "seed" class="h-6 w-6"}}}
|
|
262
262
|
</span>
|
|
263
263
|
<div>
|
|
264
264
|
<div class="text-2xl font-extrabold tabular-nums">{{savedQueries}}</div>
|
|
265
265
|
<div class="text-[13px] text-base-content/50">Saved queries</div>
|
|
266
266
|
</div>
|
|
267
267
|
</div>
|
|
268
268
|
<div class="stat-card">
|
|
269
269
|
<span class="stat-icon {{#if isPostgres}}bg-info/10 text-info{{else}}bg-emerald-500/10 text-emerald-500{{/if}}">
|
|
270
270
|
{{{icon "database" class="h-6 w-6"}}}
|
|
271
271
|
</span>
|
|
272
272
|
<div>
|
|
273
273
|
<div class="flex items-center gap-1.5">
|
|
274
274
|
<span class="text-xl font-extrabold">{{dialectName}}</span>
|
|
275
275
|
{{#if readonly}}
|
|
276
276
|
<span class="rounded bg-warning/20 px-1.5 py-0.5 text-[10px] font-bold uppercase text-warning">Read-only</span>
|
|
277
277
|
{{else}}
|
|
278
278
|
<span class="rounded bg-success/20 px-1.5 py-0.5 text-[10px] font-bold uppercase text-success">Writable</span>
|
|
279
279
|
{{/if}}
|
|
280
280
|
</div>
|
|
281
281
|
<div class="truncate font-mono text-xs text-base-content/50" title="{{dbPath}}">{{dbPath}}</div>
|
|
282
282
|
</div>
|
|
283
283
|
</div>
|
|
284
284
|
</div>
|
|
285
285
|
<div>
|
|
286
286
|
<div class="mb-3 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
|
287
287
|
<div class="flex items-center gap-2">
|
|
288
288
|
<h2 class="text-lg font-bold">Tables</h2>
|
|
289
289
|
<span id="home-table-count" class="text-sm text-base-content/45">
|
|
290
290
|
<span id="home-table-count-text">{{tables.length}} total</span>
|
|
291
291
|
</span>
|
|
292
292
|
</div>
|
|
293
293
|
{{#if tables.length}}
|
|
294
294
|
<div class="relative w-full sm:w-64">
|
|
295
295
|
<span class="pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-base-content/40">
|
|
296
296
|
{{{icon "search" class="h-4 w-4"}}}
|
|
297
297
|
</span>
|
|
298
298
|
<input id="home-table-search" type="text" placeholder="Search tables…" autocomplete="off" spellcheck="false"
|
|
299
299
|
class="field-input text-xs sm:text-sm !py-1.5 !pl-9 !pr-8 w-full" />
|
|
300
300
|
<button id="home-table-search-clear" type="button" class="hidden absolute right-2.5 top-1/2 -translate-y-1/2 text-base-content/40 hover:text-base-content focus:outline-none" title="Clear search">
|
|
301
301
|
{{{icon "x" class="h-3.5 w-3.5"}}}
|
|
302
302
|
</button>
|
|
303
303
|
</div>
|
|
304
304
|
{{/if}}
|
|
305
305
|
</div>
|
|
306
306
|
{{#if tables.length}}
|
|
307
307
|
{{#unless readonly}}
|
|
308
308
|
<div id="home-bulk-toolbar"
|
|
309
309
|
style="max-height: 0; overflow: hidden; transition: max-height 0.22s cubic-bezier(0.4,0,0.2,1), opacity 0.18s ease; opacity: 0;"
|
|
310
310
|
aria-hidden="true">
|
|
311
311
|
<div class="mb-3 flex flex-wrap items-center gap-3 rounded-xl border border-base-300 bg-base-100 px-4 py-3 shadow-soft">
|
|
312
312
|
<span id="home-bulk-count"
|
|
313
313
|
class="inline-flex items-center gap-1.5 rounded-full bg-primary/10 px-3 py-1 text-xs font-bold text-primary">
|
|
314
314
|
<span id="home-bulk-count-num">0</span> selected
|
|
315
315
|
</span>
|
|
316
316
|
<div class="h-4 w-px bg-base-300"></div>
|
|
317
317
|
<div class="dropdown dropdown-bottom" id="home-dd-clear">
|
|
318
318
|
<button tabindex="0" id="home-btn-clear"
|
|
319
319
|
class="btn btn-sm btn-warning gap-1.5 font-semibold">
|
|
320
320
|
{{{icon "refresh" class="h-3.5 w-3.5"}}}
|
|
321
321
|
Clear Rows
|
|
322
322
|
{{{icon "chevronDown" class="h-3 w-3 opacity-60"}}}
|
|
323
323
|
</button>
|
|
324
324
|
<ul tabindex="0"
|
|
325
325
|
class="dropdown-content menu bg-base-100 rounded-xl border border-base-300 shadow-lift z-30 mt-1 w-64 p-2 text-sm">
|
|
326
326
|
<li>
|
|
327
327
|
<button id="home-clear-normal" class="flex items-start gap-2.5 rounded-lg px-3 py-2.5 hover:bg-base-200 text-left w-full">
|
|
328
328
|
<span class="mt-0.5 text-warning shrink-0">{{{icon "warning" class="h-4 w-4"}}}</span>
|
|
329
329
|
<span>
|
|
330
330
|
<span class="block font-semibold">Clear rows</span>
|
|
331
331
|
<span class="block text-xs text-base-content/50">Deletes all rows; fails if FK references exist</span>
|
|
332
332
|
</span>
|
|
333
333
|
</button>
|
|
334
334
|
</li>
|
|
335
335
|
<li>
|
|
336
336
|
<button id="home-clear-force" class="flex items-start gap-2.5 rounded-lg px-3 py-2.5 hover:bg-error/10 text-left w-full text-error">
|
|
337
337
|
<span class="mt-0.5 shrink-0">{{{icon "warning" class="h-4 w-4"}}}</span>
|
|
338
338
|
<span>
|
|
339
339
|
<span class="block font-semibold">Force Clear <span class="text-[10px] font-bold uppercase tracking-wide rounded bg-error/15 px-1 py-0.5 ml-1">ignore FK</span></span>
|
|
340
340
|
<span class="block text-xs text-error/60">Disables FK checks; deletes regardless of references</span>
|
|
341
341
|
</span>
|
|
342
342
|
</button>
|
|
343
343
|
</li>
|
|
344
344
|
</ul>
|
|
345
345
|
</div>
|
|
346
346
|
<div class="dropdown dropdown-bottom" id="home-dd-drop">
|
|
347
347
|
<button tabindex="0" id="home-btn-drop"
|
|
348
348
|
class="btn btn-sm btn-error gap-1.5 font-semibold text-white">
|
|
349
349
|
{{{icon "warning" class="h-3.5 w-3.5"}}}
|
|
350
350
|
Drop Tables
|
|
351
351
|
{{{icon "chevronDown" class="h-3 w-3 opacity-60"}}}
|
|
352
352
|
</button>
|
|
353
353
|
<ul tabindex="0"
|
|
354
354
|
class="dropdown-content menu bg-base-100 rounded-xl border border-base-300 shadow-lift z-30 mt-1 w-64 p-2 text-sm">
|
|
355
355
|
<li>
|
|
356
356
|
<button id="home-drop-normal" class="flex items-start gap-2.5 rounded-lg px-3 py-2.5 hover:bg-base-200 text-left w-full">
|
|
357
357
|
<span class="mt-0.5 text-error shrink-0">{{{icon "warning" class="h-4 w-4"}}}</span>
|
|
358
358
|
<span>
|
|
359
359
|
<span class="block font-semibold">Drop tables</span>
|
|
360
360
|
<span class="block text-xs text-base-content/50">Permanently deletes tables and all data</span>
|
|
361
361
|
</span>
|
|
362
362
|
</button>
|
|
363
363
|
</li>
|
|
364
364
|
<li>
|
|
365
365
|
<button id="home-drop-force" class="flex items-start gap-2.5 rounded-lg px-3 py-2.5 hover:bg-error/10 text-left w-full text-error">
|
|
366
366
|
<span class="mt-0.5 shrink-0">{{{icon "warning" class="h-4 w-4"}}}</span>
|
|
367
367
|
<span>
|
|
368
368
|
<span class="block font-semibold">Force Drop <span class="text-[10px] font-bold uppercase tracking-wide rounded bg-error/15 px-1 py-0.5 ml-1">ignore FK</span></span>
|
|
369
369
|
<span class="block text-xs text-error/60">Drops with FK checks disabled; cascades on Postgres</span>
|
|
370
370
|
</span>
|
|
371
371
|
</button>
|
|
372
372
|
</li>
|
|
373
373
|
</ul>
|
|
374
374
|
</div>
|
|
375
375
|
<div class="ml-auto">
|
|
376
376
|
<button id="home-bulk-deselect"
|
|
377
377
|
class="btn btn-ghost btn-sm gap-1 text-base-content/50 hover:text-base-content">
|
|
378
378
|
{{{icon "x" class="h-3.5 w-3.5"}}}
|
|
379
379
|
Deselect all
|
|
380
380
|
</button>
|
|
381
381
|
</div>
|
|
382
382
|
</div>
|
|
383
383
|
</div>
|
|
384
384
|
{{/unless}}
|
|
385
385
|
<div class="app-card overflow-hidden">
|
|
386
386
|
<div class="overflow-x-auto">
|
|
387
387
|
<table class="table table-sm" id="home-tables-table">
|
|
388
388
|
<thead>
|
|
389
389
|
<tr class="bg-base-200/70 text-xs uppercase tracking-wide text-base-content/50">
|
|
390
390
|
{{#unless readonly}}
|
|
391
391
|
<th class="w-10">
|
|
392
392
|
<input type="checkbox" id="home-select-all"
|
|
393
393
|
class="checkbox checkbox-sm checkbox-primary"
|
|
394
394
|
title="Select all tables" />
|
|
395
395
|
</th>
|
|
396
396
|
{{/unless}}
|
|
397
397
|
<th class="font-semibold">Table</th>
|
|
398
398
|
<th class="text-right font-semibold">Columns</th>
|
|
399
399
|
<th class="text-right font-semibold">Rows</th>
|
|
400
400
|
<th class="text-right font-semibold">Actions</th>
|
|
401
401
|
</tr>
|
|
402
402
|
</thead>
|
|
403
403
|
<tbody id="home-tables-tbody">
|
|
404
404
|
{{#each tables}}
|
|
405
405
|
<tr class="group hover:bg-base-200/50 home-table-row" data-table="{{this}}">
|
|
406
406
|
{{#unless ../readonly}}
|
|
407
407
|
<td class="w-10">
|
|
408
408
|
<input type="checkbox"
|
|
409
409
|
class="checkbox checkbox-sm checkbox-primary home-table-checkbox"
|
|
410
410
|
data-table="{{this}}"
|
|
411
411
|
title="Select {{this}}" />
|
|
412
412
|
</td>
|
|
413
413
|
{{/unless}}
|
|
414
414
|
<td>
|
|
415
415
|
<a href="{{../basePath}}/tables/{{encodeUri ../dbId}}/{{encodeUri this}}" class="inline-flex items-center gap-2.5 font-medium">
|
|
416
416
|
<span class="grid h-8 w-8 place-items-center rounded-lg bg-primary/10 text-primary transition-colors group-hover:bg-primary/20">
|
|
417
417
|
{{{icon "table" class="h-4 w-4"}}}
|
|
418
418
|
</span>
|
|
419
419
|
<span class="font-mono text-[13.5px]">{{this}}</span>
|
|
420
420
|
</a>
|
|
421
421
|
</td>
|
|
422
422
|
<td class="text-right tabular-nums text-base-content/60">{{lookup ../cols this}}</td>
|
|
423
423
|
<td class="text-right tabular-nums text-base-content/60">{{lookup ../counts this}}</td>
|
|
424
424
|
<td class="text-right">
|
|
425
425
|
<a href="{{../basePath}}/tables/{{encodeUri ../dbId}}/{{encodeUri this}}" class="btn btn-ghost btn-xs gap-1 text-base-content/70 hover:text-primary">
|
|
426
426
|
Browse
|
|
427
427
|
{{{icon "arrowRight" class="h-3.5 w-3.5"}}}
|
|
428
428
|
</a>
|
|
429
429
|
</td>
|
|
430
430
|
</tr>
|
|
431
431
|
{{/each}}
|
|
432
432
|
<tr id="home-no-tables-match" class="hidden">
|
|
433
433
|
<td colspan="{{#if readonly}}4{{else}}5{{/if}}" class="py-12 text-center text-sm text-base-content/50">
|
|
434
434
|
<div class="flex flex-col items-center justify-center gap-2">
|
|
435
435
|
{{{icon "search" class="h-6 w-6 text-base-content/30"}}}
|
|
436
436
|
<p>No tables matching "<span id="home-search-query-text" class="font-semibold font-mono text-base-content/80"></span>"</p>
|
|
437
437
|
</div>
|
|
438
438
|
</td>
|
|
439
439
|
</tr>
|
|
440
440
|
</tbody>
|
|
441
441
|
</table>
|
|
442
442
|
</div>
|
|
443
443
|
</div>
|
|
444
444
|
{{else}}
|
|
445
445
|
<div class="empty-state">
|
|
446
446
|
<span class="empty-icon">
|
|
447
447
|
{{{icon "table" class="h-7 w-7"}}}
|
|
448
448
|
</span>
|
|
449
449
|
<p class="text-sm font-medium text-base-content/70">No tables yet</p>
|
|
450
450
|
<p class="max-w-sm text-sm text-base-content/45">Create one with the table designer, or run a SQL statement in the query editor to get started.</p>
|
|
451
451
|
<div class="mt-2 flex gap-2">
|
|
452
452
|
{{#unless readonly}}
|
|
453
453
|
<a href="{{basePath}}/designer/{{encodeUri dbId}}" class="btn btn-primary btn-sm">New table</a>
|
|
454
454
|
{{/unless}}
|
|
455
455
|
<a href="{{basePath}}/query/{{encodeUri dbId}}" class="btn btn-outline btn-sm">Query editor</a>
|
|
456
456
|
</div>
|
|
457
457
|
</div>
|
|
458
458
|
{{/if}}
|
|
459
459
|
</div>
|