elysia-rack 0.1.1

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 (50) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +241 -0
  3. package/dist/Dashboard-xpstr7fr.js +172 -0
  4. package/dist/Panel-fbp3wrsw.js +738 -0
  5. package/dist/index-37x76zdn.js +4 -0
  6. package/dist/index-zc7rgvmm.js +13 -0
  7. package/dist/panel-app.js +1 -0
  8. package/dist/panel-theme.js +1 -0
  9. package/dist/panel.css +1 -0
  10. package/dist/rack/adapter.d.ts +5 -0
  11. package/dist/rack/adapters/drizzle.d.ts +2 -0
  12. package/dist/rack/adapters/index.d.ts +59 -0
  13. package/dist/rack/adapters/prisma.d.ts +2 -0
  14. package/dist/rack/authorization.d.ts +23 -0
  15. package/dist/rack/idempotency.d.ts +9 -0
  16. package/dist/rack/index.d.ts +40 -0
  17. package/dist/rack/index.js +1362 -0
  18. package/dist/rack/models/drizzle.d.ts +4 -0
  19. package/dist/rack/models/index.d.ts +24 -0
  20. package/dist/rack/models/prisma.d.ts +4 -0
  21. package/dist/rack/openapi.d.ts +6 -0
  22. package/dist/rack/page.d.ts +33 -0
  23. package/dist/rack/query.d.ts +22 -0
  24. package/dist/rack/registry.d.ts +20 -0
  25. package/dist/rack/types/authorization.d.ts +16 -0
  26. package/dist/rack/types/idempotency.d.ts +46 -0
  27. package/dist/rack/types/index.d.ts +89 -0
  28. package/dist/rack/types/metadata.d.ts +36 -0
  29. package/dist/rack/types/openapi.d.ts +22 -0
  30. package/dist/rack/types/operations.d.ts +14 -0
  31. package/dist/rack/types/page.d.ts +41 -0
  32. package/dist/rack/types/query.d.ts +30 -0
  33. package/dist/rack/types/validation.d.ts +19 -0
  34. package/dist/react/index.d.ts +64 -0
  35. package/dist/react/index.js +126 -0
  36. package/dist/react/page/Dashboard.d.ts +7 -0
  37. package/dist/react/page/Panel.d.ts +36 -0
  38. package/dist/react/page/client/panel.d.ts +1 -0
  39. package/dist/react/page/client/theme.d.ts +1 -0
  40. package/dist/react/page/components/Breadcrumb.d.ts +7 -0
  41. package/dist/react/page/components/DataTable.d.ts +10 -0
  42. package/dist/react/page/components/Masthead.d.ts +7 -0
  43. package/dist/react/page/components/Pagination.d.ts +10 -0
  44. package/dist/react/page/components/Sidebar.d.ts +16 -0
  45. package/dist/react/page/components/Toolbar.d.ts +7 -0
  46. package/dist/react/page/components/href.d.ts +10 -0
  47. package/dist/react/page/components/panelForm.d.ts +9 -0
  48. package/dist/react/render.d.ts +2 -0
  49. package/dist/react/types.d.ts +16 -0
  50. package/package.json +89 -0
