@vincent99/vlib 0.1.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 (92) hide show
  1. package/LICENSE +178 -0
  2. package/README.md +107 -0
  3. package/bin/vlib.js +10 -0
  4. package/dist/AdminForm.vue_vue_type_style_index_0_lang-xCk1ywLq.js +753 -0
  5. package/dist/auth/middleware.d.ts +18 -0
  6. package/dist/auth/middleware.d.ts.map +1 -0
  7. package/dist/auth/middleware.js +44 -0
  8. package/dist/auth/middleware.js.map +1 -0
  9. package/dist/auth/password.d.ts +10 -0
  10. package/dist/auth/password.d.ts.map +1 -0
  11. package/dist/auth/password.js +44 -0
  12. package/dist/auth/password.js.map +1 -0
  13. package/dist/cli.d.ts +3 -0
  14. package/dist/cli.d.ts.map +1 -0
  15. package/dist/cli.js +104 -0
  16. package/dist/cli.js.map +1 -0
  17. package/dist/components/AdminForm.vue.d.ts +7 -0
  18. package/dist/components/AdminTable.vue.d.ts +5 -0
  19. package/dist/components/AppLayout.vue.d.ts +36 -0
  20. package/dist/components/NavSidebar.vue.d.ts +11 -0
  21. package/dist/components/TableView.vue.d.ts +52 -0
  22. package/dist/components/index.d.ts +6 -0
  23. package/dist/components/index.js +8 -0
  24. package/dist/components/types.d.ts +25 -0
  25. package/dist/db/index.d.ts +12 -0
  26. package/dist/db/index.d.ts.map +1 -0
  27. package/dist/db/index.js +84 -0
  28. package/dist/db/index.js.map +1 -0
  29. package/dist/db/migrate.d.ts +2 -0
  30. package/dist/db/migrate.d.ts.map +1 -0
  31. package/dist/db/migrate.js +94 -0
  32. package/dist/db/migrate.js.map +1 -0
  33. package/dist/index.d.ts +3 -0
  34. package/dist/index.js +11 -0
  35. package/dist/router/index.d.ts +33 -0
  36. package/dist/router/index.js +62 -0
  37. package/dist/server/api/admin.d.ts +3 -0
  38. package/dist/server/api/admin.d.ts.map +1 -0
  39. package/dist/server/api/admin.js +184 -0
  40. package/dist/server/api/admin.js.map +1 -0
  41. package/dist/server/api/auth.d.ts +3 -0
  42. package/dist/server/api/auth.d.ts.map +1 -0
  43. package/dist/server/api/auth.js +66 -0
  44. package/dist/server/api/auth.js.map +1 -0
  45. package/dist/server/index.d.ts +17 -0
  46. package/dist/server/index.d.ts.map +1 -0
  47. package/dist/server/index.js +47 -0
  48. package/dist/server/index.js.map +1 -0
  49. package/dist/types.d.ts +53 -0
  50. package/dist/types.d.ts.map +1 -0
  51. package/dist/types.js +3 -0
  52. package/dist/types.js.map +1 -0
  53. package/dist/vlib.css +1 -0
  54. package/package.json +91 -0
  55. package/src/components/AdminForm.vue +491 -0
  56. package/src/components/AdminTable.vue +269 -0
  57. package/src/components/AppLayout.vue +280 -0
  58. package/src/components/NavSidebar.vue +176 -0
  59. package/src/components/TableView.vue +379 -0
  60. package/src/components/index.ts +13 -0
  61. package/src/components/types.ts +28 -0
  62. package/templates/.env.example +4 -0
  63. package/templates/.prettierignore +3 -0
  64. package/templates/.prettierrc +6 -0
  65. package/templates/Dockerfile.ejs +31 -0
  66. package/templates/docker-compose.prod.yml.ejs +22 -0
  67. package/templates/docker-compose.yml.ejs +22 -0
  68. package/templates/eslint.config.mjs +42 -0
  69. package/templates/index.html.ejs +13 -0
  70. package/templates/package.json.ejs +44 -0
  71. package/templates/postcss.config.js.ejs +6 -0
  72. package/templates/schemas/001-initial.sql +35 -0
  73. package/templates/scripts/migrate.ts +13 -0
  74. package/templates/server/index.ts +13 -0
  75. package/templates/src/App.vue +8 -0
  76. package/templates/src/main.ts +6 -0
  77. package/templates/src/router.ts +26 -0
  78. package/templates/src/routes/_layout.vue +58 -0
  79. package/templates/src/routes/admin/_layout.vue +8 -0
  80. package/templates/src/routes/admin/index.vue +88 -0
  81. package/templates/src/routes/admin/tables/[table]/[id].vue +20 -0
  82. package/templates/src/routes/admin/tables/[table]/index.vue +10 -0
  83. package/templates/src/routes/admin/tables/[table]/new.vue +10 -0
  84. package/templates/src/routes/index.vue +34 -0
  85. package/templates/src/routes/login.vue +128 -0
  86. package/templates/src/stores/auth.ts +58 -0
  87. package/templates/src/styles/main.scss +98 -0
  88. package/templates/src/styles/variables.scss +7 -0
  89. package/templates/tailwind.config.js.ejs +27 -0
  90. package/templates/tsconfig.json.ejs +26 -0
  91. package/templates/tsconfig.server.json.ejs +17 -0
  92. package/templates/vite.config.ts.ejs +36 -0
