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.
- package/LICENSE +21 -0
- package/README.md +241 -0
- package/dist/Dashboard-xpstr7fr.js +172 -0
- package/dist/Panel-fbp3wrsw.js +738 -0
- package/dist/index-37x76zdn.js +4 -0
- package/dist/index-zc7rgvmm.js +13 -0
- package/dist/panel-app.js +1 -0
- package/dist/panel-theme.js +1 -0
- package/dist/panel.css +1 -0
- package/dist/rack/adapter.d.ts +5 -0
- package/dist/rack/adapters/drizzle.d.ts +2 -0
- package/dist/rack/adapters/index.d.ts +59 -0
- package/dist/rack/adapters/prisma.d.ts +2 -0
- package/dist/rack/authorization.d.ts +23 -0
- package/dist/rack/idempotency.d.ts +9 -0
- package/dist/rack/index.d.ts +40 -0
- package/dist/rack/index.js +1362 -0
- package/dist/rack/models/drizzle.d.ts +4 -0
- package/dist/rack/models/index.d.ts +24 -0
- package/dist/rack/models/prisma.d.ts +4 -0
- package/dist/rack/openapi.d.ts +6 -0
- package/dist/rack/page.d.ts +33 -0
- package/dist/rack/query.d.ts +22 -0
- package/dist/rack/registry.d.ts +20 -0
- package/dist/rack/types/authorization.d.ts +16 -0
- package/dist/rack/types/idempotency.d.ts +46 -0
- package/dist/rack/types/index.d.ts +89 -0
- package/dist/rack/types/metadata.d.ts +36 -0
- package/dist/rack/types/openapi.d.ts +22 -0
- package/dist/rack/types/operations.d.ts +14 -0
- package/dist/rack/types/page.d.ts +41 -0
- package/dist/rack/types/query.d.ts +30 -0
- package/dist/rack/types/validation.d.ts +19 -0
- package/dist/react/index.d.ts +64 -0
- package/dist/react/index.js +126 -0
- package/dist/react/page/Dashboard.d.ts +7 -0
- package/dist/react/page/Panel.d.ts +36 -0
- package/dist/react/page/client/panel.d.ts +1 -0
- package/dist/react/page/client/theme.d.ts +1 -0
- package/dist/react/page/components/Breadcrumb.d.ts +7 -0
- package/dist/react/page/components/DataTable.d.ts +10 -0
- package/dist/react/page/components/Masthead.d.ts +7 -0
- package/dist/react/page/components/Pagination.d.ts +10 -0
- package/dist/react/page/components/Sidebar.d.ts +16 -0
- package/dist/react/page/components/Toolbar.d.ts +7 -0
- package/dist/react/page/components/href.d.ts +10 -0
- package/dist/react/page/components/panelForm.d.ts +9 -0
- package/dist/react/render.d.ts +2 -0
- package/dist/react/types.d.ts +16 -0
- 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
|
+
};
|