@@ -0,0 +1,738 @@
1
+ import"./index-37x76zdn.js";
2
+
3
+ // src/react/page/components/href.ts
4
+ function href(params, over = {}) {
5
+ const query = new URLSearchParams;
6
+ for (const [key, value] of Object.entries({ ...params, ...over })) {
7
+ if (value === undefined || value === null || value === "")
8
+ continue;
9
+ query.set(key, Array.isArray(value) ? value.join(",") : String(value));
10
+ }
11
+ const str = query.toString();
12
+ return str ? `?${str}` : "?";
13
+ }
14
+ function pageWindow(page, total) {
15
+ const keep = new Set([1, total, page - 1, page, page + 1]);
16
+ const sorted = [...keep].filter((p) => p >= 1 && p <= total).sort((a, b) => a - b);
17
+ const out = [];
18
+ let prev = 0;
19
+ for (const p of sorted) {
20
+ if (p - prev > 1)
21
+ out.push("…");
22
+ out.push(p);
23
+ prev = p;
24
+ }
25
+ return out;
26
+ }
27
+
28
+ // src/react/page/components/DataTable.tsx
29
+ import { jsxDEV } from "react/jsx-dev-runtime";
30
+ function statusBadge(value) {
31
+ if (typeof value !== "string")
32
+ return null;
33
+ const text = value.toLowerCase();
34
+ if (text === "active")
35
+ return "bg-success-muted text-success-muted-foreground";
36
+ if (text === "archived")
37
+ return "bg-warning-muted text-warning-muted-foreground";
38
+ return "bg-badge-background text-badge-foreground";
39
+ }
40
+ function cell(value) {
41
+ if (value === null || value === undefined)
42
+ return "";
43
+ if (value instanceof Date)
44
+ return value.toLocaleString("en-US");
45
+ if (typeof value === "object")
46
+ return JSON.stringify(value);
47
+ return String(value);
48
+ }
49
+ function DataTable({
50
+ columns,
51
+ rows,
52
+ sortable,
53
+ params,
54
+ selectable,
55
+ editable,
56
+ primaryKey
57
+ }) {
58
+ const activeSort = typeof params.sort === "string" ? params.sort : "";
59
+ const activeOrder = params.order === "desc" ? "desc" : "asc";
60
+ const pk = primaryKey ?? "id";
61
+ const extra = (selectable ? 1 : 0) + (editable ? 1 : 0);
62
+ if (rows.length === 0)
63
+ return /* @__PURE__ */ jsxDEV("div", {
64
+ className: "border-table-border overflow-x-auto rounded-md border",
65
+ children: /* @__PURE__ */ jsxDEV("table", {
66
+ className: "bg-table w-full border-collapse text-left text-sm",
67
+ "data-selectable": selectable ? true : undefined,
68
+ "data-editable": editable ? true : undefined,
69
+ "data-pk": primaryKey ?? "id",
70
+ children: /* @__PURE__ */ jsxDEV("tbody", {
71
+ "data-panel-rows": true,
72
+ children: /* @__PURE__ */ jsxDEV("tr", {
73
+ children: /* @__PURE__ */ jsxDEV("td", {
74
+ colSpan: columns.length + extra || 1,
75
+ className: "text-text-muted px-4 py-10 text-center",
76
+ children: "No records found."
77
+ }, undefined, false, undefined, this)
78
+ }, undefined, false, undefined, this)
79
+ }, undefined, false, undefined, this)
80
+ }, undefined, false, undefined, this)
81
+ }, undefined, false, undefined, this);
82
+ return /* @__PURE__ */ jsxDEV("div", {
83
+ className: "border-table-border overflow-x-auto rounded-md border",
84
+ children: /* @__PURE__ */ jsxDEV("table", {
85
+ className: "bg-table w-full border-collapse text-left text-sm",
86
+ "data-selectable": selectable ? true : undefined,
87
+ "data-editable": editable ? true : undefined,
88
+ "data-pk": primaryKey ?? "id",
89
+ children: [
90
+ /* @__PURE__ */ jsxDEV("thead", {
91
+ children: /* @__PURE__ */ jsxDEV("tr", {
92
+ className: "bg-table-header",
93
+ children: [
94
+ selectable ? /* @__PURE__ */ jsxDEV("th", {
95
+ className: "border-table-border border-b px-4 py-2",
96
+ children: /* @__PURE__ */ jsxDEV("input", {
97
+ type: "checkbox",
98
+ "data-select-all": true,
99
+ "aria-label": "Select all"
100
+ }, undefined, false, undefined, this)
101
+ }, undefined, false, undefined, this) : null,
102
+ columns.map((col) => /* @__PURE__ */ jsxDEV("th", {
103
+ "data-col": col,
104
+ className: "border-table-border border-b px-4 py-2 text-xs font-bold tracking-wider uppercase",
105
+ children: sortable?.includes(col) ? /* @__PURE__ */ jsxDEV("a", {
106
+ className: "text-link hover:text-link-hover",
107
+ "data-qlink": true,
108
+ "data-sort-link": col,
109
+ href: href(params, {
110
+ sort: col,
111
+ order: activeSort === col && activeOrder === "asc" ? "desc" : "asc"
112
+ }),
113
+ children: [
114
+ col,
115
+ /* @__PURE__ */ jsxDEV("span", {
116
+ "data-sort-ind": true,
117
+ children: activeSort === col ? activeOrder === "asc" ? " ▲" : " ▼" : ""
118
+ }, undefined, false, undefined, this)
119
+ ]
120
+ }, undefined, true, undefined, this) : col
121
+ }, col, false, undefined, this)),
122
+ editable ? /* @__PURE__ */ jsxDEV("th", {
123
+ className: "border-table-border border-b px-4 py-2 text-xs font-bold tracking-wider uppercase",
124
+ children: "Actions"
125
+ }, undefined, false, undefined, this) : null
126
+ ]
127
+ }, undefined, true, undefined, this)
128
+ }, undefined, false, undefined, this),
129
+ /* @__PURE__ */ jsxDEV("tbody", {
130
+ "data-panel-rows": true,
131
+ children: rows.map((row, i) => /* @__PURE__ */ jsxDEV("tr", {
132
+ className: "hover:bg-table-row-hover",
133
+ children: [
134
+ selectable ? /* @__PURE__ */ jsxDEV("td", {
135
+ className: "border-table-border border-t px-4 py-2",
136
+ children: /* @__PURE__ */ jsxDEV("input", {
137
+ type: "checkbox",
138
+ "data-select-row": true,
139
+ value: String(row[pk] ?? ""),
140
+ "aria-label": `Select row ${String(row[pk] ?? i)}`
141
+ }, undefined, false, undefined, this)
142
+ }, undefined, false, undefined, this) : null,
143
+ columns.map((col) => /* @__PURE__ */ jsxDEV("td", {
144
+ className: "border-table-border border-t px-4 py-2",
145
+ children: col === "status" && statusBadge(row[col]) ? /* @__PURE__ */ jsxDEV("span", {
146
+ className: `rounded-full px-2 py-0.5 text-xs font-semibold ${statusBadge(row[col]) ?? ""}`,
147
+ children: cell(row[col])
148
+ }, undefined, false, undefined, this) : cell(row[col])
149
+ }, col, false, undefined, this)),
150
+ editable ? /* @__PURE__ */ jsxDEV("td", {
151
+ className: "border-table-border border-t px-4 py-2 whitespace-nowrap",
152
+ children: /* @__PURE__ */ jsxDEV("div", {
153
+ className: "border-table-border inline-flex overflow-hidden rounded-md border",
154
+ children: [
155
+ /* @__PURE__ */ jsxDEV("button", {
156
+ type: "button",
157
+ "data-edit-id": String(row[pk] ?? ""),
158
+ className: "text-link hover:bg-table-row-hover px-2 py-1 text-sm font-semibold",
159
+ children: "Edit"
160
+ }, undefined, false, undefined, this),
161
+ /* @__PURE__ */ jsxDEV("button", {
162
+ type: "button",
163
+ "data-delete-id": String(row[pk] ?? ""),
164
+ className: "border-table-border text-destructive hover:bg-table-row-hover border-l px-2 py-1 text-sm font-semibold",
165
+ children: "Delete"
166
+ }, undefined, false, undefined, this)
167
+ ]
168
+ }, undefined, true, undefined, this)
169
+ }, undefined, false, undefined, this) : null
170
+ ]
171
+ }, i, true, undefined, this))
172
+ }, undefined, false, undefined, this)
173
+ ]
174
+ }, undefined, true, undefined, this)
175
+ }, undefined, false, undefined, this);
176
+ }
177
+
178
+ // src/react/page/components/Masthead.tsx
179
+ import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
180
+ function Masthead({ title, group, resource, total }) {
181
+ const today = new Date().toLocaleDateString("en-US", {
182
+ weekday: "long",
183
+ day: "numeric",
184
+ month: "long",
185
+ year: "numeric"
186
+ });
187
+ return /* @__PURE__ */ jsxDEV2("header", {
188
+ className: "bg-header text-header-foreground",
189
+ children: [
190
+ /* @__PURE__ */ jsxDEV2("div", {
191
+ className: "flex items-center justify-between gap-4 py-2 text-xs tracking-widest text-text-muted uppercase",
192
+ children: [
193
+ /* @__PURE__ */ jsxDEV2("span", {
194
+ children: today
195
+ }, undefined, false, undefined, this),
196
+ /* @__PURE__ */ jsxDEV2("span", {
197
+ className: "hidden sm:inline",
198
+ children: "Daily Edition"
199
+ }, undefined, false, undefined, this),
200
+ /* @__PURE__ */ jsxDEV2("button", {
201
+ id: "koran-theme-toggle",
202
+ type: "button",
203
+ className: "border-border hover:bg-navigation-hover rounded-full border px-3 py-1 tracking-widest uppercase",
204
+ children: "◐ Theme"
205
+ }, undefined, false, undefined, this)
206
+ ]
207
+ }, undefined, true, undefined, this),
208
+ /* @__PURE__ */ jsxDEV2("h1", {
209
+ className: "font-koran py-2 text-center text-5xl font-black tracking-tight",
210
+ children: title
211
+ }, undefined, false, undefined, this),
212
+ /* @__PURE__ */ jsxDEV2("div", {
213
+ className: "flex items-center justify-center gap-2 pb-3 text-sm text-text-muted",
214
+ children: [
215
+ group ? /* @__PURE__ */ jsxDEV2("span", {
216
+ className: "uppercase",
217
+ children: group
218
+ }, undefined, false, undefined, this) : null,
219
+ group && (resource || total !== undefined) ? /* @__PURE__ */ jsxDEV2("span", {
220
+ "aria-hidden": "true",
221
+ children: "·"
222
+ }, undefined, false, undefined, this) : null,
223
+ resource ? /* @__PURE__ */ jsxDEV2("span", {
224
+ className: "font-mono text-xs",
225
+ children: [
226
+ "/",
227
+ resource
228
+ ]
229
+ }, undefined, true, undefined, this) : null,
230
+ total !== undefined ? /* @__PURE__ */ jsxDEV2("span", {
231
+ children: [
232
+ total,
233
+ " records"
234
+ ]
235
+ }, undefined, true, undefined, this) : null
236
+ ]
237
+ }, undefined, true, undefined, this),
238
+ /* @__PURE__ */ jsxDEV2("div", {
239
+ className: "koran-rule-double"
240
+ }, undefined, false, undefined, this)
241
+ ]
242
+ }, undefined, true, undefined, this);
243
+ }
244
+
245
+ // src/react/page/components/Pagination.tsx
246
+ import { jsxDEV as jsxDEV3 } from "react/jsx-dev-runtime";
247
+ var pageClass = "border-border rounded-md border px-3 py-1 text-sm hover:bg-navigation-hover";
248
+ var currentClass = "bg-primary text-primary-foreground rounded-md px-3 py-1 text-sm font-bold";
249
+ var disabledClass = "border-border text-text-disabled rounded-md border px-3 py-1 text-sm";
250
+ function Pagination({ page, totalPages, total, limit, params }) {
251
+ const from = total === 0 ? 0 : (page - 1) * limit + 1;
252
+ const to = Math.min(page * limit, total);
253
+ return /* @__PURE__ */ jsxDEV3("nav", {
254
+ className: "flex flex-wrap items-center justify-between gap-3 py-4 text-sm",
255
+ children: [
256
+ /* @__PURE__ */ jsxDEV3("p", {
257
+ className: "text-text-muted",
258
+ "data-panel-count": true,
259
+ children: [
260
+ "Page ",
261
+ page,
262
+ " of ",
263
+ totalPages,
264
+ " — ",
265
+ from,
266
+ "–",
267
+ to,
268
+ " of ",
269
+ total,
270
+ " records"
271
+ ]
272
+ }, undefined, true, undefined, this),
273
+ /* @__PURE__ */ jsxDEV3("div", {
274
+ className: "flex items-center gap-1",
275
+ "data-panel-pages": true,
276
+ children: [
277
+ page > 1 ? /* @__PURE__ */ jsxDEV3("a", {
278
+ className: pageClass,
279
+ "data-qlink": true,
280
+ href: href(params, { page: page - 1 }),
281
+ children: "← Previous"
282
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV3("span", {
283
+ className: disabledClass,
284
+ children: "← Previous"
285
+ }, undefined, false, undefined, this),
286
+ pageWindow(page, totalPages).map((p, i) => p === "…" ? /* @__PURE__ */ jsxDEV3("span", {
287
+ className: "text-text-muted px-1",
288
+ children: "…"
289
+ }, `e${i}`, false, undefined, this) : p === page ? /* @__PURE__ */ jsxDEV3("span", {
290
+ className: currentClass,
291
+ children: p
292
+ }, p, false, undefined, this) : /* @__PURE__ */ jsxDEV3("a", {
293
+ className: pageClass,
294
+ "data-qlink": true,
295
+ href: href(params, { page: p }),
296
+ children: p
297
+ }, p, false, undefined, this)),
298
+ page < totalPages ? /* @__PURE__ */ jsxDEV3("a", {
299
+ className: pageClass,
300
+ "data-qlink": true,
301
+ href: href(params, { page: page + 1 }),
302
+ children: "Next →"
303
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV3("span", {
304
+ className: disabledClass,
305
+ children: "Next →"
306
+ }, undefined, false, undefined, this)
307
+ ]
308
+ }, undefined, true, undefined, this)
309
+ ]
310
+ }, undefined, true, undefined, this);
311
+ }
312
+
313
+ // src/react/page/components/Toolbar.tsx
314
+ import { jsxDEV as jsxDEV4, Fragment } from "react/jsx-dev-runtime";
315
+ var inputClass = "bg-input-background border-input text-foreground placeholder:text-form-placeholder rounded-md border px-3 py-1.5 text-sm";
316
+ var labelClass = "flex flex-col gap-1 text-xs font-semibold tracking-wider text-form-label uppercase";
317
+ function Toolbar({ searchable, filterable, sortable, params }) {
318
+ if (!searchable?.length && !filterable?.length && !sortable?.length)
319
+ return null;
320
+ return /* @__PURE__ */ jsxDEV4("form", {
321
+ method: "get",
322
+ action: "",
323
+ className: "flex flex-wrap items-end gap-3 py-4",
324
+ children: [
325
+ searchable && searchable.length > 0 ? /* @__PURE__ */ jsxDEV4("label", {
326
+ className: labelClass,
327
+ children: [
328
+ "Search",
329
+ /* @__PURE__ */ jsxDEV4("input", {
330
+ name: "search",
331
+ defaultValue: String(params.search ?? ""),
332
+ placeholder: searchable.join(", "),
333
+ className: inputClass
334
+ }, undefined, false, undefined, this)
335
+ ]
336
+ }, undefined, true, undefined, this) : null,
337
+ (filterable ?? []).map((field) => /* @__PURE__ */ jsxDEV4("label", {
338
+ className: labelClass,
339
+ children: [
340
+ field,
341
+ /* @__PURE__ */ jsxDEV4("input", {
342
+ name: field,
343
+ defaultValue: String(params[field] ?? ""),
344
+ className: inputClass
345
+ }, undefined, false, undefined, this)
346
+ ]
347
+ }, field, true, undefined, this)),
348
+ sortable && sortable.length > 0 ? /* @__PURE__ */ jsxDEV4(Fragment, {
349
+ children: [
350
+ /* @__PURE__ */ jsxDEV4("label", {
351
+ className: labelClass,
352
+ children: [
353
+ "Sort",
354
+ /* @__PURE__ */ jsxDEV4("select", {
355
+ name: "sort",
356
+ defaultValue: String(params.sort ?? ""),
357
+ className: inputClass,
358
+ children: [
359
+ /* @__PURE__ */ jsxDEV4("option", {
360
+ value: "",
361
+ children: "—"
362
+ }, undefined, false, undefined, this),
363
+ sortable.map((field) => /* @__PURE__ */ jsxDEV4("option", {
364
+ value: field,
365
+ children: field
366
+ }, field, false, undefined, this))
367
+ ]
368
+ }, undefined, true, undefined, this)
369
+ ]
370
+ }, undefined, true, undefined, this),
371
+ /* @__PURE__ */ jsxDEV4("label", {
372
+ className: labelClass,
373
+ children: [
374
+ "Order",
375
+ /* @__PURE__ */ jsxDEV4("select", {
376
+ name: "order",
377
+ defaultValue: String(params.order ?? ""),
378
+ className: inputClass,
379
+ children: [
380
+ /* @__PURE__ */ jsxDEV4("option", {
381
+ value: "asc",
382
+ children: "Ascending"
383
+ }, undefined, false, undefined, this),
384
+ /* @__PURE__ */ jsxDEV4("option", {
385
+ value: "desc",
386
+ children: "Descending"
387
+ }, undefined, false, undefined, this)
388
+ ]
389
+ }, undefined, true, undefined, this)
390
+ ]
391
+ }, undefined, true, undefined, this)
392
+ ]
393
+ }, undefined, true, undefined, this) : null,
394
+ /* @__PURE__ */ jsxDEV4("button", {
395
+ type: "submit",
396
+ className: "bg-primary text-primary-foreground rounded-md px-4 py-1.5 text-sm font-semibold",
397
+ children: "Apply"
398
+ }, undefined, false, undefined, this)
399
+ ]
400
+ }, undefined, true, undefined, this);
401
+ }
402
+
403
+ // src/react/page/Panel.tsx
404
+ import { jsxDEV as jsxDEV5 } from "react/jsx-dev-runtime";
405
+ var inputClass2 = "bg-input-background border-input text-foreground rounded-md border px-3 py-1.5 text-sm font-normal normal-case";
406
+ var fieldLabelClass = "flex flex-col gap-1 text-xs font-semibold tracking-wider text-form-label uppercase";
407
+ function FieldInput({ field, required }) {
408
+ switch (field.kind) {
409
+ case "enum":
410
+ return /* @__PURE__ */ jsxDEV5("label", {
411
+ className: fieldLabelClass,
412
+ children: [
413
+ field.name,
414
+ /* @__PURE__ */ jsxDEV5("select", {
415
+ name: field.name,
416
+ required,
417
+ defaultValue: "",
418
+ className: inputClass2,
419
+ children: [
420
+ /* @__PURE__ */ jsxDEV5("option", {
421
+ value: "",
422
+ children: "—"
423
+ }, undefined, false, undefined, this),
424
+ (field.enumValues ?? []).map((v) => /* @__PURE__ */ jsxDEV5("option", {
425
+ value: v,
426
+ children: v
427
+ }, v, false, undefined, this))
428
+ ]
429
+ }, undefined, true, undefined, this)
430
+ ]
431
+ }, undefined, true, undefined, this);
432
+ case "boolean":
433
+ return /* @__PURE__ */ jsxDEV5("label", {
434
+ className: fieldLabelClass,
435
+ children: [
436
+ field.name,
437
+ /* @__PURE__ */ jsxDEV5("input", {
438
+ type: "checkbox",
439
+ name: field.name,
440
+ value: "true",
441
+ className: "h-4 w-4"
442
+ }, undefined, false, undefined, this)
443
+ ]
444
+ }, undefined, true, undefined, this);
445
+ case "integer":
446
+ case "number":
447
+ return /* @__PURE__ */ jsxDEV5("label", {
448
+ className: fieldLabelClass,
449
+ children: [
450
+ field.name,
451
+ /* @__PURE__ */ jsxDEV5("input", {
452
+ type: "number",
453
+ step: field.kind === "number" ? "any" : "1",
454
+ name: field.name,
455
+ required,
456
+ className: inputClass2
457
+ }, undefined, false, undefined, this)
458
+ ]
459
+ }, undefined, true, undefined, this);
460
+ case "date":
461
+ return /* @__PURE__ */ jsxDEV5("label", {
462
+ className: fieldLabelClass,
463
+ children: [
464
+ field.name,
465
+ /* @__PURE__ */ jsxDEV5("input", {
466
+ type: "datetime-local",
467
+ name: field.name,
468
+ required,
469
+ className: inputClass2
470
+ }, undefined, false, undefined, this)
471
+ ]
472
+ }, undefined, true, undefined, this);
473
+ case "json":
474
+ return /* @__PURE__ */ jsxDEV5("label", {
475
+ className: fieldLabelClass,
476
+ children: [
477
+ field.name,
478
+ /* @__PURE__ */ jsxDEV5("textarea", {
479
+ name: field.name,
480
+ "data-json": true,
481
+ rows: 3,
482
+ placeholder: "{}",
483
+ className: `${inputClass2} font-mono`
484
+ }, undefined, false, undefined, this)
485
+ ]
486
+ }, undefined, true, undefined, this);
487
+ default:
488
+ return /* @__PURE__ */ jsxDEV5("label", {
489
+ className: fieldLabelClass,
490
+ children: [
491
+ field.name,
492
+ /* @__PURE__ */ jsxDEV5("input", {
493
+ name: field.name,
494
+ required,
495
+ className: inputClass2
496
+ }, undefined, false, undefined, this)
497
+ ]
498
+ }, undefined, true, undefined, this);
499
+ }
500
+ }
501
+ async function Panel(props) {
502
+ const params = props.params ?? {};
503
+ const page = Math.max(Number(params.page ?? 1) || 1, 1);
504
+ const limit = Math.max(Number(params.limit ?? props.query?.pagination?.default ?? 20) || 1, 1);
505
+ let rows = [];
506
+ let total = 0;
507
+ if (props.load) {
508
+ const result = await props.load(params);
509
+ rows = result.data;
510
+ total = result.total;
511
+ }
512
+ const columns = rows.length > 0 ? Object.keys(rows[0] ?? {}).filter((c) => c !== "deletedAt") : [];
513
+ const title = props.metadata?.pluralLabel ?? props.metadata?.label ?? props.resource ?? "Panel";
514
+ const totalPages = Math.max(1, Math.ceil(total / limit));
515
+ const pk = props.primaryKey ?? "id";
516
+ const deletedAt = props.deletedAtField ?? "deletedAt";
517
+ const fieldDefs = props.fields?.length ? props.fields.filter((f) => !f.autoIncrement && f.name !== deletedAt).map((f) => ({
518
+ name: f.name,
519
+ kind: f.kind,
520
+ nullable: f.nullable,
521
+ enumValues: f.enumValues
522
+ })) : columns.filter((c) => c !== deletedAt).map((name) => ({
523
+ name,
524
+ kind: "text",
525
+ nullable: name === pk
526
+ }));
527
+ const createFields = fieldDefs;
528
+ const editFields = fieldDefs.filter((f) => f.name !== pk);
529
+ const isRequired = (f) => !f.nullable && f.kind !== "boolean";
530
+ const canCreate = props.operations?.create !== false;
531
+ const canEdit = props.operations?.update !== false || props.operations?.replace !== false;
532
+ const canDelete = props.operations?.delete !== false;
533
+ const selectable = canEdit || canDelete;
534
+ return /* @__PURE__ */ jsxDEV5("div", {
535
+ className: "koran-paper font-koran-body text-foreground min-h-screen",
536
+ children: /* @__PURE__ */ jsxDEV5("div", {
537
+ className: "mx-auto max-w-5xl px-4 pb-10 sm:px-6",
538
+ children: [
539
+ /* @__PURE__ */ jsxDEV5(Masthead, {
540
+ title,
541
+ group: props.metadata?.group,
542
+ resource: props.resource,
543
+ total
544
+ }, undefined, false, undefined, this),
545
+ /* @__PURE__ */ jsxDEV5("div", {
546
+ "data-query-url": props.queryUrl,
547
+ children: [
548
+ /* @__PURE__ */ jsxDEV5(Toolbar, {
549
+ searchable: props.query?.searchable,
550
+ filterable: props.query?.filterable,
551
+ sortable: props.query?.sortable,
552
+ params
553
+ }, undefined, false, undefined, this),
554
+ /* @__PURE__ */ jsxDEV5("div", {
555
+ className: "flex items-center justify-between gap-3 pb-3",
556
+ children: [
557
+ /* @__PURE__ */ jsxDEV5("div", {
558
+ "data-bulk-bar": true,
559
+ style: { display: "none" },
560
+ className: "items-center gap-2",
561
+ children: [
562
+ /* @__PURE__ */ jsxDEV5("span", {
563
+ "data-bulk-count": true,
564
+ className: "text-sm text-text-muted"
565
+ }, undefined, false, undefined, this),
566
+ canDelete ? /* @__PURE__ */ jsxDEV5("button", {
567
+ type: "button",
568
+ "data-bulk-delete": true,
569
+ className: "bg-destructive text-destructive-foreground rounded-md px-3 py-1.5 text-sm font-semibold",
570
+ children: "Delete selected"
571
+ }, undefined, false, undefined, this) : null,
572
+ canEdit ? /* @__PURE__ */ jsxDEV5("span", {
573
+ className: "inline-flex items-center gap-2",
574
+ children: [
575
+ /* @__PURE__ */ jsxDEV5("select", {
576
+ "data-bulk-field": true,
577
+ "aria-label": "Bulk field",
578
+ className: "bg-input-background border-input rounded-md border px-2 py-1.5 text-sm",
579
+ children: editFields.map((f) => /* @__PURE__ */ jsxDEV5("option", {
580
+ value: f.name,
581
+ children: f.name
582
+ }, f.name, false, undefined, this))
583
+ }, undefined, false, undefined, this),
584
+ /* @__PURE__ */ jsxDEV5("input", {
585
+ "data-bulk-value": true,
586
+ placeholder: "New value",
587
+ "aria-label": "Bulk value",
588
+ className: "bg-input-background border-input placeholder:text-form-placeholder rounded-md border px-2 py-1.5 text-sm"
589
+ }, undefined, false, undefined, this),
590
+ /* @__PURE__ */ jsxDEV5("button", {
591
+ type: "button",
592
+ "data-bulk-apply": true,
593
+ className: "bg-secondary text-secondary-foreground rounded-md px-3 py-1.5 text-sm font-semibold",
594
+ children: "Apply"
595
+ }, undefined, false, undefined, this)
596
+ ]
597
+ }, undefined, true, undefined, this) : null,
598
+ /* @__PURE__ */ jsxDEV5("button", {
599
+ type: "button",
600
+ "data-bulk-clear": true,
601
+ className: "text-text-muted text-sm underline",
602
+ children: "Clear"
603
+ }, undefined, false, undefined, this)
604
+ ]
605
+ }, undefined, true, undefined, this),
606
+ canCreate ? /* @__PURE__ */ jsxDEV5("button", {
607
+ type: "button",
608
+ "data-open-create": true,
609
+ className: "bg-primary text-primary-foreground ml-auto rounded-md px-4 py-1.5 text-sm font-semibold",
610
+ children: "+ New"
611
+ }, undefined, false, undefined, this) : null
612
+ ]
613
+ }, undefined, true, undefined, this),
614
+ /* @__PURE__ */ jsxDEV5(DataTable, {
615
+ columns,
616
+ rows,
617
+ sortable: props.query?.sortable,
618
+ params,
619
+ selectable,
620
+ editable: canEdit || canDelete,
621
+ primaryKey: pk
622
+ }, undefined, false, undefined, this),
623
+ /* @__PURE__ */ jsxDEV5(Pagination, {
624
+ page,
625
+ totalPages,
626
+ total,
627
+ limit,
628
+ params
629
+ }, undefined, false, undefined, this),
630
+ /* @__PURE__ */ jsxDEV5("dialog", {
631
+ id: "rack-create",
632
+ className: "bg-card text-card-foreground rounded-lg border p-0",
633
+ children: /* @__PURE__ */ jsxDEV5("form", {
634
+ "data-create-form": true,
635
+ className: "flex min-w-80 flex-col gap-3 p-6",
636
+ children: [
637
+ /* @__PURE__ */ jsxDEV5("h2", {
638
+ className: "font-koran text-2xl font-bold",
639
+ children: "New record"
640
+ }, undefined, false, undefined, this),
641
+ createFields.length > 0 ? createFields.map((f) => /* @__PURE__ */ jsxDEV5(FieldInput, {
642
+ field: f,
643
+ required: isRequired(f)
644
+ }, f.name, false, undefined, this)) : /* @__PURE__ */ jsxDEV5("label", {
645
+ className: "flex flex-col gap-1 text-xs font-semibold tracking-wider text-form-label uppercase",
646
+ children: [
647
+ "JSON body",
648
+ /* @__PURE__ */ jsxDEV5("textarea", {
649
+ "data-create-json": true,
650
+ rows: 4,
651
+ placeholder: '{"name": "..."}',
652
+ className: "bg-input-background border-input text-foreground rounded-md border px-3 py-1.5 font-mono text-sm font-normal normal-case"
653
+ }, undefined, false, undefined, this)
654
+ ]
655
+ }, undefined, true, undefined, this),
656
+ /* @__PURE__ */ jsxDEV5("p", {
657
+ "data-form-error": true,
658
+ className: "text-form-error text-sm"
659
+ }, undefined, false, undefined, this),
660
+ /* @__PURE__ */ jsxDEV5("div", {
661
+ className: "flex justify-end gap-2",
662
+ children: [
663
+ /* @__PURE__ */ jsxDEV5("button", {
664
+ type: "button",
665
+ "data-close": true,
666
+ className: "border-border rounded-md border px-4 py-1.5 text-sm",
667
+ children: "Cancel"
668
+ }, undefined, false, undefined, this),
669
+ /* @__PURE__ */ jsxDEV5("button", {
670
+ type: "submit",
671
+ className: "bg-primary text-primary-foreground rounded-md px-4 py-1.5 text-sm font-semibold",
672
+ children: "Create"
673
+ }, undefined, false, undefined, this)
674
+ ]
675
+ }, undefined, true, undefined, this)
676
+ ]
677
+ }, undefined, true, undefined, this)
678
+ }, undefined, false, undefined, this),
679
+ /* @__PURE__ */ jsxDEV5("dialog", {
680
+ id: "rack-edit",
681
+ className: "bg-card text-card-foreground rounded-lg border p-0",
682
+ children: /* @__PURE__ */ jsxDEV5("form", {
683
+ "data-edit-form": true,
684
+ className: "flex min-w-80 flex-col gap-3 p-6",
685
+ children: [
686
+ /* @__PURE__ */ jsxDEV5("h2", {
687
+ className: "font-koran text-2xl font-bold",
688
+ children: [
689
+ "Edit record ",
690
+ /* @__PURE__ */ jsxDEV5("span", {
691
+ "data-edit-id-label": true,
692
+ className: "text-text-muted text-lg"
693
+ }, undefined, false, undefined, this)
694
+ ]
695
+ }, undefined, true, undefined, this),
696
+ editFields.map((f) => /* @__PURE__ */ jsxDEV5(FieldInput, {
697
+ field: f
698
+ }, f.name, false, undefined, this)),
699
+ /* @__PURE__ */ jsxDEV5("p", {
700
+ "data-form-error": true,
701
+ className: "text-form-error text-sm"
702
+ }, undefined, false, undefined, this),
703
+ /* @__PURE__ */ jsxDEV5("div", {
704
+ className: "flex justify-end gap-2",
705
+ children: [
706
+ /* @__PURE__ */ jsxDEV5("button", {
707
+ type: "button",
708
+ "data-close": true,
709
+ className: "border-border rounded-md border px-4 py-1.5 text-sm",
710
+ children: "Cancel"
711
+ }, undefined, false, undefined, this),
712
+ /* @__PURE__ */ jsxDEV5("button", {
713
+ type: "submit",
714
+ className: "bg-primary text-primary-foreground rounded-md px-4 py-1.5 text-sm font-semibold",
715
+ children: "Save"
716
+ }, undefined, false, undefined, this)
717
+ ]
718
+ }, undefined, true, undefined, this)
719
+ ]
720
+ }, undefined, true, undefined, this)
721
+ }, undefined, false, undefined, this),
722
+ /* @__PURE__ */ jsxDEV5("script", {
723
+ type: "module",
724
+ src: "/__rack/panel-app.js"
725
+ }, undefined, false, undefined, this)
726
+ ]
727
+ }, undefined, true, undefined, this),
728
+ /* @__PURE__ */ jsxDEV5("footer", {
729
+ className: "border-border text-text-muted border-t pt-3 text-center text-xs tracking-widest uppercase",
730
+ children: "Printed by elysia-rack"
731
+ }, undefined, false, undefined, this)
732
+ ]
733
+ }, undefined, true, undefined, this)
734
+ }, undefined, false, undefined, this);
735
+ }
736
+ export {
737
+ Panel as default
738
+ };