@@ -0,0 +1,753 @@
1
+ import { defineComponent as K, ref as h, resolveComponent as H, openBlock as t, createElementBlock as a, Fragment as D, renderList as O, createElementVNode as e, toDisplayString as b, createCommentVNode as $, normalizeClass as F, withDirectives as M, createBlock as W, withCtx as A, createTextVNode as j, vShow as ae, onMounted as X, onUnmounted as le, renderSlot as z, createVNode as P, computed as x, watch as ne, withModifiers as Z, vModelCheckbox as Y, vModelText as J } from "vue";
2
+ import { useRouter as ee } from "vue-router";
3
+ const se = { class: "vl-nav" }, oe = {
4
+ key: 0,
5
+ class: "vl-nav__group"
6
+ }, ie = ["onClick"], ue = {
7
+ key: 0,
8
+ class: "vl-nav__group-icon"
9
+ }, re = { class: "vl-nav__group-label" }, ve = { class: "vl-nav__group-children" }, de = {
10
+ key: 0,
11
+ class: "vl-nav__link-icon"
12
+ }, ce = {
13
+ key: 0,
14
+ class: "vl-nav__link-icon"
15
+ }, me = /* @__PURE__ */ K({
16
+ __name: "NavSidebar",
17
+ props: {
18
+ items: {}
19
+ },
20
+ emits: ["navigate"],
21
+ setup(u) {
22
+ const y = u, S = h(
23
+ new Set(
24
+ y.items.filter((n) => n.children && n.defaultOpen !== !1).map((n) => n.label)
25
+ )
26
+ );
27
+ function k(n) {
28
+ return S.value.has(n);
29
+ }
30
+ function o(n) {
31
+ S.value.has(n) ? S.value.delete(n) : S.value.add(n);
32
+ }
33
+ return (n, p) => {
34
+ const C = H("RouterLink");
35
+ return t(), a("div", se, [
36
+ (t(!0), a(D, null, O(u.items, (m) => (t(), a(D, {
37
+ key: m.label
38
+ }, [
39
+ m.children ? (t(), a("div", oe, [
40
+ e("button", {
41
+ class: "vl-nav__group-header",
42
+ onClick: (f) => o(m.label)
43
+ }, [
44
+ m.icon ? (t(), a("span", ue, b(m.icon), 1)) : $("", !0),
45
+ e("span", re, b(m.label), 1),
46
+ e("span", {
47
+ class: F(["vl-nav__group-caret", { "vl-nav__group-caret--open": k(m.label) }])
48
+ }, "▸", 2)
49
+ ], 8, ie),
50
+ M(e("div", ve, [
51
+ (t(!0), a(D, null, O(m.children, (f) => (t(), W(C, {
52
+ key: f.to,
53
+ to: f.to,
54
+ class: "vl-nav__link vl-nav__link--child",
55
+ "active-class": "vl-nav__link--active",
56
+ "exact-active-class": "vl-nav__link--exact-active",
57
+ onClick: p[0] || (p[0] = (r) => n.$emit("navigate"))
58
+ }, {
59
+ default: A(() => [
60
+ f.icon ? (t(), a("span", de, b(f.icon), 1)) : $("", !0),
61
+ j(" " + b(f.label), 1)
62
+ ]),
63
+ _: 2
64
+ }, 1032, ["to"]))), 128))
65
+ ], 512), [
66
+ [ae, k(m.label)]
67
+ ])
68
+ ])) : (t(), W(C, {
69
+ key: 1,
70
+ to: m.to,
71
+ class: "vl-nav__link",
72
+ "active-class": "vl-nav__link--active",
73
+ "exact-active-class": "vl-nav__link--exact-active",
74
+ onClick: p[1] || (p[1] = (f) => n.$emit("navigate"))
75
+ }, {
76
+ default: A(() => [
77
+ m.icon ? (t(), a("span", ce, b(m.icon), 1)) : $("", !0),
78
+ j(" " + b(m.label), 1)
79
+ ]),
80
+ _: 2
81
+ }, 1032, ["to"]))
82
+ ], 64))), 128))
83
+ ]);
84
+ };
85
+ }
86
+ }), _e = { class: "vl-app" }, fe = { class: "vl-header" }, be = { class: "vl-header__left" }, he = { class: "vl-header__title" }, pe = { class: "vl-header__right" }, ye = { class: "vl-user-menu__name" }, ke = {
87
+ key: 0,
88
+ class: "vl-user-menu__dropdown"
89
+ }, $e = { class: "vl-body" }, ge = { class: "vl-main" }, Ct = /* @__PURE__ */ K({
90
+ __name: "AppLayout",
91
+ props: {
92
+ appName: { default: "App" },
93
+ user: { default: null },
94
+ navItems: { default: () => [] }
95
+ },
96
+ emits: ["logout"],
97
+ setup(u, { emit: y }) {
98
+ const S = y, k = ee(), o = h(!1), n = h(!1), p = h(null);
99
+ function C() {
100
+ n.value = !1, S("logout");
101
+ }
102
+ function m(f) {
103
+ p.value && !p.value.contains(f.target) && (n.value = !1);
104
+ }
105
+ return k.afterEach(() => {
106
+ o.value = !1;
107
+ }), X(() => document.addEventListener("click", m)), le(() => document.removeEventListener("click", m)), (f, r) => (t(), a("div", _e, [
108
+ e("header", fe, [
109
+ e("div", be, [
110
+ e("button", {
111
+ class: "vl-hamburger",
112
+ "aria-label": "Toggle navigation",
113
+ onClick: r[0] || (r[0] = (g) => o.value = !o.value)
114
+ }, [...r[4] || (r[4] = [
115
+ e("span", { class: "vl-hamburger__bar" }, null, -1),
116
+ e("span", { class: "vl-hamburger__bar" }, null, -1),
117
+ e("span", { class: "vl-hamburger__bar" }, null, -1)
118
+ ])]),
119
+ z(f.$slots, "logo", {}, () => [
120
+ e("span", he, b(u.appName), 1)
121
+ ])
122
+ ]),
123
+ e("div", pe, [
124
+ u.user ? (t(), a("div", {
125
+ key: 0,
126
+ ref_key: "userMenuRef",
127
+ ref: p,
128
+ class: "vl-user-menu"
129
+ }, [
130
+ e("button", {
131
+ class: "vl-user-menu__btn",
132
+ onClick: r[1] || (r[1] = (g) => n.value = !n.value)
133
+ }, [
134
+ e("span", ye, b(u.user.displayName || u.user.username), 1),
135
+ r[5] || (r[5] = e("span", { class: "vl-user-menu__caret" }, "▾", -1))
136
+ ]),
137
+ n.value ? (t(), a("div", ke, [
138
+ e("button", {
139
+ class: "vl-user-menu__item",
140
+ onClick: C
141
+ }, "Logout")
142
+ ])) : $("", !0)
143
+ ], 512)) : $("", !0)
144
+ ])
145
+ ]),
146
+ e("div", $e, [
147
+ o.value ? (t(), a("div", {
148
+ key: 0,
149
+ class: "vl-sidebar-overlay",
150
+ onClick: r[2] || (r[2] = (g) => o.value = !1)
151
+ })) : $("", !0),
152
+ e("nav", {
153
+ class: F(["vl-sidebar", { "vl-sidebar--open": o.value }])
154
+ }, [
155
+ z(f.$slots, "sidebar", {}, () => [
156
+ P(me, {
157
+ items: u.navItems,
158
+ onNavigate: r[3] || (r[3] = (g) => o.value = !1)
159
+ }, null, 8, ["items"])
160
+ ])
161
+ ], 2),
162
+ e("main", ge, [
163
+ z(f.$slots, "default")
164
+ ])
165
+ ])
166
+ ]));
167
+ }
168
+ }), we = { class: "vl-table-view" }, Ce = { class: "vl-table-view__toolbar" }, Se = { class: "vl-table-view__toolbar-left" }, Ne = {
169
+ key: 0,
170
+ class: "vl-table-view__selected-count"
171
+ }, Ee = { class: "vl-table-view__toolbar-right" }, Te = ["onClick"], Le = { class: "vl-table-view__scroll" }, Ve = { class: "vl-table" }, Ue = { class: "vl-table__th vl-table__th--check" }, je = ["checked", "indeterminate"], Re = ["onClick"], xe = {
172
+ key: 0,
173
+ class: "vl-table__sort-icon"
174
+ }, De = {
175
+ key: 0,
176
+ class: "vl-table__th vl-table__th--actions"
177
+ }, Ie = { class: "vl-table__td vl-table__td--check" }, Me = ["checked", "aria-label", "onChange"], Oe = {
178
+ key: 0,
179
+ class: "vl-table__td vl-table__td--actions"
180
+ }, ze = { key: 0 }, Ae = ["colspan"], Pe = {
181
+ key: 0,
182
+ class: "vl-table-view__pagination"
183
+ }, Be = ["disabled"], Je = { class: "vl-table-view__pagination-info" }, Fe = ["disabled"], Ke = /* @__PURE__ */ K({
184
+ __name: "TableView",
185
+ props: {
186
+ rows: {},
187
+ headers: {},
188
+ idKey: { default: "id" },
189
+ total: { default: 0 },
190
+ page: { default: 1 },
191
+ pageSize: { default: 50 },
192
+ actions: { default: () => [] }
193
+ },
194
+ emits: ["update:page", "action", "sort"],
195
+ setup(u, { expose: y, emit: S }) {
196
+ const k = u, o = S, n = h(/* @__PURE__ */ new Set()), p = h(null), C = h("asc"), m = x(() => Math.ceil((k.total || k.rows.length) / k.pageSize)), f = x(
197
+ () => k.rows.length > 0 && k.rows.every((i) => n.value.has(g(i)))
198
+ ), r = x(() => k.rows.some((i) => n.value.has(g(i))));
199
+ function g(i) {
200
+ return i[k.idKey];
201
+ }
202
+ function R() {
203
+ f.value ? k.rows.forEach((i) => n.value.delete(g(i))) : k.rows.forEach((i) => n.value.add(g(i)));
204
+ }
205
+ function V(i) {
206
+ n.value.has(i) ? n.value.delete(i) : n.value.add(i);
207
+ }
208
+ function N(i) {
209
+ o("action", i.key, Array.from(n.value)), n.value.clear();
210
+ }
211
+ function U(i) {
212
+ p.value === i ? C.value = C.value === "asc" ? "desc" : "asc" : (p.value = i, C.value = "asc"), o("sort", p.value, C.value);
213
+ }
214
+ function B(i) {
215
+ if (i == null)
216
+ return "";
217
+ if (typeof i == "object")
218
+ return JSON.stringify(i);
219
+ const E = String(i);
220
+ return E.length > 120 ? E.slice(0, 120) + "…" : E;
221
+ }
222
+ return y({ selectedIds: n }), (i, E) => (t(), a("div", we, [
223
+ e("div", Ce, [
224
+ e("div", Se, [
225
+ z(i.$slots, "toolbar-left", {}, () => [
226
+ n.value.size > 0 ? (t(), a("span", Ne, b(n.value.size) + " selected ", 1)) : $("", !0)
227
+ ])
228
+ ]),
229
+ e("div", Ee, [
230
+ n.value.size > 0 && u.actions.length > 0 ? (t(!0), a(D, { key: 0 }, O(u.actions, (_) => (t(), a("button", {
231
+ key: _.key,
232
+ class: F(["vl-btn", [`vl-btn--${_.variant || "default"}`]]),
233
+ onClick: (v) => N(_)
234
+ }, b(_.label), 11, Te))), 128)) : $("", !0),
235
+ z(i.$slots, "toolbar-right")
236
+ ])
237
+ ]),
238
+ e("div", Le, [
239
+ e("table", Ve, [
240
+ e("thead", null, [
241
+ e("tr", null, [
242
+ e("th", Ue, [
243
+ e("input", {
244
+ type: "checkbox",
245
+ checked: f.value,
246
+ indeterminate: r.value && !f.value,
247
+ "aria-label": "Select all",
248
+ onChange: R
249
+ }, null, 40, je)
250
+ ]),
251
+ (t(!0), a(D, null, O(u.headers, (_) => (t(), a("th", {
252
+ key: _.key,
253
+ class: F(["vl-table__th", { "vl-table__th--sortable": _.sortable !== !1 }]),
254
+ onClick: (v) => _.sortable !== !1 && U(_.key)
255
+ }, [
256
+ j(b(_.label) + " ", 1),
257
+ p.value === _.key ? (t(), a("span", xe, b(C.value === "asc" ? "↑" : "↓"), 1)) : $("", !0)
258
+ ], 10, Re))), 128)),
259
+ i.$slots["row-actions"] ? (t(), a("th", De, "Actions")) : $("", !0)
260
+ ])
261
+ ]),
262
+ e("tbody", null, [
263
+ (t(!0), a(D, null, O(u.rows, (_) => (t(), a("tr", {
264
+ key: g(_),
265
+ class: F(["vl-table__tr", { "vl-table__tr--selected": n.value.has(g(_)) }])
266
+ }, [
267
+ e("td", Ie, [
268
+ e("input", {
269
+ type: "checkbox",
270
+ checked: n.value.has(g(_)),
271
+ "aria-label": `Select row ${g(_)}`,
272
+ onChange: (v) => V(g(_))
273
+ }, null, 40, Me)
274
+ ]),
275
+ (t(!0), a(D, null, O(u.headers, (v) => (t(), a("td", {
276
+ key: v.key,
277
+ class: "vl-table__td"
278
+ }, [
279
+ z(i.$slots, `cell-${v.key}`, {
280
+ row: _,
281
+ value: _[v.key]
282
+ }, () => [
283
+ j(b(B(_[v.key])), 1)
284
+ ])
285
+ ]))), 128)),
286
+ i.$slots["row-actions"] ? (t(), a("td", Oe, [
287
+ z(i.$slots, "row-actions", {
288
+ id: g(_),
289
+ row: _
290
+ })
291
+ ])) : $("", !0)
292
+ ], 2))), 128)),
293
+ u.rows.length === 0 ? (t(), a("tr", ze, [
294
+ e("td", {
295
+ colspan: u.headers.length + 2,
296
+ class: "vl-table__empty"
297
+ }, " No rows found. ", 8, Ae)
298
+ ])) : $("", !0)
299
+ ])
300
+ ])
301
+ ]),
302
+ u.total > u.pageSize ? (t(), a("div", Pe, [
303
+ e("button", {
304
+ class: "vl-btn vl-btn--sm",
305
+ disabled: u.page <= 1,
306
+ onClick: E[0] || (E[0] = (_) => i.$emit("update:page", u.page - 1))
307
+ }, "← Prev", 8, Be),
308
+ e("span", Je, " Page " + b(u.page) + " of " + b(m.value) + "  (" + b(u.total) + " total) ", 1),
309
+ e("button", {
310
+ class: "vl-btn vl-btn--sm",
311
+ disabled: u.page >= m.value,
312
+ onClick: E[1] || (E[1] = (_) => i.$emit("update:page", u.page + 1))
313
+ }, "Next →", 8, Fe)
314
+ ])) : $("", !0)
315
+ ]));
316
+ }
317
+ }), Ge = { class: "vl-admin-table" }, qe = { class: "vl-admin-table__header" }, He = { class: "vl-admin-table__title" }, Xe = {
318
+ key: 0,
319
+ class: "vl-alert vl-alert--error"
320
+ }, Qe = ["onClick"], We = { class: "vl-modal" }, Ye = { class: "vl-modal__actions" }, St = /* @__PURE__ */ K({
321
+ __name: "AdminTable",
322
+ props: {
323
+ tableName: {}
324
+ },
325
+ setup(u) {
326
+ const y = u, S = h([]), k = h([]), o = h(0), n = h(1), p = h(50), C = h(void 0), m = h("asc"), f = h(null), r = h(null), g = x(
327
+ () => k.value.map((v) => ({ key: v.name, label: v.name, sortable: !0 }))
328
+ ), R = [
329
+ { key: "delete", label: "Delete Selected", variant: "danger" }
330
+ ];
331
+ async function V(v) {
332
+ n.value = v, await N();
333
+ }
334
+ async function N() {
335
+ f.value = null;
336
+ try {
337
+ const v = new URLSearchParams({
338
+ page: String(n.value),
339
+ pageSize: String(p.value),
340
+ ...C.value ? { orderBy: C.value, dir: m.value } : {}
341
+ }), d = await fetch(`/api/admin/tables/${y.tableName}?${v}`);
342
+ if (!d.ok)
343
+ throw new Error((await d.json()).error);
344
+ const T = await d.json();
345
+ S.value = T.rows, o.value = T.total, k.value.length === 0 && T.rows.length > 0 && (k.value = Object.keys(T.rows[0]).map((I) => ({
346
+ name: I,
347
+ type: "TEXT"
348
+ })));
349
+ } catch (v) {
350
+ f.value = String(v);
351
+ }
352
+ }
353
+ async function U() {
354
+ try {
355
+ const v = await fetch("/api/admin/tables");
356
+ if (!v.ok)
357
+ return;
358
+ const T = (await v.json()).find((I) => I.name === y.tableName);
359
+ T && (k.value = T.columns);
360
+ } catch {
361
+ }
362
+ }
363
+ function B(v, d) {
364
+ C.value = v, m.value = d, n.value = 1, N();
365
+ }
366
+ function i(v, d) {
367
+ v === "delete" && E(d);
368
+ }
369
+ function E(v) {
370
+ r.value = v;
371
+ }
372
+ async function _() {
373
+ if (!r.value)
374
+ return;
375
+ const v = r.value;
376
+ r.value = null;
377
+ try {
378
+ const d = await fetch(`/api/admin/tables/${y.tableName}`, {
379
+ method: "DELETE",
380
+ headers: { "Content-Type": "application/json" },
381
+ body: JSON.stringify({ ids: v })
382
+ });
383
+ if (!d.ok)
384
+ throw new Error((await d.json()).error);
385
+ await N();
386
+ } catch (d) {
387
+ f.value = String(d);
388
+ }
389
+ }
390
+ return ne(
391
+ () => y.tableName,
392
+ () => {
393
+ n.value = 1, k.value = [], U(), N();
394
+ }
395
+ ), X(() => {
396
+ U(), N();
397
+ }), (v, d) => {
398
+ const T = H("RouterLink");
399
+ return t(), a("div", Ge, [
400
+ e("div", qe, [
401
+ e("h1", He, b(u.tableName), 1),
402
+ P(T, {
403
+ to: `/admin/tables/${u.tableName}/new`,
404
+ class: "vl-btn vl-btn--primary"
405
+ }, {
406
+ default: A(() => [...d[2] || (d[2] = [
407
+ j(" + Add Row ", -1)
408
+ ])]),
409
+ _: 1
410
+ }, 8, ["to"])
411
+ ]),
412
+ f.value ? (t(), a("div", Xe, b(f.value), 1)) : $("", !0),
413
+ P(Ke, {
414
+ rows: S.value,
415
+ headers: g.value,
416
+ total: o.value,
417
+ page: n.value,
418
+ "page-size": p.value,
419
+ actions: R,
420
+ "onUpdate:page": V,
421
+ onAction: i,
422
+ onSort: B
423
+ }, {
424
+ "row-actions": A(({ row: I, id: G }) => [
425
+ P(T, {
426
+ to: `/admin/tables/${u.tableName}/${G}`,
427
+ class: "vl-btn vl-btn--sm"
428
+ }, {
429
+ default: A(() => [...d[3] || (d[3] = [
430
+ j("Edit", -1)
431
+ ])]),
432
+ _: 1
433
+ }, 8, ["to"]),
434
+ e("button", {
435
+ class: "vl-btn vl-btn--sm vl-btn--danger",
436
+ onClick: (Q) => E([G])
437
+ }, " Del ", 8, Qe)
438
+ ]),
439
+ _: 1
440
+ }, 8, ["rows", "headers", "total", "page", "page-size"]),
441
+ r.value ? (t(), a("div", {
442
+ key: 1,
443
+ class: "vl-modal-overlay",
444
+ onClick: d[1] || (d[1] = Z((I) => r.value = null, ["self"]))
445
+ }, [
446
+ e("div", We, [
447
+ d[6] || (d[6] = e("h3", { class: "vl-modal__title" }, "Confirm Delete", -1)),
448
+ e("p", null, [
449
+ j(" Delete " + b(r.value.length) + " row" + b(r.value.length !== 1 ? "s" : "") + "?", 1),
450
+ d[4] || (d[4] = e("br", null, null, -1)),
451
+ d[5] || (d[5] = j("This cannot be undone. ", -1))
452
+ ]),
453
+ e("div", Ye, [
454
+ e("button", {
455
+ class: "vl-btn",
456
+ onClick: d[0] || (d[0] = (I) => r.value = null)
457
+ }, "Cancel"),
458
+ e("button", {
459
+ class: "vl-btn vl-btn--danger",
460
+ onClick: _
461
+ }, " Delete ")
462
+ ])
463
+ ])
464
+ ])) : $("", !0)
465
+ ]);
466
+ };
467
+ }
468
+ }), Ze = { class: "vl-admin-form" }, et = { class: "vl-admin-form__header" }, tt = { class: "vl-admin-form__title" }, at = {
469
+ key: 0,
470
+ class: "vl-alert vl-alert--error"
471
+ }, lt = {
472
+ key: 1,
473
+ class: "vl-alert vl-alert--error"
474
+ }, nt = {
475
+ key: 2,
476
+ class: "vl-alert vl-alert--success"
477
+ }, st = {
478
+ key: 3,
479
+ class: "vl-admin-form__loading"
480
+ }, ot = { class: "vl-form__field-header" }, it = ["id", "onUpdate:modelValue"], ut = ["for"], rt = { class: "vl-form__type" }, vt = {
481
+ key: 0,
482
+ class: "vl-form__required"
483
+ }, dt = { class: "vl-form__input-wrap" }, ct = ["value"], mt = ["id", "onUpdate:modelValue"], _t = ["id", "onUpdate:modelValue", "placeholder"], ft = ["id", "onUpdate:modelValue"], bt = ["id", "onUpdate:modelValue"], ht = ["id", "onUpdate:modelValue"], pt = ["id", "onUpdate:modelValue"], yt = {
484
+ key: 2,
485
+ class: "vl-form__multi-warn"
486
+ }, kt = { class: "vl-form__actions" }, $t = ["disabled"], q = "— Multiple —", Nt = /* @__PURE__ */ K({
487
+ __name: "AdminForm",
488
+ props: {
489
+ tableName: {},
490
+ rowIds: {}
491
+ },
492
+ setup(u) {
493
+ const y = u, S = ee(), k = h([]), o = h({}), n = h({}), p = h({}), C = h(!0), m = h(!1), f = h(null), r = h(null), g = h(!1), R = x(() => !y.rowIds || y.rowIds.length === 0), V = x(() => {
494
+ var l;
495
+ return (((l = y.rowIds) == null ? void 0 : l.length) ?? 0) > 1;
496
+ }), N = x(() => y.rowIds ?? []), U = x(
497
+ () => k.value.filter((l) => R.value ? l.pk === 0 || l.dflt_value === null : l.pk === 0)
498
+ ), B = x(
499
+ () => V.value && U.value.some(
500
+ (l) => p.value[l.name] && o.value[l.name] === q
501
+ )
502
+ );
503
+ function i(l) {
504
+ return /bool/i.test(l.type);
505
+ }
506
+ function E(l) {
507
+ return /json/i.test(l.type) || l.name.toLowerCase() === "preferences";
508
+ }
509
+ function _(l) {
510
+ return /text|varchar|nvarchar|clob/i.test(l.type) && l.name.length > 40;
511
+ }
512
+ function v(l) {
513
+ return /int|real|float|double|numeric|decimal/i.test(l.type);
514
+ }
515
+ function d(l) {
516
+ return /date|time/i.test(l.type);
517
+ }
518
+ function T(l) {
519
+ const c = new Set(
520
+ Object.values(n.value).map((w) => w[l])
521
+ );
522
+ return c.size > 1 ? q : String([...c][0] ?? "");
523
+ }
524
+ async function I() {
525
+ const l = await fetch("/api/admin/tables");
526
+ if (!l.ok)
527
+ throw new Error("Failed to load table info");
528
+ const w = (await l.json()).find((s) => s.name === y.tableName);
529
+ if (!w)
530
+ throw new Error(`Table "${y.tableName}" not found`);
531
+ k.value = w.columns;
532
+ }
533
+ async function G() {
534
+ if (!R.value)
535
+ for (const l of N.value) {
536
+ const c = await fetch(`/api/admin/tables/${y.tableName}/${l}`);
537
+ if (!c.ok)
538
+ throw new Error(`Row ${l} not found`);
539
+ n.value[String(l)] = await c.json();
540
+ }
541
+ }
542
+ function Q() {
543
+ var l;
544
+ if (R.value) {
545
+ for (const c of U.value)
546
+ o.value[c.name] = c.dflt_value ?? null;
547
+ return;
548
+ }
549
+ for (const c of U.value)
550
+ if (V.value) {
551
+ const w = T(c.name);
552
+ o.value[c.name] = w, p.value[c.name] = !1;
553
+ } else {
554
+ const w = String(N.value[0]);
555
+ o.value[c.name] = ((l = n.value[w]) == null ? void 0 : l[c.name]) ?? null;
556
+ }
557
+ }
558
+ async function te() {
559
+ m.value = !1, r.value = null, g.value = !1, m.value = !0;
560
+ try {
561
+ if (R.value) {
562
+ const l = await fetch(`/api/admin/tables/${y.tableName}`, {
563
+ method: "POST",
564
+ headers: { "Content-Type": "application/json" },
565
+ body: JSON.stringify(o.value)
566
+ });
567
+ if (!l.ok)
568
+ throw new Error((await l.json()).error);
569
+ S.push(`/admin/tables/${y.tableName}`);
570
+ } else if (V.value) {
571
+ const l = {};
572
+ for (const c of U.value)
573
+ if (p.value[c.name] && o.value[c.name] !== q) {
574
+ if (c.name.toLowerCase().includes("password") && !o.value[c.name])
575
+ continue;
576
+ l[c.name] = o.value[c.name];
577
+ }
578
+ for (const c of N.value) {
579
+ const w = await fetch(`/api/admin/tables/${y.tableName}/${c}`, {
580
+ method: "PUT",
581
+ headers: { "Content-Type": "application/json" },
582
+ body: JSON.stringify(l)
583
+ });
584
+ if (!w.ok)
585
+ throw new Error((await w.json()).error);
586
+ }
587
+ S.push(`/admin/tables/${y.tableName}`);
588
+ } else {
589
+ const l = { ...o.value };
590
+ for (const w of U.value)
591
+ w.name.toLowerCase().includes("password") && !l[w.name] && delete l[w.name];
592
+ const c = await fetch(
593
+ `/api/admin/tables/${y.tableName}/${N.value[0]}`,
594
+ {
595
+ method: "PUT",
596
+ headers: { "Content-Type": "application/json" },
597
+ body: JSON.stringify(l)
598
+ }
599
+ );
600
+ if (!c.ok)
601
+ throw new Error((await c.json()).error);
602
+ g.value = !0;
603
+ }
604
+ } catch (l) {
605
+ r.value = String(l);
606
+ } finally {
607
+ m.value = !1;
608
+ }
609
+ }
610
+ return X(async () => {
611
+ try {
612
+ await I(), await G(), Q();
613
+ } catch (l) {
614
+ f.value = String(l);
615
+ } finally {
616
+ C.value = !1;
617
+ }
618
+ }), (l, c) => {
619
+ const w = H("RouterLink");
620
+ return t(), a("div", Ze, [
621
+ e("div", et, [
622
+ P(w, {
623
+ to: `/admin/tables/${u.tableName}`,
624
+ class: "vl-back-link"
625
+ }, {
626
+ default: A(() => [
627
+ j("← Back to " + b(u.tableName), 1)
628
+ ]),
629
+ _: 1
630
+ }, 8, ["to"]),
631
+ e("h1", tt, b(V.value ? `Edit ${N.value.length} rows` : R.value ? `New ${u.tableName} row` : "Edit row"), 1)
632
+ ]),
633
+ f.value ? (t(), a("div", at, b(f.value), 1)) : $("", !0),
634
+ r.value ? (t(), a("div", lt, b(r.value), 1)) : $("", !0),
635
+ g.value ? (t(), a("div", nt, " Saved successfully. ")) : $("", !0),
636
+ C.value ? (t(), a("div", st, "Loading…")) : (t(), a("form", {
637
+ key: 4,
638
+ class: "vl-form",
639
+ onSubmit: Z(te, ["prevent"])
640
+ }, [
641
+ (t(!0), a(D, null, O(U.value, (s) => (t(), a("div", {
642
+ key: s.name,
643
+ class: "vl-form__field"
644
+ }, [
645
+ e("div", ot, [
646
+ V.value ? M((t(), a("input", {
647
+ key: 0,
648
+ id: `enable-${s.name}`,
649
+ "onUpdate:modelValue": (L) => p.value[s.name] = L,
650
+ type: "checkbox",
651
+ class: "vl-form__field-enable"
652
+ }, null, 8, it)), [
653
+ [Y, p.value[s.name]]
654
+ ]) : $("", !0),
655
+ e("label", {
656
+ for: `field-${s.name}`,
657
+ class: "vl-form__label"
658
+ }, [
659
+ j(b(s.name) + " ", 1),
660
+ e("span", rt, b(s.type), 1),
661
+ s.notnull && !s.dflt_value ? (t(), a("span", vt, "*")) : $("", !0)
662
+ ], 8, ut)
663
+ ]),
664
+ e("div", dt, [
665
+ V.value && !p.value[s.name] ? (t(), a("input", {
666
+ key: 0,
667
+ type: "text",
668
+ value: T(s.name),
669
+ disabled: "",
670
+ class: "vl-form__input vl-form__input--disabled"
671
+ }, null, 8, ct)) : (t(), a(D, { key: 1 }, [
672
+ i(s) ? M((t(), a("input", {
673
+ key: 0,
674
+ id: `field-${s.name}`,
675
+ "onUpdate:modelValue": (L) => o.value[s.name] = L,
676
+ type: "checkbox",
677
+ class: "vl-form__checkbox"
678
+ }, null, 8, mt)), [
679
+ [Y, o.value[s.name]]
680
+ ]) : s.name.toLowerCase().includes("password") ? M((t(), a("input", {
681
+ key: 1,
682
+ id: `field-${s.name}`,
683
+ "onUpdate:modelValue": (L) => o.value[s.name] = L,
684
+ type: "password",
685
+ class: "vl-form__input",
686
+ placeholder: R.value ? "" : "(leave blank to keep unchanged)"
687
+ }, null, 8, _t)), [
688
+ [J, o.value[s.name]]
689
+ ]) : E(s) || _(s) ? M((t(), a("textarea", {
690
+ key: 2,
691
+ id: `field-${s.name}`,
692
+ "onUpdate:modelValue": (L) => o.value[s.name] = L,
693
+ class: "vl-form__input vl-form__input--textarea",
694
+ rows: "4"
695
+ }, null, 8, ft)), [
696
+ [J, o.value[s.name]]
697
+ ]) : v(s) ? M((t(), a("input", {
698
+ key: 3,
699
+ id: `field-${s.name}`,
700
+ "onUpdate:modelValue": (L) => o.value[s.name] = L,
701
+ type: "number",
702
+ class: "vl-form__input"
703
+ }, null, 8, bt)), [
704
+ [J, o.value[s.name]]
705
+ ]) : d(s) ? M((t(), a("input", {
706
+ key: 4,
707
+ id: `field-${s.name}`,
708
+ "onUpdate:modelValue": (L) => o.value[s.name] = L,
709
+ type: "datetime-local",
710
+ class: "vl-form__input"
711
+ }, null, 8, ht)), [
712
+ [J, o.value[s.name]]
713
+ ]) : M((t(), a("input", {
714
+ key: 5,
715
+ id: `field-${s.name}`,
716
+ "onUpdate:modelValue": (L) => o.value[s.name] = L,
717
+ type: "text",
718
+ class: "vl-form__input"
719
+ }, null, 8, pt)), [
720
+ [J, o.value[s.name]]
721
+ ])
722
+ ], 64)),
723
+ V.value && p.value[s.name] && o.value[s.name] === q ? (t(), a("span", yt, 'Cannot save "— Multiple —". Please enter a new value.')) : $("", !0)
724
+ ])
725
+ ]))), 128)),
726
+ e("div", kt, [
727
+ P(w, {
728
+ to: `/admin/tables/${u.tableName}`,
729
+ class: "vl-btn"
730
+ }, {
731
+ default: A(() => [...c[0] || (c[0] = [
732
+ j("Cancel", -1)
733
+ ])]),
734
+ _: 1
735
+ }, 8, ["to"]),
736
+ e("button", {
737
+ type: "submit",
738
+ class: "vl-btn vl-btn--primary",
739
+ disabled: m.value || B.value
740
+ }, b(m.value ? "Saving…" : "Save"), 9, $t)
741
+ ])
742
+ ], 32))
743
+ ]);
744
+ };
745
+ }
746
+ });
747
+ export {
748
+ Nt as _,
749
+ St as a,
750
+ Ct as b,
751
+ me as c,
752
+ Ke as d
753
+ };