@maykonpaulo/maestro-admin 0.3.0 → 0.4.0-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -12
- package/dist/index.d.ts +281 -3
- package/dist/index.js +1285 -469
- package/dist/index.js.map +1 -1
- package/package.json +2 -3
- package/src/styles.css +1716 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/components/MaestroAdmin.tsx
|
|
2
|
-
import { useMemo as
|
|
2
|
+
import { useCallback as useCallback5, useEffect as useEffect7, useMemo as useMemo7, useState as useState14 } from "react";
|
|
3
3
|
|
|
4
4
|
// src/client/MaestroClient.ts
|
|
5
5
|
var MaestroApiError = class extends Error {
|
|
@@ -110,6 +110,11 @@ function useEntity(entityId) {
|
|
|
110
110
|
const entities = useEntities();
|
|
111
111
|
return entityId ? entities.find((e) => e.id === entityId) : void 0;
|
|
112
112
|
}
|
|
113
|
+
var RootContext = createContext(null);
|
|
114
|
+
var AdminRootProvider = RootContext.Provider;
|
|
115
|
+
function useAdminRoot() {
|
|
116
|
+
return useContext(RootContext);
|
|
117
|
+
}
|
|
113
118
|
|
|
114
119
|
// src/hooks.ts
|
|
115
120
|
import { useCallback, useEffect, useState } from "react";
|
|
@@ -198,9 +203,6 @@ function formFields(entity, mode) {
|
|
|
198
203
|
return entity.fields.filter((f) => mode === "create" ? f.form.creatable : f.form.editable).sort((a, b) => (a.form.order ?? 0) - (b.form.order ?? 0));
|
|
199
204
|
}
|
|
200
205
|
|
|
201
|
-
// src/components/Sidebar.tsx
|
|
202
|
-
import { useEffect as useEffect2, useMemo, useRef, useState as useState2 } from "react";
|
|
203
|
-
|
|
204
206
|
// src/grouping.ts
|
|
205
207
|
var MIN_PREFIX = 4;
|
|
206
208
|
function commonPrefix(a, b) {
|
|
@@ -257,6 +259,9 @@ function buildMenu(entities, labels) {
|
|
|
257
259
|
return { groups, ungrouped };
|
|
258
260
|
}
|
|
259
261
|
|
|
262
|
+
// src/components/Sidebar.tsx
|
|
263
|
+
import { useMemo, useState as useState2 } from "react";
|
|
264
|
+
|
|
260
265
|
// src/i18n.ts
|
|
261
266
|
import { createContext as createContext2, useContext as useContext2 } from "react";
|
|
262
267
|
var EN = {
|
|
@@ -264,7 +269,7 @@ var EN = {
|
|
|
264
269
|
loadingMetadata: "Loading metadata\u2026",
|
|
265
270
|
records: (total) => `${total} record${total === 1 ? "" : "s"}`,
|
|
266
271
|
searchPlaceholder: (plural) => `Search ${plural.toLowerCase()}\u2026`,
|
|
267
|
-
newRecord: "
|
|
272
|
+
newRecord: "New",
|
|
268
273
|
view: "View",
|
|
269
274
|
edit: "Edit",
|
|
270
275
|
clone: "Clone",
|
|
@@ -303,14 +308,42 @@ var EN = {
|
|
|
303
308
|
searchMenu: "Search menu\u2026",
|
|
304
309
|
recents: "Recents",
|
|
305
310
|
menus: "Menus",
|
|
306
|
-
noMenuResults: "No matches"
|
|
311
|
+
noMenuResults: "No matches",
|
|
312
|
+
hub: "Hub",
|
|
313
|
+
breadcrumb: "Breadcrumb",
|
|
314
|
+
search: "Search",
|
|
315
|
+
commandKey: "\u2318K",
|
|
316
|
+
commandPalette: "Command palette",
|
|
317
|
+
palettePlaceholder: "Go to an entity, or run an action\u2026",
|
|
318
|
+
goTo: "Go to",
|
|
319
|
+
onThisScreen: "On this screen",
|
|
320
|
+
appearance: "Appearance",
|
|
321
|
+
palette: "Palette",
|
|
322
|
+
density: "Density",
|
|
323
|
+
colorScheme: "Light / dark",
|
|
324
|
+
schemeSystem: "system",
|
|
325
|
+
schemeLight: "light",
|
|
326
|
+
schemeDark: "dark",
|
|
327
|
+
entities: "Entities",
|
|
328
|
+
hubMeta: (entities, sources) => `${entities} ${entities === 1 ? "entity" : "entities"} across ${sources} ${sources === 1 ? "datasource" : "datasources"}.`,
|
|
329
|
+
otherEntities: "Other",
|
|
330
|
+
noEntities: "No entities",
|
|
331
|
+
noEntitiesHint: "The server served an empty metadata document.",
|
|
332
|
+
fieldCount: (count) => `${count} field${count === 1 ? "" : "s"}`,
|
|
333
|
+
capRead: "read",
|
|
334
|
+
capCreate: "create",
|
|
335
|
+
capUpdate: "update",
|
|
336
|
+
capDelete: "delete",
|
|
337
|
+
columns: "Columns",
|
|
338
|
+
columnsCount: (visible, total) => `${visible}/${total}`,
|
|
339
|
+
resetColumns: "Reset columns"
|
|
307
340
|
};
|
|
308
341
|
var PT_BR = {
|
|
309
342
|
loading: "Carregando\u2026",
|
|
310
343
|
loadingMetadata: "Carregando metadados\u2026",
|
|
311
344
|
records: (total) => `${total} registro${total === 1 ? "" : "s"}`,
|
|
312
345
|
searchPlaceholder: (plural) => `Buscar ${plural.toLowerCase()}\u2026`,
|
|
313
|
-
newRecord: "
|
|
346
|
+
newRecord: "Novo",
|
|
314
347
|
view: "Visualizar",
|
|
315
348
|
edit: "Editar",
|
|
316
349
|
clone: "Clonar",
|
|
@@ -349,7 +382,35 @@ var PT_BR = {
|
|
|
349
382
|
searchMenu: "Buscar no menu\u2026",
|
|
350
383
|
recents: "Recentes",
|
|
351
384
|
menus: "Menus",
|
|
352
|
-
noMenuResults: "Nada encontrado"
|
|
385
|
+
noMenuResults: "Nada encontrado",
|
|
386
|
+
hub: "Hub",
|
|
387
|
+
breadcrumb: "Trilha",
|
|
388
|
+
search: "Buscar",
|
|
389
|
+
commandKey: "\u2318K",
|
|
390
|
+
commandPalette: "Paleta de comandos",
|
|
391
|
+
palettePlaceholder: "Ir para uma entidade, ou executar uma a\xE7\xE3o\u2026",
|
|
392
|
+
goTo: "Ir para",
|
|
393
|
+
onThisScreen: "Nesta tela",
|
|
394
|
+
appearance: "Apar\xEAncia",
|
|
395
|
+
palette: "Paleta",
|
|
396
|
+
density: "Densidade",
|
|
397
|
+
colorScheme: "Claro / escuro",
|
|
398
|
+
schemeSystem: "sistema",
|
|
399
|
+
schemeLight: "claro",
|
|
400
|
+
schemeDark: "escuro",
|
|
401
|
+
entities: "Entidades",
|
|
402
|
+
hubMeta: (entities, sources) => `${entities} ${entities === 1 ? "entidade" : "entidades"} em ${sources} ${sources === 1 ? "origem de dados" : "origens de dados"}.`,
|
|
403
|
+
otherEntities: "Outras",
|
|
404
|
+
noEntities: "Nenhuma entidade",
|
|
405
|
+
noEntitiesHint: "O servidor entregou um documento de metadados vazio.",
|
|
406
|
+
fieldCount: (count) => `${count} campo${count === 1 ? "" : "s"}`,
|
|
407
|
+
capRead: "ler",
|
|
408
|
+
capCreate: "criar",
|
|
409
|
+
capUpdate: "editar",
|
|
410
|
+
capDelete: "excluir",
|
|
411
|
+
columns: "Colunas",
|
|
412
|
+
columnsCount: (visible, total) => `${visible}/${total}`,
|
|
413
|
+
resetColumns: "Restaurar colunas"
|
|
353
414
|
};
|
|
354
415
|
var LOCALES = { en: EN, "pt-BR": PT_BR };
|
|
355
416
|
function stringsFor(locale) {
|
|
@@ -361,8 +422,179 @@ function useT() {
|
|
|
361
422
|
return useContext2(I18nContext);
|
|
362
423
|
}
|
|
363
424
|
|
|
364
|
-
// src/components/
|
|
425
|
+
// src/components/icons.tsx
|
|
365
426
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
427
|
+
var PATHS = {
|
|
428
|
+
hub: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
429
|
+
/* @__PURE__ */ jsx("rect", { x: "3.5", y: "3.5", width: "7", height: "7", rx: "1.5" }),
|
|
430
|
+
/* @__PURE__ */ jsx("rect", { x: "13.5", y: "3.5", width: "7", height: "7", rx: "1.5" }),
|
|
431
|
+
/* @__PURE__ */ jsx("rect", { x: "3.5", y: "13.5", width: "7", height: "7", rx: "1.5" }),
|
|
432
|
+
/* @__PURE__ */ jsx("rect", { x: "13.5", y: "13.5", width: "7", height: "7", rx: "1.5" })
|
|
433
|
+
] }),
|
|
434
|
+
baton: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
435
|
+
/* @__PURE__ */ jsx("path", { d: "M4 20 14 10" }),
|
|
436
|
+
/* @__PURE__ */ jsx("circle", { cx: "17", cy: "7", r: "3.2" })
|
|
437
|
+
] }),
|
|
438
|
+
search: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
439
|
+
/* @__PURE__ */ jsx("circle", { cx: "11", cy: "11", r: "6.5" }),
|
|
440
|
+
/* @__PURE__ */ jsx("path", { d: "m16 16 4.5 4.5" })
|
|
441
|
+
] }),
|
|
442
|
+
chevronRight: /* @__PURE__ */ jsx("path", { d: "m9 5 7 7-7 7" }),
|
|
443
|
+
chevronLeft: /* @__PURE__ */ jsx("path", { d: "M15 5 8 12l7 7" }),
|
|
444
|
+
chevronsLeft: /* @__PURE__ */ jsx("path", { d: "M17 5l-7 7 7 7M10 5l-7 7 7 7" }),
|
|
445
|
+
chevronsRight: /* @__PURE__ */ jsx("path", { d: "m7 5 7 7-7 7M14 5l7 7-7 7" }),
|
|
446
|
+
plus: /* @__PURE__ */ jsx("path", { d: "M12 5v14M5 12h14" }),
|
|
447
|
+
close: /* @__PURE__ */ jsx("path", { d: "M6 6l12 12M18 6 6 18" }),
|
|
448
|
+
check: /* @__PURE__ */ jsx("path", { d: "m4.5 12.5 5 5 10-11" }),
|
|
449
|
+
filter: /* @__PURE__ */ jsx("path", { d: "M3 5h18l-7 8v6l-4 2v-8L3 5Z" }),
|
|
450
|
+
download: /* @__PURE__ */ jsx("path", { d: "M12 3v12M7.5 10.5 12 15l4.5-4.5M4 20h16" }),
|
|
451
|
+
columns: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
452
|
+
/* @__PURE__ */ jsx("rect", { x: "3", y: "4", width: "18", height: "16", rx: "2" }),
|
|
453
|
+
/* @__PURE__ */ jsx("path", { d: "M9.5 4v16M15 4v16" })
|
|
454
|
+
] }),
|
|
455
|
+
rows: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
456
|
+
/* @__PURE__ */ jsx("rect", { x: "3", y: "4", width: "18", height: "16", rx: "2" }),
|
|
457
|
+
/* @__PURE__ */ jsx("path", { d: "M3 9.3h18M3 14.7h18" })
|
|
458
|
+
] }),
|
|
459
|
+
sun: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
460
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "4.2" }),
|
|
461
|
+
/* @__PURE__ */ jsx("path", { d: "M12 2.5v2.2M12 19.3v2.2M4.2 4.2l1.6 1.6M18.2 18.2l1.6 1.6M2.5 12h2.2M19.3 12h2.2M4.2 19.8l1.6-1.6M18.2 5.8l1.6-1.6" })
|
|
462
|
+
] }),
|
|
463
|
+
moon: /* @__PURE__ */ jsx("path", { d: "M20 14.5A8.5 8.5 0 0 1 9.5 4a8.5 8.5 0 1 0 10.5 10.5Z" }),
|
|
464
|
+
swatch: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
465
|
+
/* @__PURE__ */ jsx("circle", { cx: "8.5", cy: "8.5", r: "4.5" }),
|
|
466
|
+
/* @__PURE__ */ jsx("circle", { cx: "15.5", cy: "8.5", r: "4.5" }),
|
|
467
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "15", r: "4.5" })
|
|
468
|
+
] }),
|
|
469
|
+
dots: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
470
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "5", r: "1.4" }),
|
|
471
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "1.4" }),
|
|
472
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "19", r: "1.4" })
|
|
473
|
+
] }),
|
|
474
|
+
trash: /* @__PURE__ */ jsx("path", { d: "M4 7h16M9 7V4.5h6V7M6.5 7l1 13h9l1-13" }),
|
|
475
|
+
pencil: /* @__PURE__ */ jsx("path", { d: "M4 20h4L20 8l-4-4L4 16v4Z" }),
|
|
476
|
+
eye: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
477
|
+
/* @__PURE__ */ jsx("path", { d: "M2.5 12S6 5.5 12 5.5 21.5 12 21.5 12 18 18.5 12 18.5 2.5 12 2.5 12Z" }),
|
|
478
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "3" })
|
|
479
|
+
] }),
|
|
480
|
+
copy: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
481
|
+
/* @__PURE__ */ jsx("rect", { x: "9", y: "9", width: "11", height: "11", rx: "2" }),
|
|
482
|
+
/* @__PURE__ */ jsx("path", { d: "M15 6.5V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h.5" })
|
|
483
|
+
] }),
|
|
484
|
+
clone: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
485
|
+
/* @__PURE__ */ jsx("rect", { x: "4", y: "4", width: "11", height: "11", rx: "2" }),
|
|
486
|
+
/* @__PURE__ */ jsx("path", { d: "M9 20h9a2 2 0 0 0 2-2V9" })
|
|
487
|
+
] }),
|
|
488
|
+
arrowUpRight: /* @__PURE__ */ jsx("path", { d: "M7 17 17 7M9 7h8v8" }),
|
|
489
|
+
arrowLeft: /* @__PURE__ */ jsx("path", { d: "M19 12H6M11 6l-5 6 5 6" }),
|
|
490
|
+
sort: /* @__PURE__ */ jsx("path", { d: "M8 9 12 5l4 4M8 15l4 4 4-4" }),
|
|
491
|
+
sortAsc: /* @__PURE__ */ jsx("path", { d: "M12 19V6M7 11l5-5 5 5" }),
|
|
492
|
+
sortDesc: /* @__PURE__ */ jsx("path", { d: "M12 5v13M7 13l5 5 5-5" }),
|
|
493
|
+
database: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
494
|
+
/* @__PURE__ */ jsx("ellipse", { cx: "12", cy: "6", rx: "8", ry: "3.2" }),
|
|
495
|
+
/* @__PURE__ */ jsx("path", { d: "M4 6v12c0 1.8 3.6 3.2 8 3.2s8-1.4 8-3.2V6M4 12c0 1.8 3.6 3.2 8 3.2s8-1.4 8-3.2" })
|
|
496
|
+
] }),
|
|
497
|
+
table: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
498
|
+
/* @__PURE__ */ jsx("rect", { x: "3", y: "4", width: "18", height: "16", rx: "2" }),
|
|
499
|
+
/* @__PURE__ */ jsx("path", { d: "M3 9.5h18M9 9.5V20" })
|
|
500
|
+
] }),
|
|
501
|
+
users: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
502
|
+
/* @__PURE__ */ jsx("circle", { cx: "9", cy: "8", r: "3.2" }),
|
|
503
|
+
/* @__PURE__ */ jsx("path", { d: "M3 19c0-3 2.7-4.8 6-4.8s6 1.8 6 4.8" }),
|
|
504
|
+
/* @__PURE__ */ jsx("path", { d: "M16 5.5a3 3 0 0 1 0 5.6M17.5 14.6c2 .6 3.5 2.1 3.5 4.4" })
|
|
505
|
+
] }),
|
|
506
|
+
box: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
507
|
+
/* @__PURE__ */ jsx("path", { d: "M12 3 20.5 7.5v9L12 21l-8.5-4.5v-9L12 3Z" }),
|
|
508
|
+
/* @__PURE__ */ jsx("path", { d: "M3.5 7.5 12 12l8.5-4.5M12 12v9" })
|
|
509
|
+
] }),
|
|
510
|
+
receipt: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
511
|
+
/* @__PURE__ */ jsx("path", { d: "M5 3h14v18l-2.3-1.6-2.3 1.6-2.4-1.6L9.6 21l-2.3-1.6L5 21V3Z" }),
|
|
512
|
+
/* @__PURE__ */ jsx("path", { d: "M9 8h6M9 12h6" })
|
|
513
|
+
] }),
|
|
514
|
+
key: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
515
|
+
/* @__PURE__ */ jsx("circle", { cx: "8", cy: "13", r: "4" }),
|
|
516
|
+
/* @__PURE__ */ jsx("path", { d: "m11 11 8-8M17 5l2 2M15 7l2 2" })
|
|
517
|
+
] }),
|
|
518
|
+
shield: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
519
|
+
/* @__PURE__ */ jsx("path", { d: "M12 3 20 6v6c0 4.4-3.3 7.9-8 9-4.7-1.1-8-4.6-8-9V6l8-3Z" }),
|
|
520
|
+
/* @__PURE__ */ jsx("path", { d: "m9 12 2.2 2.2L15.5 10" })
|
|
521
|
+
] }),
|
|
522
|
+
activity: /* @__PURE__ */ jsx("path", { d: "M3 12h4l2.5-6 4 12 2.5-6h5" }),
|
|
523
|
+
tag: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
524
|
+
/* @__PURE__ */ jsx("path", { d: "M3 11.5V4h7.5l10 10-7.5 7.5-10-10Z" }),
|
|
525
|
+
/* @__PURE__ */ jsx("circle", { cx: "7.5", cy: "7.5", r: "1.3" })
|
|
526
|
+
] }),
|
|
527
|
+
map: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
528
|
+
/* @__PURE__ */ jsx("path", { d: "M9 4 3 6.5v13L9 17l6 2.5 6-2.5v-13L15 6.5 9 4Z" }),
|
|
529
|
+
/* @__PURE__ */ jsx("path", { d: "M9 4v13M15 6.5v13" })
|
|
530
|
+
] }),
|
|
531
|
+
calendar: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
532
|
+
/* @__PURE__ */ jsx("rect", { x: "3.5", y: "5", width: "17", height: "15", rx: "2" }),
|
|
533
|
+
/* @__PURE__ */ jsx("path", { d: "M3.5 10h17M8 3v4M16 3v4" })
|
|
534
|
+
] }),
|
|
535
|
+
mail: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
536
|
+
/* @__PURE__ */ jsx("rect", { x: "3", y: "5", width: "18", height: "14", rx: "2" }),
|
|
537
|
+
/* @__PURE__ */ jsx("path", { d: "m3.5 7 8.5 6 8.5-6" })
|
|
538
|
+
] }),
|
|
539
|
+
file: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
540
|
+
/* @__PURE__ */ jsx("path", { d: "M13 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V9l-6-6Z" }),
|
|
541
|
+
/* @__PURE__ */ jsx("path", { d: "M13 3v6h6" })
|
|
542
|
+
] }),
|
|
543
|
+
alert: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
544
|
+
/* @__PURE__ */ jsx("path", { d: "M12 4 2.5 20h19L12 4Z" }),
|
|
545
|
+
/* @__PURE__ */ jsx("path", { d: "M12 10v4.5M12 17.4v.2" })
|
|
546
|
+
] })
|
|
547
|
+
};
|
|
548
|
+
function Icon({
|
|
549
|
+
name,
|
|
550
|
+
size = 15,
|
|
551
|
+
className,
|
|
552
|
+
title
|
|
553
|
+
}) {
|
|
554
|
+
return /* @__PURE__ */ jsxs(
|
|
555
|
+
"svg",
|
|
556
|
+
{
|
|
557
|
+
viewBox: "0 0 24 24",
|
|
558
|
+
width: size,
|
|
559
|
+
height: size,
|
|
560
|
+
className: className ? `mst-icon ${className}` : "mst-icon",
|
|
561
|
+
"aria-hidden": title ? void 0 : true,
|
|
562
|
+
role: title ? "img" : void 0,
|
|
563
|
+
focusable: "false",
|
|
564
|
+
children: [
|
|
565
|
+
title && /* @__PURE__ */ jsx("title", { children: title }),
|
|
566
|
+
PATHS[name]
|
|
567
|
+
]
|
|
568
|
+
}
|
|
569
|
+
);
|
|
570
|
+
}
|
|
571
|
+
var ENTITY_ICON_HINTS = [
|
|
572
|
+
[["user", "usuario", "people", "pessoa", "customer", "cliente", "associado", "member", "contact"], "users"],
|
|
573
|
+
[["invoice", "fatura", "cobranca", "billing", "receipt", "nota"], "receipt"],
|
|
574
|
+
[["payment", "pagamento", "transaction", "transacao", "ledger"], "table"],
|
|
575
|
+
[["product", "produto", "item", "sku", "inventory", "estoque", "catalog"], "box"],
|
|
576
|
+
[["address", "endereco", "location", "local", "region", "city", "cidade"], "map"],
|
|
577
|
+
[["role", "papel", "permission", "permissao", "grant", "token", "credential"], "key"],
|
|
578
|
+
[["audit", "auditoria", "log", "event", "evento", "metric"], "activity"],
|
|
579
|
+
[["policy", "politica", "security", "seguranca", "consent", "consentimento"], "shield"],
|
|
580
|
+
[["tag", "label", "category", "categoria", "plan", "plano", "subscription", "assinatura"], "tag"],
|
|
581
|
+
[["schedule", "agenda", "appointment", "date", "periodo", "calendar"], "calendar"],
|
|
582
|
+
[["message", "mensagem", "email", "notification", "notificacao", "ticket"], "mail"],
|
|
583
|
+
[["document", "documento", "file", "arquivo", "contract", "contrato", "report"], "file"]
|
|
584
|
+
];
|
|
585
|
+
function fold(text) {
|
|
586
|
+
return text.toLowerCase().normalize("NFD").replace(/\p{M}/gu, "");
|
|
587
|
+
}
|
|
588
|
+
function entityIcon(entity) {
|
|
589
|
+
const haystack = fold(`${entity.id} ${entity.table} ${entity.label.singular} ${entity.label.plural}`);
|
|
590
|
+
for (const [words, icon] of ENTITY_ICON_HINTS) {
|
|
591
|
+
if (words.some((word) => haystack.includes(word))) return icon;
|
|
592
|
+
}
|
|
593
|
+
return "table";
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// src/components/Sidebar.tsx
|
|
597
|
+
import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
366
598
|
function normalize(text) {
|
|
367
599
|
return text.toLowerCase().normalize("NFD").replace(/\p{M}/gu, "");
|
|
368
600
|
}
|
|
@@ -381,9 +613,9 @@ function Highlight({ label, query }) {
|
|
|
381
613
|
if (idx === -1) return label;
|
|
382
614
|
const start = originalIndex[idx];
|
|
383
615
|
const end = (originalIndex[idx + foldedQuery.length - 1] ?? label.length - 1) + 1;
|
|
384
|
-
return /* @__PURE__ */
|
|
616
|
+
return /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
385
617
|
label.slice(0, start),
|
|
386
|
-
/* @__PURE__ */
|
|
618
|
+
/* @__PURE__ */ jsx2("mark", { children: label.slice(start, end) }),
|
|
387
619
|
label.slice(end)
|
|
388
620
|
] });
|
|
389
621
|
}
|
|
@@ -394,33 +626,40 @@ function EntityButton({
|
|
|
394
626
|
subtitle,
|
|
395
627
|
query
|
|
396
628
|
}) {
|
|
397
|
-
return /* @__PURE__ */
|
|
629
|
+
return /* @__PURE__ */ jsxs2(
|
|
398
630
|
"button",
|
|
399
631
|
{
|
|
632
|
+
type: "button",
|
|
633
|
+
"aria-current": active,
|
|
400
634
|
onClick: () => onSelect(entity.id),
|
|
401
|
-
className:
|
|
635
|
+
className: "mst-navitem",
|
|
636
|
+
title: entity.label.plural,
|
|
402
637
|
children: [
|
|
403
|
-
/* @__PURE__ */
|
|
404
|
-
|
|
638
|
+
/* @__PURE__ */ jsx2(Icon, { name: entityIcon(entity), size: 14 }),
|
|
639
|
+
/* @__PURE__ */ jsxs2("span", { className: "mst-navitem__label", children: [
|
|
640
|
+
query ? /* @__PURE__ */ jsx2(Highlight, { label: entity.label.plural, query }) : entity.label.plural,
|
|
641
|
+
subtitle && /* @__PURE__ */ jsxs2("span", { className: "mst-navitem__sub", children: [
|
|
642
|
+
" \xB7 ",
|
|
643
|
+
subtitle
|
|
644
|
+
] })
|
|
645
|
+
] })
|
|
405
646
|
]
|
|
406
647
|
}
|
|
407
648
|
);
|
|
408
649
|
}
|
|
409
|
-
function SectionTitle({ children }) {
|
|
410
|
-
return /* @__PURE__ */ jsx("div", { className: "px-3 pb-1 pt-3 text-xs font-semibold uppercase tracking-wide text-slate-400", children });
|
|
411
|
-
}
|
|
412
650
|
function Sidebar({
|
|
413
651
|
entities,
|
|
414
652
|
activeId,
|
|
415
653
|
onSelect,
|
|
416
654
|
title,
|
|
417
655
|
labels,
|
|
418
|
-
recents = []
|
|
656
|
+
recents = [],
|
|
657
|
+
onHome,
|
|
658
|
+
homeActive = false
|
|
419
659
|
}) {
|
|
420
660
|
const t = useT();
|
|
421
661
|
const [query, setQuery] = useState2("");
|
|
422
662
|
const [expanded, setExpanded] = useState2({});
|
|
423
|
-
const searchRef = useRef(null);
|
|
424
663
|
const menu = useMemo(() => buildMenu(entities, labels), [entities, labels]);
|
|
425
664
|
const groupOf = useMemo(() => {
|
|
426
665
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -433,27 +672,19 @@ function Sidebar({
|
|
|
433
672
|
const q = normalize(query);
|
|
434
673
|
return entities.filter((e) => normalize(`${e.label.plural} ${e.label.singular} ${e.table}`).includes(q)).sort((a, b) => a.label.plural.localeCompare(b.label.plural));
|
|
435
674
|
}, [entities, query]);
|
|
436
|
-
useEffect2(() => {
|
|
437
|
-
const onKey = (e) => {
|
|
438
|
-
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "k") {
|
|
439
|
-
e.preventDefault();
|
|
440
|
-
searchRef.current?.focus();
|
|
441
|
-
}
|
|
442
|
-
};
|
|
443
|
-
document.addEventListener("keydown", onKey);
|
|
444
|
-
return () => document.removeEventListener("keydown", onKey);
|
|
445
|
-
}, []);
|
|
446
675
|
const select = (entityId) => {
|
|
447
676
|
setQuery("");
|
|
448
677
|
onSelect(entityId);
|
|
449
678
|
};
|
|
450
679
|
const isExpanded = (groupId, hasActive) => expanded[groupId] ?? hasActive;
|
|
451
|
-
return /* @__PURE__ */
|
|
452
|
-
/* @__PURE__ */
|
|
453
|
-
|
|
680
|
+
return /* @__PURE__ */ jsxs2("aside", { className: "mst-side", children: [
|
|
681
|
+
/* @__PURE__ */ jsxs2("div", { className: "mst-side__brand", children: [
|
|
682
|
+
/* @__PURE__ */ jsx2("span", { className: "mst-mark", children: /* @__PURE__ */ jsx2(Icon, { name: "baton", size: 15 }) }),
|
|
683
|
+
/* @__PURE__ */ jsx2("span", { className: "mst-side__title", children: title })
|
|
684
|
+
] }),
|
|
685
|
+
/* @__PURE__ */ jsx2("div", { className: "mst-side__search", children: /* @__PURE__ */ jsx2(
|
|
454
686
|
"input",
|
|
455
687
|
{
|
|
456
|
-
ref: searchRef,
|
|
457
688
|
type: "search",
|
|
458
689
|
value: query,
|
|
459
690
|
onChange: (e) => setQuery(e.target.value),
|
|
@@ -461,11 +692,11 @@ function Sidebar({
|
|
|
461
692
|
if (e.key === "Escape") setQuery("");
|
|
462
693
|
if (e.key === "Enter" && results[0]) select(results[0].id);
|
|
463
694
|
},
|
|
464
|
-
placeholder:
|
|
465
|
-
|
|
695
|
+
placeholder: t.searchMenu,
|
|
696
|
+
"aria-label": t.searchMenu
|
|
466
697
|
}
|
|
467
698
|
) }),
|
|
468
|
-
/* @__PURE__ */
|
|
699
|
+
/* @__PURE__ */ jsx2("nav", { className: "mst-side__nav", children: query ? results.length === 0 ? /* @__PURE__ */ jsx2("p", { className: "mst-side__empty", children: t.noMenuResults }) : results.map((e) => /* @__PURE__ */ jsx2(
|
|
469
700
|
EntityButton,
|
|
470
701
|
{
|
|
471
702
|
entity: e,
|
|
@@ -475,90 +706,599 @@ function Sidebar({
|
|
|
475
706
|
query
|
|
476
707
|
},
|
|
477
708
|
e.id
|
|
478
|
-
)) : /* @__PURE__ */
|
|
479
|
-
|
|
480
|
-
/* @__PURE__ */
|
|
481
|
-
|
|
709
|
+
)) : /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
710
|
+
onHome && /* @__PURE__ */ jsxs2("button", { type: "button", className: "mst-navitem", "aria-current": homeActive, onClick: onHome, title: t.hub, children: [
|
|
711
|
+
/* @__PURE__ */ jsx2(Icon, { name: "hub", size: 14 }),
|
|
712
|
+
/* @__PURE__ */ jsx2("span", { className: "mst-navitem__label", children: t.hub })
|
|
482
713
|
] }),
|
|
483
|
-
/* @__PURE__ */
|
|
714
|
+
recentEntities.length > 0 && /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
715
|
+
/* @__PURE__ */ jsx2("p", { className: "mst-side__section", children: t.recents }),
|
|
716
|
+
recentEntities.map((e) => /* @__PURE__ */ jsx2(EntityButton, { entity: e, active: e.id === activeId, onSelect: select }, e.id))
|
|
717
|
+
] }),
|
|
718
|
+
/* @__PURE__ */ jsx2("p", { className: "mst-side__section", children: t.menus }),
|
|
484
719
|
menu.groups.map((group) => {
|
|
485
720
|
const hasActive = group.entities.some((e) => e.id === activeId);
|
|
486
721
|
const open = isExpanded(group.id, hasActive);
|
|
487
|
-
return /* @__PURE__ */
|
|
488
|
-
/* @__PURE__ */
|
|
722
|
+
return /* @__PURE__ */ jsxs2("div", { children: [
|
|
723
|
+
/* @__PURE__ */ jsxs2(
|
|
489
724
|
"button",
|
|
490
725
|
{
|
|
726
|
+
type: "button",
|
|
727
|
+
"aria-expanded": open,
|
|
491
728
|
onClick: () => setExpanded((prev) => ({ ...prev, [group.id]: !open })),
|
|
492
|
-
className: "
|
|
729
|
+
className: "mst-navitem mst-navitem--folder",
|
|
493
730
|
children: [
|
|
494
|
-
/* @__PURE__ */
|
|
495
|
-
|
|
496
|
-
|
|
731
|
+
/* @__PURE__ */ jsx2(
|
|
732
|
+
Icon,
|
|
733
|
+
{
|
|
734
|
+
name: "chevronRight",
|
|
735
|
+
size: 12,
|
|
736
|
+
className: open ? "mst-navitem__chev mst-navitem__chev--open" : "mst-navitem__chev"
|
|
737
|
+
}
|
|
738
|
+
),
|
|
739
|
+
/* @__PURE__ */ jsx2("span", { className: "mst-navitem__label", children: group.label }),
|
|
740
|
+
/* @__PURE__ */ jsx2("span", { className: "mst-navitem__count", children: group.entities.length })
|
|
497
741
|
]
|
|
498
742
|
}
|
|
499
743
|
),
|
|
500
|
-
open && /* @__PURE__ */
|
|
744
|
+
open && /* @__PURE__ */ jsx2("div", { className: "mst-side__children", children: group.entities.map((e) => /* @__PURE__ */ jsx2(EntityButton, { entity: e, active: e.id === activeId, onSelect: select }, e.id)) })
|
|
501
745
|
] }, group.id);
|
|
502
746
|
}),
|
|
503
|
-
menu.ungrouped.map((e) => /* @__PURE__ */
|
|
747
|
+
menu.ungrouped.map((e) => /* @__PURE__ */ jsx2(EntityButton, { entity: e, active: e.id === activeId, onSelect: select }, e.id))
|
|
504
748
|
] }) }),
|
|
505
|
-
/* @__PURE__ */
|
|
749
|
+
/* @__PURE__ */ jsxs2("div", { className: "mst-side__foot", children: [
|
|
750
|
+
/* @__PURE__ */ jsx2("span", { className: "mst-pulse", "aria-hidden": true }),
|
|
751
|
+
/* @__PURE__ */ jsx2("span", { children: t.entitiesFooter(entities.length) })
|
|
752
|
+
] })
|
|
506
753
|
] });
|
|
507
754
|
}
|
|
508
755
|
|
|
509
|
-
// src/components/
|
|
510
|
-
import {
|
|
756
|
+
// src/components/Header.tsx
|
|
757
|
+
import { useState as useState5 } from "react";
|
|
511
758
|
|
|
512
|
-
// src/
|
|
513
|
-
import {
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
759
|
+
// src/theme.tsx
|
|
760
|
+
import { createContext as createContext3, useCallback as useCallback2, useContext as useContext3, useMemo as useMemo2, useState as useState3 } from "react";
|
|
761
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
762
|
+
var MAESTRO_THEMES = [
|
|
763
|
+
{ id: "cobalto", label: "Grafite & Cobalto", swatch: "#3557e8" },
|
|
764
|
+
{ id: "latao", label: "Ard\xF3sia & Lat\xE3o", swatch: "#9a6a22" },
|
|
765
|
+
{ id: "teal", label: "Tinta & Teal", swatch: "#0e7c74" }
|
|
766
|
+
];
|
|
767
|
+
var STORAGE_KEY = "maestro-admin:appearance";
|
|
768
|
+
var DEFAULTS = {
|
|
769
|
+
preset: "cobalto",
|
|
770
|
+
scheme: "system",
|
|
771
|
+
density: "comfortable"
|
|
772
|
+
};
|
|
773
|
+
function normalizeConfig(input) {
|
|
774
|
+
if (!input) return {};
|
|
775
|
+
return typeof input === "string" ? { preset: input } : input;
|
|
776
|
+
}
|
|
777
|
+
function readStored() {
|
|
778
|
+
try {
|
|
779
|
+
const raw = globalThis.localStorage?.getItem(STORAGE_KEY);
|
|
780
|
+
const parsed = raw ? JSON.parse(raw) : null;
|
|
781
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
782
|
+
} catch {
|
|
783
|
+
return {};
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
function writeStored(value) {
|
|
787
|
+
try {
|
|
788
|
+
globalThis.localStorage?.setItem(STORAGE_KEY, JSON.stringify(value));
|
|
789
|
+
} catch {
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
var ThemeContext = createContext3({
|
|
793
|
+
...DEFAULTS,
|
|
794
|
+
picker: true,
|
|
795
|
+
setPreset: () => void 0,
|
|
796
|
+
setScheme: () => void 0,
|
|
797
|
+
setDensity: () => void 0
|
|
798
|
+
});
|
|
799
|
+
function useTheme() {
|
|
800
|
+
return useContext3(ThemeContext);
|
|
801
|
+
}
|
|
802
|
+
function MaestroThemeProvider({
|
|
803
|
+
theme,
|
|
804
|
+
className,
|
|
805
|
+
children,
|
|
806
|
+
rootRef
|
|
807
|
+
}) {
|
|
808
|
+
const config = useMemo2(() => normalizeConfig(theme), [theme]);
|
|
809
|
+
const remember = config.remember !== false;
|
|
810
|
+
const [state, setState] = useState3(() => {
|
|
811
|
+
const stored = remember ? readStored() : {};
|
|
812
|
+
return {
|
|
813
|
+
preset: stored.preset ?? config.preset ?? DEFAULTS.preset,
|
|
814
|
+
scheme: stored.scheme ?? config.scheme ?? DEFAULTS.scheme,
|
|
815
|
+
density: stored.density ?? config.density ?? DEFAULTS.density
|
|
816
|
+
};
|
|
817
|
+
});
|
|
818
|
+
const update = useCallback2(
|
|
819
|
+
(patch) => {
|
|
820
|
+
setState((prev) => {
|
|
821
|
+
const next = { ...prev, ...patch };
|
|
822
|
+
if (remember) writeStored(next);
|
|
823
|
+
return next;
|
|
824
|
+
});
|
|
825
|
+
},
|
|
826
|
+
[remember]
|
|
827
|
+
);
|
|
828
|
+
const value = useMemo2(
|
|
829
|
+
() => ({
|
|
830
|
+
preset: state.preset ?? DEFAULTS.preset,
|
|
831
|
+
scheme: state.scheme ?? DEFAULTS.scheme,
|
|
832
|
+
density: state.density ?? DEFAULTS.density,
|
|
833
|
+
picker: config.picker !== false,
|
|
834
|
+
setPreset: (preset) => update({ preset }),
|
|
835
|
+
setScheme: (scheme) => update({ scheme }),
|
|
836
|
+
setDensity: (density) => update({ density })
|
|
837
|
+
}),
|
|
838
|
+
[state, config.picker, update]
|
|
839
|
+
);
|
|
840
|
+
return /* @__PURE__ */ jsx3(ThemeContext.Provider, { value, children: /* @__PURE__ */ jsx3(
|
|
841
|
+
"div",
|
|
842
|
+
{
|
|
843
|
+
ref: rootRef,
|
|
844
|
+
className,
|
|
845
|
+
"data-maestro-theme": value.preset,
|
|
846
|
+
"data-maestro-scheme": value.scheme,
|
|
847
|
+
"data-maestro-density": value.density,
|
|
848
|
+
style: config.tokens,
|
|
849
|
+
children
|
|
850
|
+
}
|
|
851
|
+
) });
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
// src/components/menu.tsx
|
|
855
|
+
import { useEffect as useEffect2, useLayoutEffect, useRef, useState as useState4 } from "react";
|
|
856
|
+
import { createPortal } from "react-dom";
|
|
857
|
+
import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
858
|
+
function PopMenu({ anchor, items, onClose }) {
|
|
859
|
+
const ref = useRef(null);
|
|
860
|
+
const [pos, setPos] = useState4(anchor);
|
|
861
|
+
const root = useAdminRoot();
|
|
862
|
+
useLayoutEffect(() => {
|
|
863
|
+
const el = ref.current;
|
|
864
|
+
if (!el) return;
|
|
865
|
+
const rect = el.getBoundingClientRect();
|
|
866
|
+
setPos({
|
|
867
|
+
x: Math.max(4, Math.min(anchor.x, window.innerWidth - rect.width - 8)),
|
|
868
|
+
y: Math.max(4, Math.min(anchor.y, window.innerHeight - rect.height - 8))
|
|
869
|
+
});
|
|
870
|
+
}, [anchor]);
|
|
871
|
+
useEffect2(() => {
|
|
872
|
+
const onDown = (e) => {
|
|
873
|
+
if (ref.current && !ref.current.contains(e.target)) onClose();
|
|
874
|
+
};
|
|
875
|
+
const onKey = (e) => {
|
|
876
|
+
if (e.key === "Escape") onClose();
|
|
877
|
+
};
|
|
878
|
+
document.addEventListener("mousedown", onDown);
|
|
879
|
+
document.addEventListener("keydown", onKey);
|
|
880
|
+
window.addEventListener("scroll", onClose, true);
|
|
881
|
+
window.addEventListener("resize", onClose);
|
|
882
|
+
return () => {
|
|
883
|
+
document.removeEventListener("mousedown", onDown);
|
|
884
|
+
document.removeEventListener("keydown", onKey);
|
|
885
|
+
window.removeEventListener("scroll", onClose, true);
|
|
886
|
+
window.removeEventListener("resize", onClose);
|
|
887
|
+
};
|
|
888
|
+
}, [onClose]);
|
|
889
|
+
return createPortal(
|
|
890
|
+
/* @__PURE__ */ jsx4("div", { ref, role: "menu", style: { left: pos.x, top: pos.y }, className: "mst-menu", children: items.map((item) => /* @__PURE__ */ jsxs3("div", { children: [
|
|
891
|
+
item.separated && /* @__PURE__ */ jsx4("div", { className: "mst-menu__sep" }),
|
|
892
|
+
/* @__PURE__ */ jsxs3(
|
|
893
|
+
"button",
|
|
894
|
+
{
|
|
895
|
+
type: "button",
|
|
896
|
+
role: "menuitem",
|
|
897
|
+
onClick: () => {
|
|
898
|
+
onClose();
|
|
899
|
+
item.onSelect();
|
|
900
|
+
},
|
|
901
|
+
className: `mst-menu__item${item.danger ? " mst-menu__item--danger" : ""}`,
|
|
902
|
+
children: [
|
|
903
|
+
item.icon && /* @__PURE__ */ jsx4(Icon, { name: item.icon, size: 14 }),
|
|
904
|
+
item.label
|
|
905
|
+
]
|
|
906
|
+
}
|
|
907
|
+
)
|
|
908
|
+
] }, item.id)) }),
|
|
909
|
+
root ?? document.body
|
|
910
|
+
);
|
|
911
|
+
}
|
|
912
|
+
function MenuButton({ items, label }) {
|
|
913
|
+
const [anchor, setAnchor] = useState4(void 0);
|
|
914
|
+
return /* @__PURE__ */ jsxs3(Fragment3, { children: [
|
|
915
|
+
/* @__PURE__ */ jsx4(
|
|
916
|
+
"button",
|
|
917
|
+
{
|
|
918
|
+
type: "button",
|
|
919
|
+
"aria-label": label,
|
|
920
|
+
title: label,
|
|
921
|
+
onClick: (e) => {
|
|
922
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
923
|
+
setAnchor({ x: rect.right - 184, y: rect.bottom + 4 });
|
|
924
|
+
},
|
|
925
|
+
className: "mst-iconbtn",
|
|
926
|
+
children: /* @__PURE__ */ jsx4(Icon, { name: "dots", size: 14 })
|
|
927
|
+
}
|
|
928
|
+
),
|
|
929
|
+
anchor && /* @__PURE__ */ jsx4(PopMenu, { anchor, items, onClose: () => setAnchor(void 0) })
|
|
930
|
+
] });
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
// src/components/Header.tsx
|
|
934
|
+
import { Fragment as Fragment4, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
935
|
+
var NEXT_SCHEME = { system: "light", light: "dark", dark: "system" };
|
|
936
|
+
var SCHEME_ICON = {
|
|
937
|
+
system: "swatch",
|
|
938
|
+
light: "sun",
|
|
939
|
+
dark: "moon"
|
|
519
940
|
};
|
|
941
|
+
function Header({ crumbs, onOpenPalette }) {
|
|
942
|
+
const t = useT();
|
|
943
|
+
const theme = useTheme();
|
|
944
|
+
const [anchor, setAnchor] = useState5(void 0);
|
|
945
|
+
const themeItems = MAESTRO_THEMES.map((option) => ({
|
|
946
|
+
id: option.id,
|
|
947
|
+
label: /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
948
|
+
/* @__PURE__ */ jsx5("span", { className: "mst-swatch", style: { background: option.swatch } }),
|
|
949
|
+
option.label,
|
|
950
|
+
theme.preset === option.id && /* @__PURE__ */ jsx5(Icon, { name: "check", size: 13 })
|
|
951
|
+
] }),
|
|
952
|
+
onSelect: () => theme.setPreset(option.id)
|
|
953
|
+
}));
|
|
954
|
+
const schemeLabel = theme.scheme === "system" ? t.schemeSystem : theme.scheme === "light" ? t.schemeLight : t.schemeDark;
|
|
955
|
+
return /* @__PURE__ */ jsxs4("header", { className: "mst-header", children: [
|
|
956
|
+
/* @__PURE__ */ jsx5("nav", { className: "mst-crumbs", "aria-label": t.breadcrumb, children: crumbs.map((crumb, index) => /* @__PURE__ */ jsxs4("span", { className: "mst-crumbs__item", children: [
|
|
957
|
+
index > 0 && /* @__PURE__ */ jsx5(Icon, { name: "chevronRight", size: 12, className: "mst-crumbs__sep" }),
|
|
958
|
+
crumb.onClick ? /* @__PURE__ */ jsxs4("button", { type: "button", className: "mst-crumbs__link", onClick: crumb.onClick, children: [
|
|
959
|
+
crumb.icon && /* @__PURE__ */ jsx5(Icon, { name: crumb.icon, size: 13 }),
|
|
960
|
+
crumb.label
|
|
961
|
+
] }) : /* @__PURE__ */ jsxs4("span", { className: "mst-crumbs__now", children: [
|
|
962
|
+
crumb.icon && /* @__PURE__ */ jsx5(Icon, { name: crumb.icon, size: 13 }),
|
|
963
|
+
crumb.label
|
|
964
|
+
] })
|
|
965
|
+
] }, `${crumb.label}-${index}`)) }),
|
|
966
|
+
/* @__PURE__ */ jsxs4("button", { type: "button", className: "mst-searchchip", onClick: onOpenPalette, children: [
|
|
967
|
+
/* @__PURE__ */ jsx5(Icon, { name: "search", size: 13 }),
|
|
968
|
+
/* @__PURE__ */ jsx5("span", { children: t.search }),
|
|
969
|
+
/* @__PURE__ */ jsx5("kbd", { className: "mst-kbd", children: t.commandKey })
|
|
970
|
+
] }),
|
|
971
|
+
theme.picker && /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
972
|
+
/* @__PURE__ */ jsx5(
|
|
973
|
+
"button",
|
|
974
|
+
{
|
|
975
|
+
type: "button",
|
|
976
|
+
"aria-label": t.density,
|
|
977
|
+
title: t.density,
|
|
978
|
+
"aria-pressed": theme.density === "compact",
|
|
979
|
+
className: "mst-iconbtn",
|
|
980
|
+
onClick: () => theme.setDensity(theme.density === "compact" ? "comfortable" : "compact"),
|
|
981
|
+
children: /* @__PURE__ */ jsx5(Icon, { name: "rows", size: 14 })
|
|
982
|
+
}
|
|
983
|
+
),
|
|
984
|
+
/* @__PURE__ */ jsx5(
|
|
985
|
+
"button",
|
|
986
|
+
{
|
|
987
|
+
type: "button",
|
|
988
|
+
"aria-label": t.palette,
|
|
989
|
+
title: t.palette,
|
|
990
|
+
className: "mst-iconbtn",
|
|
991
|
+
onClick: (e) => {
|
|
992
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
993
|
+
setAnchor({ x: rect.right - 200, y: rect.bottom + 6 });
|
|
994
|
+
},
|
|
995
|
+
children: /* @__PURE__ */ jsx5(Icon, { name: "swatch", size: 14 })
|
|
996
|
+
}
|
|
997
|
+
),
|
|
998
|
+
/* @__PURE__ */ jsx5(
|
|
999
|
+
"button",
|
|
1000
|
+
{
|
|
1001
|
+
type: "button",
|
|
1002
|
+
"aria-label": `${t.colorScheme}: ${schemeLabel}`,
|
|
1003
|
+
title: `${t.colorScheme}: ${schemeLabel}`,
|
|
1004
|
+
className: "mst-iconbtn",
|
|
1005
|
+
onClick: () => theme.setScheme(NEXT_SCHEME[theme.scheme]),
|
|
1006
|
+
children: /* @__PURE__ */ jsx5(Icon, { name: SCHEME_ICON[theme.scheme], size: 14 })
|
|
1007
|
+
}
|
|
1008
|
+
)
|
|
1009
|
+
] }),
|
|
1010
|
+
anchor && /* @__PURE__ */ jsx5(PopMenu, { anchor, items: themeItems, onClose: () => setAnchor(void 0) })
|
|
1011
|
+
] });
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
// src/components/Hub.tsx
|
|
1015
|
+
import { useMemo as useMemo3 } from "react";
|
|
1016
|
+
|
|
1017
|
+
// src/components/ui.tsx
|
|
1018
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
520
1019
|
function Button({
|
|
521
1020
|
variant = "secondary",
|
|
1021
|
+
icon,
|
|
522
1022
|
className = "",
|
|
1023
|
+
children,
|
|
523
1024
|
...props
|
|
524
1025
|
}) {
|
|
525
|
-
return /* @__PURE__ */
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
...props
|
|
530
|
-
}
|
|
531
|
-
);
|
|
1026
|
+
return /* @__PURE__ */ jsxs5("button", { type: "button", className: `mst-btn mst-btn--${variant} ${className}`.trim(), ...props, children: [
|
|
1027
|
+
icon && /* @__PURE__ */ jsx6(Icon, { name: icon, size: 14 }),
|
|
1028
|
+
children
|
|
1029
|
+
] });
|
|
532
1030
|
}
|
|
533
|
-
function
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
1031
|
+
function IconButton({
|
|
1032
|
+
icon,
|
|
1033
|
+
label,
|
|
1034
|
+
className = "",
|
|
1035
|
+
...props
|
|
1036
|
+
}) {
|
|
1037
|
+
return /* @__PURE__ */ jsx6("button", { type: "button", "aria-label": label, title: label, className: `mst-iconbtn ${className}`.trim(), ...props, children: /* @__PURE__ */ jsx6(Icon, { name: icon, size: 14 }) });
|
|
1038
|
+
}
|
|
1039
|
+
function Badge({ children, tone = "neutral", dot = false }) {
|
|
1040
|
+
const toneClass = tone === "neutral" ? "" : ` mst-badge--${tone}`;
|
|
1041
|
+
return /* @__PURE__ */ jsx6("span", { className: `mst-badge${toneClass}${dot ? " mst-badge--dot" : ""}`, children });
|
|
542
1042
|
}
|
|
543
1043
|
function Spinner({ label }) {
|
|
544
|
-
return /* @__PURE__ */
|
|
545
|
-
/* @__PURE__ */ jsx2("span", { className: "h-4 w-4 animate-spin rounded-full border-2 border-slate-300 border-t-indigo-600" }),
|
|
546
|
-
label ?? "Loading\u2026"
|
|
547
|
-
] });
|
|
1044
|
+
return /* @__PURE__ */ jsx6("div", { className: "mst-spinner", role: "status", children: label ?? "Loading\u2026" });
|
|
548
1045
|
}
|
|
549
1046
|
function EmptyState({ title, hint }) {
|
|
550
|
-
return /* @__PURE__ */
|
|
551
|
-
/* @__PURE__ */
|
|
552
|
-
hint && /* @__PURE__ */
|
|
1047
|
+
return /* @__PURE__ */ jsxs5("div", { className: "mst-empty", children: [
|
|
1048
|
+
/* @__PURE__ */ jsx6("p", { className: "mst-empty__title", children: title }),
|
|
1049
|
+
hint && /* @__PURE__ */ jsx6("p", { className: "mst-empty__hint", children: hint })
|
|
553
1050
|
] });
|
|
554
1051
|
}
|
|
555
1052
|
function ErrorBanner({ error }) {
|
|
556
|
-
return /* @__PURE__ */
|
|
1053
|
+
return /* @__PURE__ */ jsxs5("div", { className: "mst-error", role: "alert", children: [
|
|
1054
|
+
/* @__PURE__ */ jsx6(Icon, { name: "alert", size: 15 }),
|
|
1055
|
+
/* @__PURE__ */ jsx6("span", { children: error.message })
|
|
1056
|
+
] });
|
|
1057
|
+
}
|
|
1058
|
+
function Toast({ children }) {
|
|
1059
|
+
return /* @__PURE__ */ jsx6("div", { className: "mst-toast", role: "status", children });
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
// src/components/Hub.tsx
|
|
1063
|
+
import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1064
|
+
var CAPS = ["list", "create", "update", "delete"];
|
|
1065
|
+
function EntityCard({ entity, onOpen }) {
|
|
1066
|
+
const t = useT();
|
|
1067
|
+
const capLabel = { list: t.capRead, create: t.capCreate, update: t.capUpdate, delete: t.capDelete };
|
|
1068
|
+
return /* @__PURE__ */ jsxs6("button", { type: "button", className: "mst-ecard", onClick: () => onOpen(entity.id), children: [
|
|
1069
|
+
/* @__PURE__ */ jsx7("span", { className: "mst-ecard__tile", children: /* @__PURE__ */ jsx7(Icon, { name: entityIcon(entity), size: 16 }) }),
|
|
1070
|
+
/* @__PURE__ */ jsxs6("span", { className: "mst-ecard__body", children: [
|
|
1071
|
+
/* @__PURE__ */ jsx7("span", { className: "mst-ecard__name", children: entity.label.plural }),
|
|
1072
|
+
/* @__PURE__ */ jsx7("span", { className: "mst-ecard__desc", children: entity.description ?? t.fieldCount(entity.fields.length) }),
|
|
1073
|
+
/* @__PURE__ */ jsx7("span", { className: "mst-caps", children: CAPS.map((cap) => /* @__PURE__ */ jsx7("span", { className: "mst-cap", "data-on": String(entity.capabilities[cap]), children: capLabel[cap] }, cap)) }),
|
|
1074
|
+
/* @__PURE__ */ jsxs6("span", { className: "mst-source", children: [
|
|
1075
|
+
/* @__PURE__ */ jsx7(Icon, { name: "database", size: 11 }),
|
|
1076
|
+
/* @__PURE__ */ jsxs6("span", { children: [
|
|
1077
|
+
entity.datasource,
|
|
1078
|
+
" \xB7 ",
|
|
1079
|
+
entity.table
|
|
1080
|
+
] })
|
|
1081
|
+
] })
|
|
1082
|
+
] })
|
|
1083
|
+
] });
|
|
1084
|
+
}
|
|
1085
|
+
function Hub({
|
|
1086
|
+
entities,
|
|
1087
|
+
labels,
|
|
1088
|
+
onOpen
|
|
1089
|
+
}) {
|
|
1090
|
+
const t = useT();
|
|
1091
|
+
const menu = useMemo3(() => buildMenu(entities, labels), [entities, labels]);
|
|
1092
|
+
const sources = useMemo3(() => new Set(entities.map((e) => e.datasource)).size, [entities]);
|
|
1093
|
+
if (entities.length === 0) {
|
|
1094
|
+
return /* @__PURE__ */ jsx7(EmptyState, { title: t.noEntities, hint: t.noEntitiesHint });
|
|
1095
|
+
}
|
|
1096
|
+
return /* @__PURE__ */ jsxs6("section", { className: "mst-page", children: [
|
|
1097
|
+
/* @__PURE__ */ jsx7("div", { className: "mst-pagehead", children: /* @__PURE__ */ jsxs6("div", { children: [
|
|
1098
|
+
/* @__PURE__ */ jsx7("h1", { className: "mst-pagehead__title", children: t.entities }),
|
|
1099
|
+
/* @__PURE__ */ jsx7("p", { className: "mst-pagehead__meta", children: t.hubMeta(entities.length, sources) })
|
|
1100
|
+
] }) }),
|
|
1101
|
+
/* @__PURE__ */ jsxs6("div", { className: "mst-hub", children: [
|
|
1102
|
+
menu.groups.map((group) => /* @__PURE__ */ jsxs6("div", { children: [
|
|
1103
|
+
/* @__PURE__ */ jsx7("p", { className: "mst-grouplabel", children: group.label }),
|
|
1104
|
+
/* @__PURE__ */ jsx7("div", { className: "mst-cards", children: group.entities.map((entity) => /* @__PURE__ */ jsx7(EntityCard, { entity, onOpen }, entity.id)) })
|
|
1105
|
+
] }, group.id)),
|
|
1106
|
+
menu.ungrouped.length > 0 && /* @__PURE__ */ jsxs6("div", { children: [
|
|
1107
|
+
menu.groups.length > 0 && /* @__PURE__ */ jsx7("p", { className: "mst-grouplabel", children: t.otherEntities }),
|
|
1108
|
+
/* @__PURE__ */ jsx7("div", { className: "mst-cards", children: menu.ungrouped.map((entity) => /* @__PURE__ */ jsx7(EntityCard, { entity, onOpen }, entity.id)) })
|
|
1109
|
+
] })
|
|
1110
|
+
] })
|
|
1111
|
+
] });
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
// src/components/CommandPalette.tsx
|
|
1115
|
+
import { useEffect as useEffect3, useMemo as useMemo4, useRef as useRef2, useState as useState6 } from "react";
|
|
1116
|
+
import { createPortal as createPortal2 } from "react-dom";
|
|
1117
|
+
import { Fragment as Fragment5, jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1118
|
+
function normalize2(text) {
|
|
1119
|
+
return text.toLowerCase().normalize("NFD").replace(/\p{M}/gu, "");
|
|
1120
|
+
}
|
|
1121
|
+
function Highlight2({ label, query }) {
|
|
1122
|
+
if (!query) return label;
|
|
1123
|
+
const folded = normalize2(label);
|
|
1124
|
+
const idx = folded.indexOf(normalize2(query));
|
|
1125
|
+
if (idx === -1) return label;
|
|
1126
|
+
return /* @__PURE__ */ jsxs7(Fragment5, { children: [
|
|
1127
|
+
label.slice(0, idx),
|
|
1128
|
+
/* @__PURE__ */ jsx8("mark", { children: label.slice(idx, idx + query.length) }),
|
|
1129
|
+
label.slice(idx + query.length)
|
|
1130
|
+
] });
|
|
1131
|
+
}
|
|
1132
|
+
function entityCommands(entities, groupOf, onSelect) {
|
|
1133
|
+
return entities.map((entity) => ({
|
|
1134
|
+
id: `entity:${entity.id}`,
|
|
1135
|
+
label: entity.label.plural,
|
|
1136
|
+
icon: entityIcon(entity),
|
|
1137
|
+
hint: groupOf.get(entity.id),
|
|
1138
|
+
keywords: `${entity.label.singular} ${entity.table} ${entity.id}`,
|
|
1139
|
+
run: () => onSelect(entity.id)
|
|
1140
|
+
}));
|
|
1141
|
+
}
|
|
1142
|
+
function CommandPalette({ sections, onClose }) {
|
|
1143
|
+
const t = useT();
|
|
1144
|
+
const root = useAdminRoot();
|
|
1145
|
+
const inputRef = useRef2(null);
|
|
1146
|
+
const listRef = useRef2(null);
|
|
1147
|
+
const [query, setQuery] = useState6("");
|
|
1148
|
+
const [cursor, setCursor] = useState6(0);
|
|
1149
|
+
const visible = useMemo4(() => {
|
|
1150
|
+
if (!query) return sections;
|
|
1151
|
+
const q = normalize2(query);
|
|
1152
|
+
return sections.map((section) => ({
|
|
1153
|
+
...section,
|
|
1154
|
+
commands: section.commands.filter((c) => normalize2(`${c.label} ${c.keywords ?? ""}`).includes(q))
|
|
1155
|
+
})).filter((section) => section.commands.length > 0);
|
|
1156
|
+
}, [sections, query]);
|
|
1157
|
+
const flat = useMemo4(() => visible.flatMap((section) => section.commands), [visible]);
|
|
1158
|
+
const active = flat[Math.min(cursor, flat.length - 1)];
|
|
1159
|
+
useEffect3(() => setCursor(0), [query]);
|
|
1160
|
+
useEffect3(() => inputRef.current?.focus(), []);
|
|
1161
|
+
useEffect3(() => {
|
|
1162
|
+
listRef.current?.querySelector('[data-active="true"]')?.scrollIntoView({ block: "nearest" });
|
|
1163
|
+
}, [cursor, query]);
|
|
1164
|
+
const onKeyDown = (e) => {
|
|
1165
|
+
if (e.key === "Escape") {
|
|
1166
|
+
e.preventDefault();
|
|
1167
|
+
onClose();
|
|
1168
|
+
return;
|
|
1169
|
+
}
|
|
1170
|
+
if (e.key === "ArrowDown" || e.key === "n" && e.ctrlKey) {
|
|
1171
|
+
e.preventDefault();
|
|
1172
|
+
setCursor((c) => flat.length === 0 ? 0 : (Math.min(c, flat.length - 1) + 1) % flat.length);
|
|
1173
|
+
return;
|
|
1174
|
+
}
|
|
1175
|
+
if (e.key === "ArrowUp" || e.key === "p" && e.ctrlKey) {
|
|
1176
|
+
e.preventDefault();
|
|
1177
|
+
setCursor((c) => flat.length === 0 ? 0 : (Math.min(c, flat.length - 1) + flat.length - 1) % flat.length);
|
|
1178
|
+
return;
|
|
1179
|
+
}
|
|
1180
|
+
if (e.key === "Enter" && active) {
|
|
1181
|
+
e.preventDefault();
|
|
1182
|
+
onClose();
|
|
1183
|
+
active.run();
|
|
1184
|
+
}
|
|
1185
|
+
};
|
|
1186
|
+
return createPortal2(
|
|
1187
|
+
/* @__PURE__ */ jsx8(
|
|
1188
|
+
"div",
|
|
1189
|
+
{
|
|
1190
|
+
className: "mst-overlay",
|
|
1191
|
+
role: "presentation",
|
|
1192
|
+
onMouseDown: (e) => {
|
|
1193
|
+
if (e.target === e.currentTarget) onClose();
|
|
1194
|
+
},
|
|
1195
|
+
children: /* @__PURE__ */ jsxs7("div", { className: "mst-palette", role: "dialog", "aria-modal": "true", "aria-label": t.commandPalette, children: [
|
|
1196
|
+
/* @__PURE__ */ jsxs7("div", { className: "mst-palette__input", children: [
|
|
1197
|
+
/* @__PURE__ */ jsx8(Icon, { name: "search", size: 15 }),
|
|
1198
|
+
/* @__PURE__ */ jsx8(
|
|
1199
|
+
"input",
|
|
1200
|
+
{
|
|
1201
|
+
ref: inputRef,
|
|
1202
|
+
type: "text",
|
|
1203
|
+
value: query,
|
|
1204
|
+
placeholder: t.palettePlaceholder,
|
|
1205
|
+
"aria-label": t.commandPalette,
|
|
1206
|
+
onChange: (e) => setQuery(e.target.value),
|
|
1207
|
+
onKeyDown
|
|
1208
|
+
}
|
|
1209
|
+
),
|
|
1210
|
+
/* @__PURE__ */ jsx8("kbd", { className: "mst-kbd", children: "esc" })
|
|
1211
|
+
] }),
|
|
1212
|
+
/* @__PURE__ */ jsx8("div", { className: "mst-palette__list", ref: listRef, children: flat.length === 0 ? /* @__PURE__ */ jsx8("p", { className: "mst-palette__empty", children: t.noMenuResults }) : visible.map((section) => /* @__PURE__ */ jsxs7("div", { children: [
|
|
1213
|
+
/* @__PURE__ */ jsx8("p", { className: "mst-palette__section", children: section.label }),
|
|
1214
|
+
section.commands.map((command) => /* @__PURE__ */ jsxs7(
|
|
1215
|
+
"button",
|
|
1216
|
+
{
|
|
1217
|
+
type: "button",
|
|
1218
|
+
className: "mst-palette__row",
|
|
1219
|
+
"data-active": String(command.id === active?.id),
|
|
1220
|
+
onMouseEnter: () => setCursor(flat.findIndex((c) => c.id === command.id)),
|
|
1221
|
+
onClick: () => {
|
|
1222
|
+
onClose();
|
|
1223
|
+
command.run();
|
|
1224
|
+
},
|
|
1225
|
+
children: [
|
|
1226
|
+
command.icon && /* @__PURE__ */ jsx8(Icon, { name: command.icon, size: 14 }),
|
|
1227
|
+
/* @__PURE__ */ jsx8("span", { className: "mst-palette__label", children: /* @__PURE__ */ jsx8(Highlight2, { label: command.label, query }) }),
|
|
1228
|
+
command.hint && /* @__PURE__ */ jsx8("span", { className: "mst-palette__hint", children: command.hint })
|
|
1229
|
+
]
|
|
1230
|
+
},
|
|
1231
|
+
command.id
|
|
1232
|
+
))
|
|
1233
|
+
] }, section.id)) })
|
|
1234
|
+
] })
|
|
1235
|
+
}
|
|
1236
|
+
),
|
|
1237
|
+
root ?? document.body
|
|
1238
|
+
);
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
// src/components/EntityList.tsx
|
|
1242
|
+
import { useMemo as useMemo6, useState as useState9 } from "react";
|
|
1243
|
+
|
|
1244
|
+
// src/columns.ts
|
|
1245
|
+
import { useCallback as useCallback3, useMemo as useMemo5, useState as useState7 } from "react";
|
|
1246
|
+
var STORAGE_PREFIX = "maestro-admin:columns";
|
|
1247
|
+
function read(key) {
|
|
1248
|
+
try {
|
|
1249
|
+
const raw = globalThis.localStorage?.getItem(key);
|
|
1250
|
+
const parsed = raw ? JSON.parse(raw) : void 0;
|
|
1251
|
+
return Array.isArray(parsed) ? parsed.filter((x) => typeof x === "string") : void 0;
|
|
1252
|
+
} catch {
|
|
1253
|
+
return void 0;
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
function write(key, names) {
|
|
1257
|
+
try {
|
|
1258
|
+
if (names) globalThis.localStorage?.setItem(key, JSON.stringify(names));
|
|
1259
|
+
else globalThis.localStorage?.removeItem(key);
|
|
1260
|
+
} catch {
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
function useColumnPrefs(entity, max = 6) {
|
|
1264
|
+
const key = `${STORAGE_PREFIX}:${entity.id}`;
|
|
1265
|
+
const [selection, setSelection] = useState7(() => read(key));
|
|
1266
|
+
const available = useMemo5(() => listFields(entity), [entity]);
|
|
1267
|
+
const fallback = useMemo5(() => primaryListFields(entity, max), [entity, max]);
|
|
1268
|
+
const columns = useMemo5(() => {
|
|
1269
|
+
if (!selection) return fallback;
|
|
1270
|
+
const chosen = available.filter((f) => selection.includes(f.name));
|
|
1271
|
+
return chosen.length > 0 ? chosen : fallback;
|
|
1272
|
+
}, [selection, available, fallback]);
|
|
1273
|
+
const toggle = useCallback3(
|
|
1274
|
+
(name) => {
|
|
1275
|
+
setSelection((prev) => {
|
|
1276
|
+
const current = prev ?? columns.map((f) => f.name);
|
|
1277
|
+
const next = current.includes(name) ? current.filter((n) => n !== name) : [...current, name];
|
|
1278
|
+
write(key, next);
|
|
1279
|
+
return next;
|
|
1280
|
+
});
|
|
1281
|
+
},
|
|
1282
|
+
[key, columns]
|
|
1283
|
+
);
|
|
1284
|
+
const reset = useCallback3(() => {
|
|
1285
|
+
write(key, void 0);
|
|
1286
|
+
setSelection(void 0);
|
|
1287
|
+
}, [key]);
|
|
1288
|
+
const visibleNames = useMemo5(() => new Set(columns.map((f) => f.name)), [columns]);
|
|
1289
|
+
return {
|
|
1290
|
+
columns,
|
|
1291
|
+
available,
|
|
1292
|
+
visible: (name) => visibleNames.has(name),
|
|
1293
|
+
toggle,
|
|
1294
|
+
reset,
|
|
1295
|
+
customized: selection !== void 0
|
|
1296
|
+
};
|
|
557
1297
|
}
|
|
558
1298
|
|
|
559
1299
|
// src/components/RelationValue.tsx
|
|
560
|
-
import { useEffect as
|
|
561
|
-
import { jsx as
|
|
1300
|
+
import { useEffect as useEffect4, useState as useState8 } from "react";
|
|
1301
|
+
import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
562
1302
|
var displayCache = /* @__PURE__ */ new Map();
|
|
563
1303
|
function resolveDisplay(client, entityId, displayField, id) {
|
|
564
1304
|
const key = `${entityId}/${id}`;
|
|
@@ -579,8 +1319,8 @@ function RelationValue({ field, value }) {
|
|
|
579
1319
|
const client = useClient();
|
|
580
1320
|
const target = useEntity(field.relationEntity);
|
|
581
1321
|
const id = String(value);
|
|
582
|
-
const [display, setDisplay] =
|
|
583
|
-
|
|
1322
|
+
const [display, setDisplay] = useState8(void 0);
|
|
1323
|
+
useEffect4(() => {
|
|
584
1324
|
let cancelled = false;
|
|
585
1325
|
setDisplay(void 0);
|
|
586
1326
|
if (!target || target.displayField === target.primaryKey) return;
|
|
@@ -591,24 +1331,17 @@ function RelationValue({ field, value }) {
|
|
|
591
1331
|
cancelled = true;
|
|
592
1332
|
};
|
|
593
1333
|
}, [client, target, id]);
|
|
594
|
-
return /* @__PURE__ */
|
|
595
|
-
"span",
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
target.label.singular,
|
|
602
|
-
":"
|
|
603
|
-
] }),
|
|
604
|
-
/* @__PURE__ */ jsx3("span", { className: "truncate", children: display ?? shortId(id) })
|
|
605
|
-
]
|
|
606
|
-
}
|
|
607
|
-
);
|
|
1334
|
+
return /* @__PURE__ */ jsxs8("span", { title: id, className: "mst-relation", children: [
|
|
1335
|
+
target && /* @__PURE__ */ jsxs8("span", { className: "mst-subtle", children: [
|
|
1336
|
+
target.label.singular,
|
|
1337
|
+
":"
|
|
1338
|
+
] }),
|
|
1339
|
+
/* @__PURE__ */ jsx9("span", { className: "mst-relation__label", children: display ?? shortId(id) })
|
|
1340
|
+
] });
|
|
608
1341
|
}
|
|
609
1342
|
|
|
610
1343
|
// src/components/ObjectValue.tsx
|
|
611
|
-
import { jsx as
|
|
1344
|
+
import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
612
1345
|
var ISO_DATETIME = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/;
|
|
613
1346
|
var MAX_DEPTH = 4;
|
|
614
1347
|
function isPlainObject(value) {
|
|
@@ -616,49 +1349,40 @@ function isPlainObject(value) {
|
|
|
616
1349
|
}
|
|
617
1350
|
function Primitive({ value }) {
|
|
618
1351
|
if (value === null || value === void 0) {
|
|
619
|
-
return /* @__PURE__ */
|
|
1352
|
+
return /* @__PURE__ */ jsx10("span", { className: "mst-subtle", children: "\u2014" });
|
|
620
1353
|
}
|
|
621
1354
|
if (typeof value === "boolean" || typeof value === "number") {
|
|
622
|
-
return /* @__PURE__ */
|
|
1355
|
+
return /* @__PURE__ */ jsx10("code", { className: "mst-mono", style: { color: "var(--maestro-brand)" }, children: String(value) });
|
|
623
1356
|
}
|
|
624
1357
|
const text = String(value);
|
|
625
1358
|
if (ISO_DATETIME.test(text)) {
|
|
626
1359
|
const d = new Date(text);
|
|
627
1360
|
if (!Number.isNaN(d.getTime())) {
|
|
628
|
-
return /* @__PURE__ */
|
|
1361
|
+
return /* @__PURE__ */ jsx10("span", { className: "mst-nums", children: d.toLocaleString() });
|
|
629
1362
|
}
|
|
630
1363
|
}
|
|
631
|
-
return /* @__PURE__ */
|
|
1364
|
+
return /* @__PURE__ */ jsx10("span", { style: { overflowWrap: "anywhere" }, children: text });
|
|
632
1365
|
}
|
|
633
1366
|
function ObjectValue({ value, depth = 0 }) {
|
|
634
1367
|
const t = useT();
|
|
635
1368
|
if (!isPlainObject(value) && !Array.isArray(value)) {
|
|
636
|
-
return /* @__PURE__ */
|
|
1369
|
+
return /* @__PURE__ */ jsx10(Primitive, { value });
|
|
637
1370
|
}
|
|
638
1371
|
if (depth >= MAX_DEPTH) {
|
|
639
|
-
return /* @__PURE__ */
|
|
1372
|
+
return /* @__PURE__ */ jsx10("code", { className: "mst-code mst-code--block", children: JSON.stringify(value) });
|
|
640
1373
|
}
|
|
641
1374
|
const entries = Array.isArray(value) ? value.map((v, i) => [String(i), v]) : Object.entries(value);
|
|
642
1375
|
if (entries.length === 0) {
|
|
643
|
-
return /* @__PURE__ */
|
|
1376
|
+
return /* @__PURE__ */ jsx10("span", { className: "mst-subtle", children: t.emptyObject });
|
|
644
1377
|
}
|
|
645
|
-
return /* @__PURE__ */
|
|
646
|
-
"
|
|
647
|
-
{
|
|
648
|
-
|
|
649
|
-
children: entries.map(([key, entry]) => (
|
|
650
|
-
// flex-wrap + basis: deeply nested values drop to their own full-width line instead of squeezing.
|
|
651
|
-
/* @__PURE__ */ jsxs4("div", { className: "flex flex-wrap items-start gap-x-3 gap-y-1 px-3 py-1.5", children: [
|
|
652
|
-
/* @__PURE__ */ jsx4("dt", { className: "w-36 shrink-0 truncate pt-px text-xs font-medium text-slate-500", title: key, children: key }),
|
|
653
|
-
/* @__PURE__ */ jsx4("dd", { className: "min-w-0 flex-1 basis-56 text-sm", children: isPlainObject(entry) || Array.isArray(entry) ? /* @__PURE__ */ jsx4(ObjectValue, { value: entry, depth: depth + 1 }) : /* @__PURE__ */ jsx4(Primitive, { value: entry }) })
|
|
654
|
-
] }, key)
|
|
655
|
-
))
|
|
656
|
-
}
|
|
657
|
-
);
|
|
1378
|
+
return /* @__PURE__ */ jsx10("dl", { className: depth === 0 ? "mst-objectvalue mst-objectvalue--root" : "mst-objectvalue mst-objectvalue--nested", children: entries.map(([key, entry]) => /* @__PURE__ */ jsxs9("div", { className: "mst-objectvalue__row", children: [
|
|
1379
|
+
/* @__PURE__ */ jsx10("dt", { className: "mst-objectvalue__key", title: key, children: key }),
|
|
1380
|
+
/* @__PURE__ */ jsx10("dd", { className: "mst-objectvalue__val", children: isPlainObject(entry) || Array.isArray(entry) ? /* @__PURE__ */ jsx10(ObjectValue, { value: entry, depth: depth + 1 }) : /* @__PURE__ */ jsx10(Primitive, { value: entry }) })
|
|
1381
|
+
] }, key)) });
|
|
658
1382
|
}
|
|
659
1383
|
|
|
660
1384
|
// src/components/FieldValue.tsx
|
|
661
|
-
import { jsx as
|
|
1385
|
+
import { jsx as jsx11 } from "react/jsx-runtime";
|
|
662
1386
|
function enumLabel(field, value) {
|
|
663
1387
|
const opt = field.enumOptions?.find((o) => o.value === value);
|
|
664
1388
|
return opt ? opt.label : String(value);
|
|
@@ -666,117 +1390,42 @@ function enumLabel(field, value) {
|
|
|
666
1390
|
function FieldValue({ field, value, compact = false }) {
|
|
667
1391
|
const t = useT();
|
|
668
1392
|
if (value === null || value === void 0 || value === "") {
|
|
669
|
-
return /* @__PURE__ */
|
|
1393
|
+
return /* @__PURE__ */ jsx11("span", { className: "mst-subtle", children: "\u2014" });
|
|
670
1394
|
}
|
|
671
1395
|
if (field.sensitive) {
|
|
672
|
-
return /* @__PURE__ */
|
|
1396
|
+
return /* @__PURE__ */ jsx11("span", { className: "mst-subtle", children: "\u2022\u2022\u2022\u2022\u2022\u2022" });
|
|
673
1397
|
}
|
|
674
1398
|
switch (field.type) {
|
|
675
1399
|
case "boolean":
|
|
676
|
-
return value ? /* @__PURE__ */
|
|
1400
|
+
return value ? /* @__PURE__ */ jsx11(Badge, { tone: "ok", dot: true, children: t.yes }) : /* @__PURE__ */ jsx11(Badge, { dot: true, children: t.no });
|
|
677
1401
|
case "enum":
|
|
678
|
-
return /* @__PURE__ */
|
|
1402
|
+
return /* @__PURE__ */ jsx11(Badge, { tone: "brand", children: enumLabel(field, value) });
|
|
679
1403
|
case "relation":
|
|
680
|
-
return field.relationEntity ? /* @__PURE__ */
|
|
1404
|
+
return field.relationEntity ? /* @__PURE__ */ jsx11(RelationValue, { field, value }) : /* @__PURE__ */ jsx11("span", { children: String(value) });
|
|
681
1405
|
case "date":
|
|
682
1406
|
case "datetime": {
|
|
683
1407
|
const d = new Date(String(value));
|
|
684
|
-
return /* @__PURE__ */
|
|
1408
|
+
return /* @__PURE__ */ jsx11("span", { className: "mst-nums", children: Number.isNaN(d.getTime()) ? String(value) : d.toLocaleString() });
|
|
685
1409
|
}
|
|
1410
|
+
case "integer":
|
|
1411
|
+
case "float":
|
|
1412
|
+
case "number":
|
|
1413
|
+
case "decimal":
|
|
1414
|
+
case "currency":
|
|
1415
|
+
return /* @__PURE__ */ jsx11("span", { className: "mst-nums", children: String(value) });
|
|
686
1416
|
case "json":
|
|
687
1417
|
if (compact) {
|
|
688
|
-
return /* @__PURE__ */
|
|
1418
|
+
return /* @__PURE__ */ jsx11("code", { className: "mst-code", children: JSON.stringify(value) });
|
|
689
1419
|
}
|
|
690
1420
|
if (typeof value === "object") {
|
|
691
|
-
return /* @__PURE__ */
|
|
1421
|
+
return /* @__PURE__ */ jsx11(ObjectValue, { value });
|
|
692
1422
|
}
|
|
693
|
-
return /* @__PURE__ */
|
|
1423
|
+
return /* @__PURE__ */ jsx11("code", { className: "mst-code mst-code--block", children: JSON.stringify(value) });
|
|
694
1424
|
default:
|
|
695
|
-
return /* @__PURE__ */
|
|
1425
|
+
return /* @__PURE__ */ jsx11("span", { children: String(value) });
|
|
696
1426
|
}
|
|
697
1427
|
}
|
|
698
1428
|
|
|
699
|
-
// src/components/menu.tsx
|
|
700
|
-
import { useEffect as useEffect4, useLayoutEffect, useRef as useRef2, useState as useState4 } from "react";
|
|
701
|
-
import { createPortal } from "react-dom";
|
|
702
|
-
import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
703
|
-
function PopMenu({ anchor, items, onClose }) {
|
|
704
|
-
const ref = useRef2(null);
|
|
705
|
-
const [pos, setPos] = useState4(anchor);
|
|
706
|
-
useLayoutEffect(() => {
|
|
707
|
-
const el = ref.current;
|
|
708
|
-
if (!el) return;
|
|
709
|
-
const rect = el.getBoundingClientRect();
|
|
710
|
-
setPos({
|
|
711
|
-
x: Math.max(4, Math.min(anchor.x, window.innerWidth - rect.width - 8)),
|
|
712
|
-
y: Math.max(4, Math.min(anchor.y, window.innerHeight - rect.height - 8))
|
|
713
|
-
});
|
|
714
|
-
}, [anchor]);
|
|
715
|
-
useEffect4(() => {
|
|
716
|
-
const onDown = (e) => {
|
|
717
|
-
if (ref.current && !ref.current.contains(e.target)) onClose();
|
|
718
|
-
};
|
|
719
|
-
const onKey = (e) => {
|
|
720
|
-
if (e.key === "Escape") onClose();
|
|
721
|
-
};
|
|
722
|
-
document.addEventListener("mousedown", onDown);
|
|
723
|
-
document.addEventListener("keydown", onKey);
|
|
724
|
-
window.addEventListener("scroll", onClose, true);
|
|
725
|
-
window.addEventListener("resize", onClose);
|
|
726
|
-
return () => {
|
|
727
|
-
document.removeEventListener("mousedown", onDown);
|
|
728
|
-
document.removeEventListener("keydown", onKey);
|
|
729
|
-
window.removeEventListener("scroll", onClose, true);
|
|
730
|
-
window.removeEventListener("resize", onClose);
|
|
731
|
-
};
|
|
732
|
-
}, [onClose]);
|
|
733
|
-
return createPortal(
|
|
734
|
-
/* @__PURE__ */ jsx6(
|
|
735
|
-
"div",
|
|
736
|
-
{
|
|
737
|
-
ref,
|
|
738
|
-
role: "menu",
|
|
739
|
-
style: { position: "fixed", left: pos.x, top: pos.y, zIndex: 50 },
|
|
740
|
-
className: "min-w-44 rounded-md border border-slate-200 bg-white py-1 shadow-lg",
|
|
741
|
-
children: items.map((item) => /* @__PURE__ */ jsx6(
|
|
742
|
-
"button",
|
|
743
|
-
{
|
|
744
|
-
role: "menuitem",
|
|
745
|
-
onClick: () => {
|
|
746
|
-
onClose();
|
|
747
|
-
item.onSelect();
|
|
748
|
-
},
|
|
749
|
-
className: `block w-full px-3 py-1.5 text-left text-sm ${item.danger ? "text-red-600 hover:bg-red-50" : "text-slate-700 hover:bg-slate-100"}`,
|
|
750
|
-
children: item.label
|
|
751
|
-
},
|
|
752
|
-
item.id
|
|
753
|
-
))
|
|
754
|
-
}
|
|
755
|
-
),
|
|
756
|
-
document.body
|
|
757
|
-
);
|
|
758
|
-
}
|
|
759
|
-
function MenuButton({ items, label }) {
|
|
760
|
-
const [anchor, setAnchor] = useState4(void 0);
|
|
761
|
-
return /* @__PURE__ */ jsxs5(Fragment2, { children: [
|
|
762
|
-
/* @__PURE__ */ jsx6(
|
|
763
|
-
"button",
|
|
764
|
-
{
|
|
765
|
-
type: "button",
|
|
766
|
-
"aria-label": label,
|
|
767
|
-
title: label,
|
|
768
|
-
onClick: (e) => {
|
|
769
|
-
const rect = e.currentTarget.getBoundingClientRect();
|
|
770
|
-
setAnchor({ x: rect.right - 176, y: rect.bottom + 4 });
|
|
771
|
-
},
|
|
772
|
-
className: "rounded-md px-2 py-1 text-slate-500 hover:bg-slate-100 hover:text-slate-900",
|
|
773
|
-
children: "\u22EE"
|
|
774
|
-
}
|
|
775
|
-
),
|
|
776
|
-
anchor && /* @__PURE__ */ jsx6(PopMenu, { anchor, items, onClose: () => setAnchor(void 0) })
|
|
777
|
-
] });
|
|
778
|
-
}
|
|
779
|
-
|
|
780
1429
|
// src/clipboard.ts
|
|
781
1430
|
async function copyText(text) {
|
|
782
1431
|
if (navigator.clipboard?.writeText) {
|
|
@@ -797,8 +1446,9 @@ async function copyText(text) {
|
|
|
797
1446
|
}
|
|
798
1447
|
|
|
799
1448
|
// src/components/EntityList.tsx
|
|
800
|
-
import { jsx as
|
|
1449
|
+
import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
801
1450
|
var PAGE_SIZE = 20;
|
|
1451
|
+
var ACTIONS_COLUMN_WIDTH = 44;
|
|
802
1452
|
function filterFor(field, value) {
|
|
803
1453
|
if (value === null || value === void 0) return { field: field.name, operator: "isNull" };
|
|
804
1454
|
if (field.type === "boolean") return { field: field.name, operator: value ? "isTrue" : "isFalse" };
|
|
@@ -813,6 +1463,11 @@ function filterChipText(entity, filter) {
|
|
|
813
1463
|
const value = filter.value ?? "";
|
|
814
1464
|
return `${label} = ${value.length > 24 ? `${value.slice(0, 24)}\u2026` : value}`;
|
|
815
1465
|
}
|
|
1466
|
+
function pageWindow(page, totalPages) {
|
|
1467
|
+
const size = Math.min(5, totalPages);
|
|
1468
|
+
const start = Math.max(1, Math.min(page - 2, totalPages - size + 1));
|
|
1469
|
+
return Array.from({ length: size }, (_, i) => start + i);
|
|
1470
|
+
}
|
|
816
1471
|
function EntityList({
|
|
817
1472
|
entity,
|
|
818
1473
|
onOpen,
|
|
@@ -826,14 +1481,16 @@ function EntityList({
|
|
|
826
1481
|
const t = useT();
|
|
827
1482
|
const entities = useEntities();
|
|
828
1483
|
const relatedLabel = (entityId) => entities.find((e) => e.id === entityId)?.label.singular ?? entityId;
|
|
829
|
-
const
|
|
830
|
-
const
|
|
831
|
-
const
|
|
832
|
-
const [
|
|
833
|
-
const [
|
|
834
|
-
const [
|
|
835
|
-
const [
|
|
836
|
-
const [
|
|
1484
|
+
const columnPrefs = useColumnPrefs(entity);
|
|
1485
|
+
const columns = columnPrefs.columns;
|
|
1486
|
+
const searchFields = useMemo6(() => entity.fields.filter((f) => f.searchable).map((f) => f.name), [entity]);
|
|
1487
|
+
const [page, setPage] = useState9(1);
|
|
1488
|
+
const [search, setSearch] = useState9("");
|
|
1489
|
+
const [sort, setSort] = useState9(void 0);
|
|
1490
|
+
const [filters, setFilters] = useState9(initialFilters ?? []);
|
|
1491
|
+
const [cellMenu, setCellMenu] = useState9(void 0);
|
|
1492
|
+
const [columnMenu, setColumnMenu] = useState9(void 0);
|
|
1493
|
+
const [copied, setCopied] = useState9(false);
|
|
837
1494
|
const query = {
|
|
838
1495
|
page,
|
|
839
1496
|
pageSize: PAGE_SIZE,
|
|
@@ -870,17 +1527,31 @@ function EntityList({
|
|
|
870
1527
|
};
|
|
871
1528
|
const rowMenuItems = (record) => {
|
|
872
1529
|
const id = String(record[entity.primaryKey]);
|
|
873
|
-
const items = [{ id: "view", label:
|
|
874
|
-
if (entity.capabilities.update)
|
|
1530
|
+
const items = [{ id: "view", label: t.view, icon: "eye", onSelect: () => onOpen(id) }];
|
|
1531
|
+
if (entity.capabilities.update) {
|
|
1532
|
+
items.push({ id: "edit", label: t.edit, icon: "pencil", onSelect: () => onEdit(id) });
|
|
1533
|
+
}
|
|
875
1534
|
if (entity.capabilities.clone && entity.capabilities.create) {
|
|
876
|
-
items.push({ id: "clone", label:
|
|
1535
|
+
items.push({ id: "clone", label: t.clone, icon: "clone", onSelect: () => onClone(id) });
|
|
877
1536
|
}
|
|
878
1537
|
items.push(
|
|
879
|
-
{ id: "copy-id", label:
|
|
880
|
-
{
|
|
1538
|
+
{ id: "copy-id", label: t.copyId, icon: "copy", separated: true, onSelect: () => void copy(id) },
|
|
1539
|
+
{
|
|
1540
|
+
id: "copy-object",
|
|
1541
|
+
label: t.copyObject,
|
|
1542
|
+
icon: "copy",
|
|
1543
|
+
onSelect: () => void copy(JSON.stringify(record, null, 2))
|
|
1544
|
+
}
|
|
881
1545
|
);
|
|
882
1546
|
if (entity.capabilities.delete) {
|
|
883
|
-
items.push({
|
|
1547
|
+
items.push({
|
|
1548
|
+
id: "remove",
|
|
1549
|
+
label: t.remove,
|
|
1550
|
+
icon: "trash",
|
|
1551
|
+
danger: true,
|
|
1552
|
+
separated: true,
|
|
1553
|
+
onSelect: () => void onDelete(id)
|
|
1554
|
+
});
|
|
884
1555
|
}
|
|
885
1556
|
return items;
|
|
886
1557
|
};
|
|
@@ -890,130 +1561,195 @@ function EntityList({
|
|
|
890
1561
|
const value = record[field.name];
|
|
891
1562
|
const items = [];
|
|
892
1563
|
const filter = filterFor(field, value);
|
|
893
|
-
if (filter) items.push({ id: "filter", label:
|
|
894
|
-
items.push({ id: "view", label:
|
|
895
|
-
if (entity.capabilities.update) items.push({ id: "edit", label:
|
|
1564
|
+
if (filter) items.push({ id: "filter", label: t.addToFilter, icon: "filter", onSelect: () => addFilter(filter) });
|
|
1565
|
+
items.push({ id: "view", label: t.view, icon: "eye", onSelect: () => onOpen(id) });
|
|
1566
|
+
if (entity.capabilities.update) items.push({ id: "edit", label: t.edit, icon: "pencil", onSelect: () => onEdit(id) });
|
|
896
1567
|
if (field.type === "relation" && field.relationEntity && value !== null && value !== void 0) {
|
|
897
1568
|
const targetId = field.relationEntity;
|
|
898
1569
|
items.push({
|
|
899
1570
|
id: "open-related",
|
|
900
|
-
label:
|
|
1571
|
+
label: t.openRelated(relatedLabel(targetId)),
|
|
1572
|
+
icon: "arrowUpRight",
|
|
901
1573
|
onSelect: () => onNavigate(targetId, String(value))
|
|
902
1574
|
});
|
|
903
1575
|
}
|
|
904
1576
|
if (value !== null && value !== void 0) {
|
|
905
1577
|
items.push({
|
|
906
1578
|
id: "copy-value",
|
|
907
|
-
label:
|
|
1579
|
+
label: t.copyValue,
|
|
1580
|
+
icon: "copy",
|
|
1581
|
+
separated: true,
|
|
908
1582
|
onSelect: () => void copy(typeof value === "object" ? JSON.stringify(value) : String(value))
|
|
909
1583
|
});
|
|
910
1584
|
}
|
|
911
1585
|
return items;
|
|
912
1586
|
};
|
|
1587
|
+
const columnMenuItems = [
|
|
1588
|
+
...columnPrefs.available.map((field) => ({
|
|
1589
|
+
id: `col:${field.name}`,
|
|
1590
|
+
label: field.label,
|
|
1591
|
+
icon: columnPrefs.visible(field.name) ? "check" : void 0,
|
|
1592
|
+
onSelect: () => columnPrefs.toggle(field.name)
|
|
1593
|
+
})),
|
|
1594
|
+
...columnPrefs.customized ? [{ id: "reset", label: t.resetColumns, icon: "rows", separated: true, onSelect: columnPrefs.reset }] : []
|
|
1595
|
+
];
|
|
913
1596
|
const totalPages = data?.totalPages ?? (data ? Math.max(1, Math.ceil(data.total / PAGE_SIZE)) : 1);
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
1597
|
+
const firstRow = data && data.total > 0 ? (page - 1) * PAGE_SIZE + 1 : 0;
|
|
1598
|
+
const lastRow = data ? Math.min(page * PAGE_SIZE, data.total) : 0;
|
|
1599
|
+
return /* @__PURE__ */ jsxs10("section", { className: "mst-page mst-page--fill", children: [
|
|
1600
|
+
/* @__PURE__ */ jsxs10("div", { className: "mst-pagehead", children: [
|
|
1601
|
+
/* @__PURE__ */ jsxs10("div", { children: [
|
|
1602
|
+
/* @__PURE__ */ jsx12("h1", { className: "mst-pagehead__title", children: entity.label.plural }),
|
|
1603
|
+
/* @__PURE__ */ jsxs10("p", { className: "mst-pagehead__meta", children: [
|
|
1604
|
+
data && /* @__PURE__ */ jsx12("span", { className: "mst-nums", children: t.records(data.total) }),
|
|
1605
|
+
/* @__PURE__ */ jsxs10("span", { className: "mst-source", children: [
|
|
1606
|
+
/* @__PURE__ */ jsx12(Icon, { name: "database", size: 11 }),
|
|
1607
|
+
/* @__PURE__ */ jsxs10("span", { children: [
|
|
1608
|
+
entity.datasource,
|
|
1609
|
+
" \xB7 ",
|
|
1610
|
+
entity.table
|
|
1611
|
+
] })
|
|
1612
|
+
] })
|
|
1613
|
+
] })
|
|
919
1614
|
] }),
|
|
920
|
-
/* @__PURE__ */
|
|
921
|
-
searchFields.length > 0 && /* @__PURE__ */
|
|
1615
|
+
/* @__PURE__ */ jsxs10("div", { className: "mst-pagehead__actions", children: [
|
|
1616
|
+
searchFields.length > 0 && /* @__PURE__ */ jsx12(
|
|
922
1617
|
"input",
|
|
923
1618
|
{
|
|
924
1619
|
type: "search",
|
|
1620
|
+
className: "mst-input mst-input--inline",
|
|
925
1621
|
placeholder: t.searchPlaceholder(entity.label.plural),
|
|
926
1622
|
value: search,
|
|
927
1623
|
onChange: (e) => {
|
|
928
1624
|
setSearch(e.target.value);
|
|
929
1625
|
setPage(1);
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
),
|
|
1629
|
+
/* @__PURE__ */ jsxs10(
|
|
1630
|
+
Button,
|
|
1631
|
+
{
|
|
1632
|
+
icon: "columns",
|
|
1633
|
+
onClick: (e) => {
|
|
1634
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
1635
|
+
setColumnMenu({ x: rect.right - 200, y: rect.bottom + 6 });
|
|
930
1636
|
},
|
|
931
|
-
|
|
1637
|
+
children: [
|
|
1638
|
+
t.columns,
|
|
1639
|
+
" ",
|
|
1640
|
+
/* @__PURE__ */ jsx12("span", { className: "mst-subtle mst-nums", children: t.columnsCount(columns.length, columnPrefs.available.length) })
|
|
1641
|
+
]
|
|
932
1642
|
}
|
|
933
1643
|
),
|
|
934
|
-
entity.capabilities.create && /* @__PURE__ */
|
|
1644
|
+
entity.capabilities.create && /* @__PURE__ */ jsx12(Button, { variant: "primary", icon: "plus", onClick: onCreate, children: t.newRecord })
|
|
935
1645
|
] })
|
|
936
1646
|
] }),
|
|
937
|
-
filters.length > 0 && /* @__PURE__ */
|
|
938
|
-
/* @__PURE__ */
|
|
939
|
-
t.filters,
|
|
940
|
-
":"
|
|
941
|
-
] }),
|
|
942
|
-
filters.map((f) => /* @__PURE__ */ jsxs6(
|
|
1647
|
+
filters.length > 0 && /* @__PURE__ */ jsxs10("div", { className: "mst-chips", children: [
|
|
1648
|
+
filters.map((f) => /* @__PURE__ */ jsxs10(
|
|
943
1649
|
"button",
|
|
944
1650
|
{
|
|
1651
|
+
type: "button",
|
|
1652
|
+
className: "mst-chip",
|
|
1653
|
+
title: f.value,
|
|
945
1654
|
onClick: () => {
|
|
946
1655
|
setFilters((prev) => prev.filter((p) => p.field !== f.field));
|
|
947
1656
|
setPage(1);
|
|
948
1657
|
},
|
|
949
|
-
title: f.value,
|
|
950
|
-
className: "inline-flex items-center gap-1 rounded-full bg-indigo-50 px-2.5 py-0.5 text-xs font-medium text-indigo-700 hover:bg-indigo-100",
|
|
951
1658
|
children: [
|
|
952
1659
|
filterChipText(entity, f),
|
|
953
|
-
/* @__PURE__ */
|
|
1660
|
+
/* @__PURE__ */ jsx12(Icon, { name: "close", size: 11 })
|
|
954
1661
|
]
|
|
955
1662
|
},
|
|
956
1663
|
f.field
|
|
957
1664
|
)),
|
|
958
|
-
/* @__PURE__ */
|
|
1665
|
+
/* @__PURE__ */ jsx12(
|
|
959
1666
|
"button",
|
|
960
1667
|
{
|
|
1668
|
+
type: "button",
|
|
1669
|
+
className: "mst-btn mst-btn--ghost",
|
|
961
1670
|
onClick: () => {
|
|
962
1671
|
setFilters([]);
|
|
963
1672
|
setPage(1);
|
|
964
1673
|
},
|
|
965
|
-
className: "text-xs text-slate-500 underline hover:text-slate-700",
|
|
966
1674
|
children: t.clearFilters
|
|
967
1675
|
}
|
|
968
1676
|
)
|
|
969
1677
|
] }),
|
|
970
|
-
error && /* @__PURE__ */
|
|
971
|
-
loading && !data ? /* @__PURE__ */
|
|
972
|
-
/* @__PURE__ */
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
onClick: () => f.sortable && toggleSort(f.name),
|
|
977
|
-
className: `px-3 py-2 text-left font-medium text-slate-600 ${f.sortable ? "cursor-pointer select-none hover:text-slate-900" : ""}`,
|
|
978
|
-
children: [
|
|
979
|
-
f.label,
|
|
980
|
-
sort?.field === f.name && (sort.direction === "asc" ? " \u25B2" : " \u25BC")
|
|
981
|
-
]
|
|
982
|
-
},
|
|
983
|
-
f.name
|
|
984
|
-
)),
|
|
985
|
-
/* @__PURE__ */ jsx7("th", { className: "px-3 py-2 text-right font-medium text-slate-600", children: t.actions })
|
|
986
|
-
] }) }),
|
|
987
|
-
/* @__PURE__ */ jsx7("tbody", { className: "divide-y divide-slate-100 bg-white", children: data?.records.map((record, i) => {
|
|
988
|
-
const id = String(record[entity.primaryKey]);
|
|
989
|
-
return /* @__PURE__ */ jsxs6("tr", { className: "hover:bg-slate-50", children: [
|
|
990
|
-
columns.map((f) => /* @__PURE__ */ jsx7(
|
|
991
|
-
"td",
|
|
1678
|
+
error && /* @__PURE__ */ jsx12(ErrorBanner, { error }),
|
|
1679
|
+
loading && !data ? /* @__PURE__ */ jsx12(Spinner, { label: t.loading }) : data && data.records.length === 0 ? /* @__PURE__ */ jsx12(EmptyState, { title: t.noRecords, hint: search || filters.length ? t.tryDifferentSearch : void 0 }) : /* @__PURE__ */ jsxs10("div", { className: "mst-card mst-tablewrap", children: [
|
|
1680
|
+
/* @__PURE__ */ jsx12("div", { className: "mst-tablescroll", children: /* @__PURE__ */ jsxs10("table", { className: "mst-table", children: [
|
|
1681
|
+
/* @__PURE__ */ jsx12("thead", { children: /* @__PURE__ */ jsxs10("tr", { children: [
|
|
1682
|
+
columns.map((f) => /* @__PURE__ */ jsx12(
|
|
1683
|
+
"th",
|
|
992
1684
|
{
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
1685
|
+
style: { width: f.list.width, textAlign: f.list.align },
|
|
1686
|
+
"aria-sort": sort?.field === f.name ? sort.direction === "asc" ? "ascending" : "descending" : "none",
|
|
1687
|
+
children: f.sortable ? /* @__PURE__ */ jsxs10("button", { type: "button", className: "mst-th__button", onClick: () => toggleSort(f.name), children: [
|
|
1688
|
+
/* @__PURE__ */ jsx12("span", { className: "mst-th__label", children: f.label }),
|
|
1689
|
+
/* @__PURE__ */ jsx12(
|
|
1690
|
+
Icon,
|
|
1691
|
+
{
|
|
1692
|
+
name: sort?.field === f.name ? sort.direction === "asc" ? "sortAsc" : "sortDesc" : "sort",
|
|
1693
|
+
size: 11
|
|
1694
|
+
}
|
|
1695
|
+
)
|
|
1696
|
+
] }) : /* @__PURE__ */ jsx12("span", { className: "mst-th__label", children: f.label })
|
|
997
1697
|
},
|
|
998
1698
|
f.name
|
|
999
1699
|
)),
|
|
1000
|
-
/* @__PURE__ */
|
|
1001
|
-
] }
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1700
|
+
/* @__PURE__ */ jsx12("th", { style: { width: ACTIONS_COLUMN_WIDTH }, children: /* @__PURE__ */ jsx12("span", { className: "mst-sr-only", children: t.actions }) })
|
|
1701
|
+
] }) }),
|
|
1702
|
+
/* @__PURE__ */ jsx12("tbody", { children: data?.records.map((record, i) => {
|
|
1703
|
+
const id = String(record[entity.primaryKey]);
|
|
1704
|
+
return /* @__PURE__ */ jsxs10("tr", { children: [
|
|
1705
|
+
columns.map((f) => /* @__PURE__ */ jsx12(
|
|
1706
|
+
"td",
|
|
1707
|
+
{
|
|
1708
|
+
className: "mst-table__cell",
|
|
1709
|
+
style: { textAlign: f.list.align },
|
|
1710
|
+
title: f.label,
|
|
1711
|
+
onClick: (e) => setCellMenu({ anchor: { x: e.clientX, y: e.clientY }, field: f, record }),
|
|
1712
|
+
onDoubleClick: () => onOpen(id),
|
|
1713
|
+
children: /* @__PURE__ */ jsx12(FieldValue, { field: f, value: record[f.name], compact: true })
|
|
1714
|
+
},
|
|
1715
|
+
f.name
|
|
1716
|
+
)),
|
|
1717
|
+
/* @__PURE__ */ jsx12("td", { className: "mst-td--actions", children: /* @__PURE__ */ jsx12(MenuButton, { label: t.actions, items: rowMenuItems(record) }) })
|
|
1718
|
+
] }, id || i);
|
|
1719
|
+
}) })
|
|
1720
|
+
] }) }),
|
|
1721
|
+
data && /* @__PURE__ */ jsxs10("div", { className: "mst-pagination", children: [
|
|
1722
|
+
/* @__PURE__ */ jsxs10("span", { className: "mst-nums", children: [
|
|
1723
|
+
firstRow,
|
|
1724
|
+
"\u2013",
|
|
1725
|
+
lastRow
|
|
1726
|
+
] }),
|
|
1727
|
+
/* @__PURE__ */ jsx12("span", { children: t.records(data.total) }),
|
|
1728
|
+
totalPages > 1 && /* @__PURE__ */ jsxs10("span", { className: "mst-pagination__nums", children: [
|
|
1729
|
+
/* @__PURE__ */ jsx12("button", { type: "button", "aria-label": t.previous, disabled: page <= 1, onClick: () => setPage((p) => p - 1), children: /* @__PURE__ */ jsx12(Icon, { name: "chevronLeft", size: 13 }) }),
|
|
1730
|
+
pageWindow(page, totalPages).map((p) => /* @__PURE__ */ jsx12("button", { type: "button", "aria-current": p === page, onClick: () => setPage(p), children: p }, p)),
|
|
1731
|
+
/* @__PURE__ */ jsx12(
|
|
1732
|
+
"button",
|
|
1733
|
+
{
|
|
1734
|
+
type: "button",
|
|
1735
|
+
"aria-label": t.next,
|
|
1736
|
+
disabled: page >= totalPages,
|
|
1737
|
+
onClick: () => setPage((p) => p + 1),
|
|
1738
|
+
children: /* @__PURE__ */ jsx12(Icon, { name: "chevronRight", size: 13 })
|
|
1739
|
+
}
|
|
1740
|
+
)
|
|
1741
|
+
] })
|
|
1742
|
+
] })
|
|
1743
|
+
] }),
|
|
1744
|
+
cellMenu && /* @__PURE__ */ jsx12(PopMenu, { anchor: cellMenu.anchor, items: cellMenuItems(cellMenu), onClose: () => setCellMenu(void 0) }),
|
|
1745
|
+
columnMenu && /* @__PURE__ */ jsx12(PopMenu, { anchor: columnMenu, items: columnMenuItems, onClose: () => setColumnMenu(void 0) }),
|
|
1746
|
+
copied && /* @__PURE__ */ jsx12(Toast, { children: t.copied })
|
|
1011
1747
|
] });
|
|
1012
1748
|
}
|
|
1013
1749
|
|
|
1014
1750
|
// src/components/EntityDetail.tsx
|
|
1015
|
-
import { useState as
|
|
1016
|
-
import { jsx as
|
|
1751
|
+
import { useState as useState10 } from "react";
|
|
1752
|
+
import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1017
1753
|
function EntityDetail({
|
|
1018
1754
|
entity,
|
|
1019
1755
|
id,
|
|
@@ -1028,7 +1764,7 @@ function EntityDetail({
|
|
|
1028
1764
|
const entities = useEntities();
|
|
1029
1765
|
const { data, error, loading } = useEntityRecord(entity.id, id);
|
|
1030
1766
|
const fields = detailFields(entity);
|
|
1031
|
-
const [copied, setCopied] =
|
|
1767
|
+
const [copied, setCopied] = useState10(false);
|
|
1032
1768
|
const copy = async (text) => {
|
|
1033
1769
|
await copyText(text);
|
|
1034
1770
|
setCopied(true);
|
|
@@ -1041,71 +1777,72 @@ function EntityDetail({
|
|
|
1041
1777
|
};
|
|
1042
1778
|
const menuItems = [];
|
|
1043
1779
|
if (entity.capabilities.clone && entity.capabilities.create) {
|
|
1044
|
-
menuItems.push({ id: "clone", label:
|
|
1780
|
+
menuItems.push({ id: "clone", label: t.clone, icon: "clone", onSelect: onClone });
|
|
1045
1781
|
}
|
|
1046
|
-
menuItems.push({ id: "copy-id", label:
|
|
1782
|
+
menuItems.push({ id: "copy-id", label: t.copyId, icon: "copy", onSelect: () => void copy(id) });
|
|
1047
1783
|
if (data) {
|
|
1048
1784
|
menuItems.push({
|
|
1049
1785
|
id: "copy-object",
|
|
1050
|
-
label:
|
|
1786
|
+
label: t.copyObject,
|
|
1787
|
+
icon: "copy",
|
|
1051
1788
|
onSelect: () => void copy(JSON.stringify(data, null, 2))
|
|
1052
1789
|
});
|
|
1053
1790
|
}
|
|
1054
1791
|
if (entity.capabilities.delete) {
|
|
1055
|
-
menuItems.push({
|
|
1792
|
+
menuItems.push({
|
|
1793
|
+
id: "remove",
|
|
1794
|
+
label: t.remove,
|
|
1795
|
+
icon: "trash",
|
|
1796
|
+
danger: true,
|
|
1797
|
+
separated: true,
|
|
1798
|
+
onSelect: () => void onDelete()
|
|
1799
|
+
});
|
|
1056
1800
|
}
|
|
1057
|
-
return /* @__PURE__ */
|
|
1058
|
-
/* @__PURE__ */
|
|
1059
|
-
/* @__PURE__ */
|
|
1060
|
-
/* @__PURE__ */
|
|
1061
|
-
|
|
1801
|
+
return /* @__PURE__ */ jsxs11("section", { className: "mst-page", children: [
|
|
1802
|
+
/* @__PURE__ */ jsxs11("div", { className: "mst-pagehead", children: [
|
|
1803
|
+
/* @__PURE__ */ jsxs11("div", { children: [
|
|
1804
|
+
/* @__PURE__ */ jsxs11("button", { type: "button", className: "mst-backlink", onClick: onBack, children: [
|
|
1805
|
+
/* @__PURE__ */ jsx13(Icon, { name: "arrowLeft", size: 12 }),
|
|
1062
1806
|
entity.label.plural
|
|
1063
1807
|
] }),
|
|
1064
|
-
/* @__PURE__ */
|
|
1808
|
+
/* @__PURE__ */ jsxs11("h1", { className: "mst-pagehead__title", children: [
|
|
1065
1809
|
entity.label.singular,
|
|
1066
1810
|
" ",
|
|
1067
1811
|
data ? String(data[entity.displayField] ?? id) : ""
|
|
1068
1812
|
] })
|
|
1069
1813
|
] }),
|
|
1070
|
-
/* @__PURE__ */
|
|
1071
|
-
entity.capabilities.update && /* @__PURE__ */
|
|
1072
|
-
/* @__PURE__ */
|
|
1814
|
+
/* @__PURE__ */ jsxs11("div", { className: "mst-pagehead__actions", children: [
|
|
1815
|
+
entity.capabilities.update && /* @__PURE__ */ jsx13(Button, { variant: "primary", icon: "pencil", onClick: onEdit, children: t.edit }),
|
|
1816
|
+
/* @__PURE__ */ jsx13(MenuButton, { label: t.actions, items: menuItems })
|
|
1073
1817
|
] })
|
|
1074
1818
|
] }),
|
|
1075
|
-
error && /* @__PURE__ */
|
|
1076
|
-
loading && !data ? /* @__PURE__ */
|
|
1819
|
+
error && /* @__PURE__ */ jsx13(ErrorBanner, { error }),
|
|
1820
|
+
loading && !data ? /* @__PURE__ */ jsx13(Spinner, {}) : data && /* @__PURE__ */ jsx13("dl", { className: "mst-fields", children: fields.map((f) => {
|
|
1077
1821
|
const value = data[f.name];
|
|
1078
1822
|
const isRelation = f.type === "relation" && f.relationEntity && value !== null && value !== void 0;
|
|
1079
1823
|
const target = isRelation ? entities.find((e) => e.id === f.relationEntity) : void 0;
|
|
1080
|
-
return /* @__PURE__ */
|
|
1081
|
-
/* @__PURE__ */
|
|
1082
|
-
/* @__PURE__ */
|
|
1083
|
-
/* @__PURE__ */
|
|
1084
|
-
isRelation && /* @__PURE__ */
|
|
1085
|
-
|
|
1086
|
-
{
|
|
1087
|
-
|
|
1088
|
-
className: "text-xs text-indigo-600 hover:underline",
|
|
1089
|
-
children: [
|
|
1090
|
-
t.openRelated(target?.label.singular ?? f.relationEntity),
|
|
1091
|
-
" \u2197"
|
|
1092
|
-
]
|
|
1093
|
-
}
|
|
1094
|
-
)
|
|
1824
|
+
return /* @__PURE__ */ jsxs11("div", { className: "mst-fields__row", children: [
|
|
1825
|
+
/* @__PURE__ */ jsx13("dt", { className: "mst-fields__key", children: f.label }),
|
|
1826
|
+
/* @__PURE__ */ jsxs11("dd", { className: "mst-fields__value", children: [
|
|
1827
|
+
/* @__PURE__ */ jsx13(FieldValue, { field: f, value }),
|
|
1828
|
+
isRelation && /* @__PURE__ */ jsxs11("button", { type: "button", className: "mst-backlink", onClick: () => onNavigate(f.relationEntity, String(value)), children: [
|
|
1829
|
+
t.openRelated(target?.label.singular ?? f.relationEntity),
|
|
1830
|
+
/* @__PURE__ */ jsx13(Icon, { name: "arrowUpRight", size: 11 })
|
|
1831
|
+
] })
|
|
1095
1832
|
] })
|
|
1096
1833
|
] }, f.name);
|
|
1097
1834
|
}) }),
|
|
1098
|
-
copied && /* @__PURE__ */
|
|
1835
|
+
copied && /* @__PURE__ */ jsx13(Toast, { children: t.copied })
|
|
1099
1836
|
] });
|
|
1100
1837
|
}
|
|
1101
1838
|
|
|
1102
1839
|
// src/components/EntityForm.tsx
|
|
1103
|
-
import { useEffect as useEffect6, useState as
|
|
1840
|
+
import { useEffect as useEffect6, useState as useState12 } from "react";
|
|
1104
1841
|
|
|
1105
1842
|
// src/components/JsonInput.tsx
|
|
1106
|
-
import { useEffect as useEffect5, useRef as useRef3, useState as
|
|
1107
|
-
import { jsx as
|
|
1108
|
-
var leafInputClass = "
|
|
1843
|
+
import { useEffect as useEffect5, useRef as useRef3, useState as useState11 } from "react";
|
|
1844
|
+
import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1845
|
+
var leafInputClass = "mst-input";
|
|
1109
1846
|
var MAX_DEPTH2 = 3;
|
|
1110
1847
|
function RawJsonArea({
|
|
1111
1848
|
id,
|
|
@@ -1116,8 +1853,8 @@ function RawJsonArea({
|
|
|
1116
1853
|
placeholder
|
|
1117
1854
|
}) {
|
|
1118
1855
|
const t = useT();
|
|
1119
|
-
const [text, setText] =
|
|
1120
|
-
const [invalid, setInvalid] =
|
|
1856
|
+
const [text, setText] = useState11(() => value === null || value === void 0 ? "" : JSON.stringify(value, null, 2));
|
|
1857
|
+
const [invalid, setInvalid] = useState11(false);
|
|
1121
1858
|
const lastEmitted = useRef3(value);
|
|
1122
1859
|
useEffect5(() => {
|
|
1123
1860
|
if (value !== lastEmitted.current) {
|
|
@@ -1143,8 +1880,8 @@ function RawJsonArea({
|
|
|
1143
1880
|
setInvalid(true);
|
|
1144
1881
|
}
|
|
1145
1882
|
};
|
|
1146
|
-
return /* @__PURE__ */
|
|
1147
|
-
/* @__PURE__ */
|
|
1883
|
+
return /* @__PURE__ */ jsxs12("div", { className: "mst-field", children: [
|
|
1884
|
+
/* @__PURE__ */ jsx14(
|
|
1148
1885
|
"textarea",
|
|
1149
1886
|
{
|
|
1150
1887
|
id,
|
|
@@ -1154,10 +1891,10 @@ function RawJsonArea({
|
|
|
1154
1891
|
disabled,
|
|
1155
1892
|
onChange: (e) => onText(e.target.value),
|
|
1156
1893
|
"aria-invalid": invalid || void 0,
|
|
1157
|
-
className:
|
|
1894
|
+
className: `mst-textarea mst-mono${invalid ? " mst-input--invalid" : ""}`
|
|
1158
1895
|
}
|
|
1159
1896
|
),
|
|
1160
|
-
invalid && /* @__PURE__ */
|
|
1897
|
+
invalid && /* @__PURE__ */ jsx14("p", { role: "alert", className: "mst-help mst-help--warn", children: t.jsonInvalid })
|
|
1161
1898
|
] });
|
|
1162
1899
|
}
|
|
1163
1900
|
function LeafEditor({
|
|
@@ -1168,7 +1905,7 @@ function LeafEditor({
|
|
|
1168
1905
|
depth
|
|
1169
1906
|
}) {
|
|
1170
1907
|
if (typeof value === "boolean") {
|
|
1171
|
-
return /* @__PURE__ */
|
|
1908
|
+
return /* @__PURE__ */ jsx14(
|
|
1172
1909
|
"input",
|
|
1173
1910
|
{
|
|
1174
1911
|
id,
|
|
@@ -1176,12 +1913,12 @@ function LeafEditor({
|
|
|
1176
1913
|
checked: value,
|
|
1177
1914
|
disabled,
|
|
1178
1915
|
onChange: (e) => onChange(e.target.checked),
|
|
1179
|
-
className: "
|
|
1916
|
+
className: "mst-checkbox"
|
|
1180
1917
|
}
|
|
1181
1918
|
);
|
|
1182
1919
|
}
|
|
1183
1920
|
if (typeof value === "number") {
|
|
1184
|
-
return /* @__PURE__ */
|
|
1921
|
+
return /* @__PURE__ */ jsx14(
|
|
1185
1922
|
"input",
|
|
1186
1923
|
{
|
|
1187
1924
|
id,
|
|
@@ -1195,7 +1932,7 @@ function LeafEditor({
|
|
|
1195
1932
|
);
|
|
1196
1933
|
}
|
|
1197
1934
|
if (typeof value === "string") {
|
|
1198
|
-
return /* @__PURE__ */
|
|
1935
|
+
return /* @__PURE__ */ jsx14(
|
|
1199
1936
|
"input",
|
|
1200
1937
|
{
|
|
1201
1938
|
id,
|
|
@@ -1208,7 +1945,7 @@ function LeafEditor({
|
|
|
1208
1945
|
);
|
|
1209
1946
|
}
|
|
1210
1947
|
if (value === null || value === void 0) {
|
|
1211
|
-
return /* @__PURE__ */
|
|
1948
|
+
return /* @__PURE__ */ jsx14(
|
|
1212
1949
|
"input",
|
|
1213
1950
|
{
|
|
1214
1951
|
id,
|
|
@@ -1222,12 +1959,12 @@ function LeafEditor({
|
|
|
1222
1959
|
);
|
|
1223
1960
|
}
|
|
1224
1961
|
if (isPlainObject(value) && depth < MAX_DEPTH2) {
|
|
1225
|
-
return /* @__PURE__ */
|
|
1962
|
+
return /* @__PURE__ */ jsx14("div", { className: "mst-json__nest", children: /* @__PURE__ */ jsx14(ObjectEditor, { idPrefix: id, value, onChange, disabled, depth: depth + 1 }) });
|
|
1226
1963
|
}
|
|
1227
1964
|
if (Array.isArray(value) && depth < MAX_DEPTH2) {
|
|
1228
|
-
return /* @__PURE__ */
|
|
1965
|
+
return /* @__PURE__ */ jsx14("div", { className: "mst-json__nest", children: /* @__PURE__ */ jsx14(ArrayEditor, { idPrefix: id, value, onChange, disabled, depth: depth + 1 }) });
|
|
1229
1966
|
}
|
|
1230
|
-
return /* @__PURE__ */
|
|
1967
|
+
return /* @__PURE__ */ jsx14(RawJsonArea, { id, value, onChange, disabled, rows: 3 });
|
|
1231
1968
|
}
|
|
1232
1969
|
function ArrayEditor({
|
|
1233
1970
|
idPrefix,
|
|
@@ -1244,27 +1981,27 @@ function ArrayEditor({
|
|
|
1244
1981
|
const template = last === void 0 || last === null ? null : JSON.parse(JSON.stringify(last));
|
|
1245
1982
|
onChange([...value, template]);
|
|
1246
1983
|
};
|
|
1247
|
-
return /* @__PURE__ */
|
|
1984
|
+
return /* @__PURE__ */ jsxs12("div", { className: "mst-json__rows", children: [
|
|
1248
1985
|
value.map((item, i) => {
|
|
1249
1986
|
const id = `${idPrefix}-${i}`;
|
|
1250
|
-
return /* @__PURE__ */
|
|
1251
|
-
/* @__PURE__ */
|
|
1252
|
-
!disabled && /* @__PURE__ */
|
|
1987
|
+
return /* @__PURE__ */ jsxs12("div", { className: "mst-json__row", children: [
|
|
1988
|
+
/* @__PURE__ */ jsx14("label", { htmlFor: id, className: "mst-json__index", children: i }),
|
|
1989
|
+
!disabled && /* @__PURE__ */ jsx14(
|
|
1253
1990
|
"button",
|
|
1254
1991
|
{
|
|
1255
1992
|
type: "button",
|
|
1256
1993
|
title: t.removeItem,
|
|
1257
1994
|
"aria-label": `${t.removeItem} ${i}`,
|
|
1258
1995
|
onClick: () => removeAt(i),
|
|
1259
|
-
className: "
|
|
1996
|
+
className: "mst-json__remove",
|
|
1260
1997
|
children: "\u2715"
|
|
1261
1998
|
}
|
|
1262
1999
|
),
|
|
1263
|
-
/* @__PURE__ */
|
|
2000
|
+
/* @__PURE__ */ jsx14("div", { className: "mst-json__field", children: /* @__PURE__ */ jsx14(LeafEditor, { id, value: item, onChange: (next) => setAt(i, next), disabled, depth }) })
|
|
1264
2001
|
] }, i);
|
|
1265
2002
|
}),
|
|
1266
|
-
value.length === 0 && /* @__PURE__ */
|
|
1267
|
-
!disabled && /* @__PURE__ */
|
|
2003
|
+
value.length === 0 && /* @__PURE__ */ jsx14("p", { className: "mst-json__empty", children: t.emptyObject }),
|
|
2004
|
+
!disabled && /* @__PURE__ */ jsx14("div", { className: "mst-json__addrow", children: /* @__PURE__ */ jsxs12("button", { type: "button", onClick: append, className: "mst-backlink", children: [
|
|
1268
2005
|
"+ ",
|
|
1269
2006
|
t.addItem
|
|
1270
2007
|
] }) })
|
|
@@ -1280,16 +2017,16 @@ function ObjectEditor({
|
|
|
1280
2017
|
const t = useT();
|
|
1281
2018
|
const entries = Object.entries(value);
|
|
1282
2019
|
if (entries.length === 0) {
|
|
1283
|
-
return /* @__PURE__ */
|
|
2020
|
+
return /* @__PURE__ */ jsx14("p", { className: "mst-json__empty", children: t.emptyObject });
|
|
1284
2021
|
}
|
|
1285
|
-
return /* @__PURE__ */
|
|
2022
|
+
return /* @__PURE__ */ jsx14("div", { className: "mst-json__rows", children: entries.map(([key, entry]) => {
|
|
1286
2023
|
const id = `${idPrefix}-${key}`;
|
|
1287
2024
|
return (
|
|
1288
2025
|
// flex-wrap + basis: when the label column leaves less than ~16rem for the control (deeply
|
|
1289
2026
|
// nested groups), the control wraps to its own full-width line instead of squeezing.
|
|
1290
|
-
/* @__PURE__ */
|
|
1291
|
-
/* @__PURE__ */
|
|
1292
|
-
/* @__PURE__ */
|
|
2027
|
+
/* @__PURE__ */ jsxs12("div", { className: "mst-json__row", children: [
|
|
2028
|
+
/* @__PURE__ */ jsx14("label", { htmlFor: id, className: "mst-json__key", title: key, children: key }),
|
|
2029
|
+
/* @__PURE__ */ jsx14("div", { className: "mst-json__field", children: /* @__PURE__ */ jsx14(
|
|
1293
2030
|
LeafEditor,
|
|
1294
2031
|
{
|
|
1295
2032
|
id,
|
|
@@ -1310,8 +2047,8 @@ function JsonInput({
|
|
|
1310
2047
|
disabled
|
|
1311
2048
|
}) {
|
|
1312
2049
|
const t = useT();
|
|
1313
|
-
const [mode, setMode] =
|
|
1314
|
-
const [modeTouched, setModeTouched] =
|
|
2050
|
+
const [mode, setMode] = useState11(() => isPlainObject(value) ? "form" : "raw");
|
|
2051
|
+
const [modeTouched, setModeTouched] = useState11(false);
|
|
1315
2052
|
const structured = isPlainObject(value) || Array.isArray(value);
|
|
1316
2053
|
useEffect5(() => {
|
|
1317
2054
|
if (!modeTouched && structured) setMode("form");
|
|
@@ -1321,10 +2058,10 @@ function JsonInput({
|
|
|
1321
2058
|
setMode(next);
|
|
1322
2059
|
};
|
|
1323
2060
|
const showForm = mode === "form" && structured;
|
|
1324
|
-
const toggleClass = (active) => `
|
|
1325
|
-
return /* @__PURE__ */
|
|
1326
|
-
/* @__PURE__ */
|
|
1327
|
-
/* @__PURE__ */
|
|
2061
|
+
const toggleClass = (active) => `mst-json__tab${active ? " mst-json__tab--active" : ""}`;
|
|
2062
|
+
return /* @__PURE__ */ jsxs12("div", { className: "mst-json", children: [
|
|
2063
|
+
/* @__PURE__ */ jsxs12("div", { className: "mst-json__head", children: [
|
|
2064
|
+
/* @__PURE__ */ jsx14(
|
|
1328
2065
|
"button",
|
|
1329
2066
|
{
|
|
1330
2067
|
type: "button",
|
|
@@ -1334,9 +2071,9 @@ function JsonInput({
|
|
|
1334
2071
|
children: t.jsonFormMode
|
|
1335
2072
|
}
|
|
1336
2073
|
),
|
|
1337
|
-
/* @__PURE__ */
|
|
2074
|
+
/* @__PURE__ */ jsx14("button", { type: "button", className: toggleClass(!showForm), onClick: () => pick("raw"), children: t.jsonRawMode })
|
|
1338
2075
|
] }),
|
|
1339
|
-
/* @__PURE__ */
|
|
2076
|
+
/* @__PURE__ */ jsx14("div", { className: "mst-json__body", children: showForm ? Array.isArray(value) ? /* @__PURE__ */ jsx14(ArrayEditor, { idPrefix: `field-${field.name}`, value, onChange, disabled }) : /* @__PURE__ */ jsx14(
|
|
1340
2077
|
ObjectEditor,
|
|
1341
2078
|
{
|
|
1342
2079
|
idPrefix: `field-${field.name}`,
|
|
@@ -1344,7 +2081,7 @@ function JsonInput({
|
|
|
1344
2081
|
onChange,
|
|
1345
2082
|
disabled
|
|
1346
2083
|
}
|
|
1347
|
-
) : /* @__PURE__ */
|
|
2084
|
+
) : /* @__PURE__ */ jsx14(
|
|
1348
2085
|
RawJsonArea,
|
|
1349
2086
|
{
|
|
1350
2087
|
id: `field-${field.name}`,
|
|
@@ -1358,8 +2095,8 @@ function JsonInput({
|
|
|
1358
2095
|
}
|
|
1359
2096
|
|
|
1360
2097
|
// src/components/FieldInput.tsx
|
|
1361
|
-
import { jsx as
|
|
1362
|
-
var inputClass = "
|
|
2098
|
+
import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
2099
|
+
var inputClass = "mst-input";
|
|
1363
2100
|
function FieldInput({
|
|
1364
2101
|
field,
|
|
1365
2102
|
value,
|
|
@@ -1369,7 +2106,7 @@ function FieldInput({
|
|
|
1369
2106
|
const id = `field-${field.name}`;
|
|
1370
2107
|
switch (field.type) {
|
|
1371
2108
|
case "boolean":
|
|
1372
|
-
return /* @__PURE__ */
|
|
2109
|
+
return /* @__PURE__ */ jsx15(
|
|
1373
2110
|
"input",
|
|
1374
2111
|
{
|
|
1375
2112
|
id,
|
|
@@ -1377,7 +2114,7 @@ function FieldInput({
|
|
|
1377
2114
|
checked: Boolean(value),
|
|
1378
2115
|
disabled,
|
|
1379
2116
|
onChange: (e) => onChange(e.target.checked),
|
|
1380
|
-
className: "
|
|
2117
|
+
className: "mst-checkbox"
|
|
1381
2118
|
}
|
|
1382
2119
|
);
|
|
1383
2120
|
case "integer":
|
|
@@ -1385,7 +2122,7 @@ function FieldInput({
|
|
|
1385
2122
|
case "float":
|
|
1386
2123
|
case "decimal":
|
|
1387
2124
|
case "currency":
|
|
1388
|
-
return /* @__PURE__ */
|
|
2125
|
+
return /* @__PURE__ */ jsx15(
|
|
1389
2126
|
"input",
|
|
1390
2127
|
{
|
|
1391
2128
|
id,
|
|
@@ -1399,22 +2136,22 @@ function FieldInput({
|
|
|
1399
2136
|
}
|
|
1400
2137
|
);
|
|
1401
2138
|
case "enum":
|
|
1402
|
-
return /* @__PURE__ */
|
|
2139
|
+
return /* @__PURE__ */ jsxs13(
|
|
1403
2140
|
"select",
|
|
1404
2141
|
{
|
|
1405
2142
|
id,
|
|
1406
2143
|
value: value === null || value === void 0 ? "" : String(value),
|
|
1407
2144
|
disabled,
|
|
1408
2145
|
onChange: (e) => onChange(e.target.value),
|
|
1409
|
-
className:
|
|
2146
|
+
className: "mst-select",
|
|
1410
2147
|
children: [
|
|
1411
|
-
/* @__PURE__ */
|
|
1412
|
-
field.enumOptions?.map((o) => /* @__PURE__ */
|
|
2148
|
+
/* @__PURE__ */ jsx15("option", { value: "", children: "\u2014" }),
|
|
2149
|
+
field.enumOptions?.map((o) => /* @__PURE__ */ jsx15("option", { value: String(o.value), children: o.label }, String(o.value)))
|
|
1413
2150
|
]
|
|
1414
2151
|
}
|
|
1415
2152
|
);
|
|
1416
2153
|
case "date":
|
|
1417
|
-
return /* @__PURE__ */
|
|
2154
|
+
return /* @__PURE__ */ jsx15(
|
|
1418
2155
|
"input",
|
|
1419
2156
|
{
|
|
1420
2157
|
id,
|
|
@@ -1426,9 +2163,9 @@ function FieldInput({
|
|
|
1426
2163
|
}
|
|
1427
2164
|
);
|
|
1428
2165
|
case "json":
|
|
1429
|
-
return /* @__PURE__ */
|
|
2166
|
+
return /* @__PURE__ */ jsx15(JsonInput, { field, value, onChange, disabled });
|
|
1430
2167
|
case "text":
|
|
1431
|
-
return /* @__PURE__ */
|
|
2168
|
+
return /* @__PURE__ */ jsx15(
|
|
1432
2169
|
"textarea",
|
|
1433
2170
|
{
|
|
1434
2171
|
id,
|
|
@@ -1437,11 +2174,11 @@ function FieldInput({
|
|
|
1437
2174
|
placeholder: field.form.placeholder,
|
|
1438
2175
|
disabled,
|
|
1439
2176
|
onChange: (e) => onChange(e.target.value),
|
|
1440
|
-
className:
|
|
2177
|
+
className: "mst-textarea"
|
|
1441
2178
|
}
|
|
1442
2179
|
);
|
|
1443
2180
|
default:
|
|
1444
|
-
return /* @__PURE__ */
|
|
2181
|
+
return /* @__PURE__ */ jsx15(
|
|
1445
2182
|
"input",
|
|
1446
2183
|
{
|
|
1447
2184
|
id,
|
|
@@ -1457,7 +2194,7 @@ function FieldInput({
|
|
|
1457
2194
|
}
|
|
1458
2195
|
|
|
1459
2196
|
// src/components/EntityForm.tsx
|
|
1460
|
-
import { jsx as
|
|
2197
|
+
import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1461
2198
|
function EntityForm({
|
|
1462
2199
|
entity,
|
|
1463
2200
|
mode,
|
|
@@ -1471,9 +2208,9 @@ function EntityForm({
|
|
|
1471
2208
|
const fields = formFields(entity, mode);
|
|
1472
2209
|
const sourceId = mode === "edit" ? id : cloneFromId;
|
|
1473
2210
|
const existing = useEntityRecord(entity.id, sourceId);
|
|
1474
|
-
const [values, setValues] =
|
|
1475
|
-
const [saving, setSaving] =
|
|
1476
|
-
const [error, setError] =
|
|
2211
|
+
const [values, setValues] = useState12({});
|
|
2212
|
+
const [saving, setSaving] = useState12(false);
|
|
2213
|
+
const [error, setError] = useState12(void 0);
|
|
1477
2214
|
useEffect6(() => {
|
|
1478
2215
|
if (!existing.data || !sourceId) return;
|
|
1479
2216
|
if (mode === "edit") {
|
|
@@ -1506,25 +2243,25 @@ function EntityForm({
|
|
|
1506
2243
|
}
|
|
1507
2244
|
};
|
|
1508
2245
|
if (sourceId && existing.loading && !existing.data) {
|
|
1509
|
-
return /* @__PURE__ */
|
|
2246
|
+
return /* @__PURE__ */ jsx16(Spinner, {});
|
|
1510
2247
|
}
|
|
1511
2248
|
const title = mode === "edit" ? t.editTitle(entity.label.singular) : cloneFromId ? t.cloneTitle(entity.label.singular) : t.newTitle(entity.label.singular);
|
|
1512
|
-
return /* @__PURE__ */
|
|
1513
|
-
/* @__PURE__ */
|
|
1514
|
-
/* @__PURE__ */
|
|
1515
|
-
|
|
2249
|
+
return /* @__PURE__ */ jsxs14("section", { className: "mst-page", children: [
|
|
2250
|
+
/* @__PURE__ */ jsx16("div", { className: "mst-pagehead", children: /* @__PURE__ */ jsxs14("div", { children: [
|
|
2251
|
+
/* @__PURE__ */ jsxs14("button", { type: "button", className: "mst-backlink", onClick: onCancel, children: [
|
|
2252
|
+
/* @__PURE__ */ jsx16(Icon, { name: "arrowLeft", size: 12 }),
|
|
1516
2253
|
entity.label.plural
|
|
1517
2254
|
] }),
|
|
1518
|
-
/* @__PURE__ */
|
|
1519
|
-
] }),
|
|
1520
|
-
error && /* @__PURE__ */
|
|
1521
|
-
/* @__PURE__ */
|
|
1522
|
-
fields.map((f) => /* @__PURE__ */
|
|
1523
|
-
/* @__PURE__ */
|
|
2255
|
+
/* @__PURE__ */ jsx16("h1", { className: "mst-pagehead__title", children: title })
|
|
2256
|
+
] }) }),
|
|
2257
|
+
error && /* @__PURE__ */ jsx16(ErrorBanner, { error }),
|
|
2258
|
+
/* @__PURE__ */ jsxs14("form", { onSubmit: submit, className: "mst-card mst-form", children: [
|
|
2259
|
+
fields.map((f) => /* @__PURE__ */ jsxs14("div", { className: "mst-field", children: [
|
|
2260
|
+
/* @__PURE__ */ jsxs14("label", { htmlFor: `field-${f.name}`, className: "mst-label", children: [
|
|
1524
2261
|
f.label,
|
|
1525
|
-
f.required && /* @__PURE__ */
|
|
2262
|
+
f.required && /* @__PURE__ */ jsx16("span", { className: "mst-label__req", children: "*" })
|
|
1526
2263
|
] }),
|
|
1527
|
-
/* @__PURE__ */
|
|
2264
|
+
/* @__PURE__ */ jsx16(
|
|
1528
2265
|
FieldInput,
|
|
1529
2266
|
{
|
|
1530
2267
|
field: f,
|
|
@@ -1533,11 +2270,11 @@ function EntityForm({
|
|
|
1533
2270
|
onChange: (next) => setValues((v) => ({ ...v, [f.name]: next }))
|
|
1534
2271
|
}
|
|
1535
2272
|
),
|
|
1536
|
-
f.form.helpText && /* @__PURE__ */
|
|
2273
|
+
f.form.helpText && /* @__PURE__ */ jsx16("p", { className: "mst-help", children: f.form.helpText })
|
|
1537
2274
|
] }, f.name)),
|
|
1538
|
-
/* @__PURE__ */
|
|
1539
|
-
/* @__PURE__ */
|
|
1540
|
-
/* @__PURE__ */
|
|
2275
|
+
/* @__PURE__ */ jsxs14("div", { className: "mst-form__foot", children: [
|
|
2276
|
+
/* @__PURE__ */ jsx16(Button, { type: "button", variant: "secondary", onClick: onCancel, disabled: saving, children: t.cancel }),
|
|
2277
|
+
/* @__PURE__ */ jsx16(Button, { type: "submit", variant: "primary", disabled: saving, children: saving ? t.saving : mode === "create" ? t.create : t.save })
|
|
1541
2278
|
] })
|
|
1542
2279
|
] })
|
|
1543
2280
|
] });
|
|
@@ -1566,10 +2303,10 @@ function applyLabels(metadata, labels) {
|
|
|
1566
2303
|
}
|
|
1567
2304
|
|
|
1568
2305
|
// src/recents.ts
|
|
1569
|
-
import { useCallback as
|
|
1570
|
-
var
|
|
2306
|
+
import { useCallback as useCallback4, useState as useState13 } from "react";
|
|
2307
|
+
var STORAGE_KEY2 = "maestro-admin:recents";
|
|
1571
2308
|
var MAX_RECENTS = 8;
|
|
1572
|
-
function
|
|
2309
|
+
function read2(key) {
|
|
1573
2310
|
try {
|
|
1574
2311
|
const raw = globalThis.localStorage?.getItem(key);
|
|
1575
2312
|
const parsed = raw ? JSON.parse(raw) : [];
|
|
@@ -1578,20 +2315,20 @@ function read(key) {
|
|
|
1578
2315
|
return [];
|
|
1579
2316
|
}
|
|
1580
2317
|
}
|
|
1581
|
-
function
|
|
2318
|
+
function write2(key, ids) {
|
|
1582
2319
|
try {
|
|
1583
2320
|
globalThis.localStorage?.setItem(key, JSON.stringify(ids));
|
|
1584
2321
|
} catch {
|
|
1585
2322
|
}
|
|
1586
2323
|
}
|
|
1587
2324
|
function useRecents(scope) {
|
|
1588
|
-
const key = scope ? `${
|
|
1589
|
-
const [recents, setRecents] =
|
|
1590
|
-
const push =
|
|
2325
|
+
const key = scope ? `${STORAGE_KEY2}:${scope}` : STORAGE_KEY2;
|
|
2326
|
+
const [recents, setRecents] = useState13(() => read2(key));
|
|
2327
|
+
const push = useCallback4(
|
|
1591
2328
|
(entityId) => {
|
|
1592
2329
|
setRecents((prev) => {
|
|
1593
2330
|
const next = [entityId, ...prev.filter((id) => id !== entityId)].slice(0, MAX_RECENTS);
|
|
1594
|
-
|
|
2331
|
+
write2(key, next);
|
|
1595
2332
|
return next;
|
|
1596
2333
|
});
|
|
1597
2334
|
},
|
|
@@ -1601,30 +2338,81 @@ function useRecents(scope) {
|
|
|
1601
2338
|
}
|
|
1602
2339
|
|
|
1603
2340
|
// src/components/MaestroAdmin.tsx
|
|
1604
|
-
import { jsx as
|
|
2341
|
+
import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1605
2342
|
function AdminShell({ title, labels }) {
|
|
1606
2343
|
const { data, error, loading } = useMetadata();
|
|
1607
2344
|
const t = useT();
|
|
1608
|
-
const [view, setView] =
|
|
2345
|
+
const [view, setView] = useState14({ kind: "hub" });
|
|
2346
|
+
const [paletteOpen, setPaletteOpen] = useState14(false);
|
|
1609
2347
|
const { recents, push: pushRecent } = useRecents();
|
|
1610
|
-
const metadata =
|
|
1611
|
-
const entities = metadata?.entities ?? [];
|
|
1612
|
-
const activeId = view
|
|
2348
|
+
const metadata = useMemo7(() => data ? applyLabels(data, labels) : void 0, [data, labels]);
|
|
2349
|
+
const entities = useMemo7(() => metadata?.entities ?? [], [metadata]);
|
|
2350
|
+
const activeId = view.kind === "hub" ? void 0 : view.entityId;
|
|
1613
2351
|
const activeEntity = entities.find((e) => e.id === activeId);
|
|
1614
|
-
const
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
2352
|
+
const groupOf = useMemo7(() => {
|
|
2353
|
+
const map = /* @__PURE__ */ new Map();
|
|
2354
|
+
for (const group of buildMenu(entities, labels).groups) {
|
|
2355
|
+
for (const entity of group.entities) map.set(entity.id, group.label);
|
|
2356
|
+
}
|
|
2357
|
+
return map;
|
|
2358
|
+
}, [entities, labels]);
|
|
2359
|
+
const openEntity = useCallback5(
|
|
2360
|
+
(entityId) => {
|
|
2361
|
+
pushRecent(entityId);
|
|
2362
|
+
setView({ kind: "list", entityId });
|
|
2363
|
+
},
|
|
2364
|
+
[pushRecent]
|
|
2365
|
+
);
|
|
2366
|
+
const navigateToRecord = useCallback5(
|
|
2367
|
+
(entityId, id) => {
|
|
2368
|
+
pushRecent(entityId);
|
|
2369
|
+
setView({ kind: "detail", entityId, id });
|
|
2370
|
+
},
|
|
2371
|
+
[pushRecent]
|
|
2372
|
+
);
|
|
2373
|
+
useEffect7(() => {
|
|
2374
|
+
const onKey = (e) => {
|
|
2375
|
+
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "k") {
|
|
2376
|
+
e.preventDefault();
|
|
2377
|
+
setPaletteOpen((open) => !open);
|
|
2378
|
+
}
|
|
2379
|
+
};
|
|
2380
|
+
document.addEventListener("keydown", onKey);
|
|
2381
|
+
return () => document.removeEventListener("keydown", onKey);
|
|
2382
|
+
}, []);
|
|
2383
|
+
const crumbs = useMemo7(() => {
|
|
2384
|
+
const home = { label: t.hub, icon: "hub", onClick: view.kind === "hub" ? void 0 : () => setView({ kind: "hub" }) };
|
|
2385
|
+
if (view.kind === "hub" || !activeEntity) return [home];
|
|
2386
|
+
const entityCrumb = {
|
|
2387
|
+
label: activeEntity.label.plural,
|
|
2388
|
+
icon: entityIcon(activeEntity),
|
|
2389
|
+
onClick: view.kind === "list" ? void 0 : () => setView({ kind: "list", entityId: activeEntity.id })
|
|
2390
|
+
};
|
|
2391
|
+
if (view.kind === "list") return [home, entityCrumb];
|
|
2392
|
+
const leaf = view.kind === "detail" ? { label: view.id } : view.kind === "edit" ? { label: t.editTitle(activeEntity.label.singular) } : { label: view.cloneFromId ? t.cloneTitle(activeEntity.label.singular) : t.newTitle(activeEntity.label.singular) };
|
|
2393
|
+
return [home, entityCrumb, leaf];
|
|
2394
|
+
}, [view, activeEntity, t]);
|
|
2395
|
+
const paletteSections = useMemo7(() => {
|
|
2396
|
+
const goTo = entityCommands(entities, groupOf, openEntity);
|
|
2397
|
+
goTo.unshift({ id: "hub", label: t.hub, icon: "hub", run: () => setView({ kind: "hub" }) });
|
|
2398
|
+
const screen = [];
|
|
2399
|
+
if (activeEntity?.capabilities.create) {
|
|
2400
|
+
screen.push({
|
|
2401
|
+
id: "create",
|
|
2402
|
+
label: t.newTitle(activeEntity.label.singular),
|
|
2403
|
+
icon: "plus",
|
|
2404
|
+
run: () => setView({ kind: "create", entityId: activeEntity.id })
|
|
2405
|
+
});
|
|
2406
|
+
}
|
|
2407
|
+
const sections = [{ id: "goto", label: t.goTo, commands: goTo }];
|
|
2408
|
+
if (screen.length > 0) sections.unshift({ id: "screen", label: t.onThisScreen, commands: screen });
|
|
2409
|
+
return sections;
|
|
2410
|
+
}, [entities, groupOf, openEntity, activeEntity, t]);
|
|
1623
2411
|
if (loading && !data) {
|
|
1624
|
-
return /* @__PURE__ */
|
|
2412
|
+
return /* @__PURE__ */ jsx17("div", { className: "mst-boot", children: /* @__PURE__ */ jsx17(Spinner, { label: t.loadingMetadata }) });
|
|
1625
2413
|
}
|
|
1626
|
-
return /* @__PURE__ */
|
|
1627
|
-
/* @__PURE__ */
|
|
2414
|
+
return /* @__PURE__ */ jsxs15(AdminEntitiesProvider, { value: entities, children: [
|
|
2415
|
+
/* @__PURE__ */ jsx17(
|
|
1628
2416
|
Sidebar,
|
|
1629
2417
|
{
|
|
1630
2418
|
entities,
|
|
@@ -1632,83 +2420,111 @@ function AdminShell({ title, labels }) {
|
|
|
1632
2420
|
title,
|
|
1633
2421
|
labels,
|
|
1634
2422
|
recents,
|
|
1635
|
-
onSelect: openEntity
|
|
2423
|
+
onSelect: openEntity,
|
|
2424
|
+
onHome: () => setView({ kind: "hub" }),
|
|
2425
|
+
homeActive: view.kind === "hub"
|
|
1636
2426
|
}
|
|
1637
2427
|
),
|
|
1638
|
-
/* @__PURE__ */
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
{
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
2428
|
+
/* @__PURE__ */ jsxs15("div", { className: "mst-main", children: [
|
|
2429
|
+
/* @__PURE__ */ jsx17(Header, { crumbs, onOpenPalette: () => setPaletteOpen(true) }),
|
|
2430
|
+
/* @__PURE__ */ jsxs15("div", { className: "mst-content", children: [
|
|
2431
|
+
error && /* @__PURE__ */ jsx17(ErrorBanner, { error }),
|
|
2432
|
+
view.kind === "hub" && /* @__PURE__ */ jsx17(Hub, { entities, labels, onOpen: openEntity }),
|
|
2433
|
+
activeEntity && view.kind === "list" && /* @__PURE__ */ jsx17(
|
|
2434
|
+
EntityList,
|
|
2435
|
+
{
|
|
2436
|
+
entity: activeEntity,
|
|
2437
|
+
onOpen: (id) => setView({ kind: "detail", entityId: activeEntity.id, id }),
|
|
2438
|
+
onCreate: () => setView({ kind: "create", entityId: activeEntity.id }),
|
|
2439
|
+
onEdit: (id) => setView({ kind: "edit", entityId: activeEntity.id, id }),
|
|
2440
|
+
onClone: (id) => setView({ kind: "create", entityId: activeEntity.id, cloneFromId: id }),
|
|
2441
|
+
onNavigate: navigateToRecord
|
|
2442
|
+
}
|
|
2443
|
+
),
|
|
2444
|
+
activeEntity && view.kind === "detail" && /* @__PURE__ */ jsx17(
|
|
2445
|
+
EntityDetail,
|
|
2446
|
+
{
|
|
2447
|
+
entity: activeEntity,
|
|
2448
|
+
id: view.id,
|
|
2449
|
+
onBack: () => setView({ kind: "list", entityId: activeEntity.id }),
|
|
2450
|
+
onEdit: () => setView({ kind: "edit", entityId: activeEntity.id, id: view.id }),
|
|
2451
|
+
onClone: () => setView({ kind: "create", entityId: activeEntity.id, cloneFromId: view.id }),
|
|
2452
|
+
onDeleted: () => setView({ kind: "list", entityId: activeEntity.id }),
|
|
2453
|
+
onNavigate: navigateToRecord
|
|
2454
|
+
}
|
|
2455
|
+
),
|
|
2456
|
+
activeEntity && (view.kind === "create" || view.kind === "edit") && /* @__PURE__ */ jsx17(
|
|
2457
|
+
EntityForm,
|
|
2458
|
+
{
|
|
2459
|
+
entity: activeEntity,
|
|
2460
|
+
mode: view.kind,
|
|
2461
|
+
id: view.kind === "edit" ? view.id : void 0,
|
|
2462
|
+
cloneFromId: view.kind === "create" ? view.cloneFromId : void 0,
|
|
2463
|
+
onCancel: () => setView({ kind: "list", entityId: activeEntity.id }),
|
|
2464
|
+
onSaved: () => setView({ kind: "list", entityId: activeEntity.id })
|
|
2465
|
+
}
|
|
2466
|
+
)
|
|
2467
|
+
] })
|
|
2468
|
+
] }),
|
|
2469
|
+
paletteOpen && /* @__PURE__ */ jsx17(CommandPalette, { sections: paletteSections, onClose: () => setPaletteOpen(false) })
|
|
2470
|
+
] });
|
|
1676
2471
|
}
|
|
1677
2472
|
function MaestroAdmin(props) {
|
|
1678
|
-
const client =
|
|
2473
|
+
const client = useMemo7(
|
|
1679
2474
|
() => props.client ?? new MaestroClient({ apiUrl: props.apiUrl ?? "", headers: props.headers }),
|
|
1680
2475
|
[props.client, props.apiUrl, props.headers]
|
|
1681
2476
|
);
|
|
1682
|
-
const strings =
|
|
1683
|
-
|
|
2477
|
+
const strings = useMemo7(() => stringsFor(props.locale ?? "en"), [props.locale]);
|
|
2478
|
+
const [root, setRoot] = useState14(null);
|
|
2479
|
+
return /* @__PURE__ */ jsx17(AdminClientProvider, { value: client, children: /* @__PURE__ */ jsx17(I18nProvider, { value: strings, children: /* @__PURE__ */ jsx17(MaestroThemeProvider, { theme: props.theme, className: "mst-root", rootRef: setRoot, children: /* @__PURE__ */ jsx17(AdminRootProvider, { value: root, children: /* @__PURE__ */ jsx17(AdminShell, { title: props.title ?? "Maestro Admin", labels: props.labels }) }) }) }) });
|
|
1684
2480
|
}
|
|
1685
2481
|
export {
|
|
1686
2482
|
AdminClientProvider,
|
|
2483
|
+
Badge,
|
|
2484
|
+
Button,
|
|
2485
|
+
CommandPalette,
|
|
2486
|
+
EmptyState,
|
|
1687
2487
|
EntityDetail,
|
|
1688
2488
|
EntityForm,
|
|
1689
2489
|
EntityList,
|
|
2490
|
+
ErrorBanner,
|
|
1690
2491
|
FieldInput,
|
|
1691
2492
|
FieldValue,
|
|
2493
|
+
Header,
|
|
2494
|
+
Hub,
|
|
1692
2495
|
I18nProvider,
|
|
2496
|
+
Icon,
|
|
2497
|
+
IconButton,
|
|
1693
2498
|
JsonInput,
|
|
2499
|
+
MAESTRO_THEMES,
|
|
1694
2500
|
MaestroAdmin,
|
|
1695
2501
|
MaestroApiError,
|
|
1696
2502
|
MaestroClient,
|
|
2503
|
+
MaestroThemeProvider,
|
|
1697
2504
|
ObjectValue,
|
|
1698
2505
|
Sidebar,
|
|
2506
|
+
Spinner,
|
|
2507
|
+
Toast,
|
|
1699
2508
|
applyLabels,
|
|
1700
2509
|
buildMenu,
|
|
1701
2510
|
detailFields,
|
|
2511
|
+
entityCommands,
|
|
2512
|
+
entityIcon,
|
|
1702
2513
|
formFields,
|
|
1703
2514
|
listFields,
|
|
1704
2515
|
primaryListFields,
|
|
1705
2516
|
stringsFor,
|
|
2517
|
+
useAdminRoot,
|
|
1706
2518
|
useAsync,
|
|
1707
2519
|
useClient,
|
|
2520
|
+
useColumnPrefs,
|
|
2521
|
+
useEntities,
|
|
2522
|
+
useEntity,
|
|
1708
2523
|
useEntityList,
|
|
1709
2524
|
useEntityRecord,
|
|
1710
2525
|
useMetadata,
|
|
1711
2526
|
useRecents,
|
|
1712
|
-
useT
|
|
2527
|
+
useT,
|
|
2528
|
+
useTheme
|
|
1713
2529
|
};
|
|
1714
2530
|
//# sourceMappingURL=index.js.map
|