@asteby/metacore-runtime-react 46.4.0 → 47.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/action-modal-dispatcher.d.ts +10 -1
  3. package/dist/action-modal-dispatcher.d.ts.map +1 -1
  4. package/dist/action-modal-dispatcher.js +107 -19
  5. package/dist/business/catalog.d.ts.map +1 -1
  6. package/dist/business/catalog.js +24 -0
  7. package/dist/business/index.d.ts +3 -0
  8. package/dist/business/index.d.ts.map +1 -1
  9. package/dist/business/index.js +3 -0
  10. package/dist/business/print-send-dialog.d.ts +27 -0
  11. package/dist/business/print-send-dialog.d.ts.map +1 -0
  12. package/dist/business/print-send-dialog.js +55 -0
  13. package/dist/business/relate-documents.d.ts +41 -0
  14. package/dist/business/relate-documents.d.ts.map +1 -0
  15. package/dist/business/relate-documents.js +42 -0
  16. package/dist/business/vehicle-picker.d.ts +36 -0
  17. package/dist/business/vehicle-picker.d.ts.map +1 -0
  18. package/dist/business/vehicle-picker.js +61 -0
  19. package/dist/cfdi-stamp-panel.d.ts +29 -0
  20. package/dist/cfdi-stamp-panel.d.ts.map +1 -0
  21. package/dist/cfdi-stamp-panel.js +53 -0
  22. package/dist/dialogs/dynamic-record.d.ts.map +1 -1
  23. package/dist/dialogs/dynamic-record.js +5 -0
  24. package/dist/document-form-dialog.d.ts +23 -0
  25. package/dist/document-form-dialog.d.ts.map +1 -0
  26. package/dist/document-form-dialog.js +196 -0
  27. package/dist/dynamic-crud-page.d.ts +6 -0
  28. package/dist/dynamic-crud-page.d.ts.map +1 -1
  29. package/dist/dynamic-crud-page.js +4 -2
  30. package/dist/dynamic-kanban.d.ts.map +1 -1
  31. package/dist/dynamic-kanban.js +3 -1
  32. package/dist/dynamic-table.d.ts.map +1 -1
  33. package/dist/dynamic-table.js +6 -2
  34. package/dist/index.d.ts +5 -0
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +5 -0
  37. package/dist/nav-permissions.d.ts +19 -0
  38. package/dist/nav-permissions.d.ts.map +1 -0
  39. package/dist/nav-permissions.js +51 -0
  40. package/dist/navigation-builder.d.ts +6 -0
  41. package/dist/navigation-builder.d.ts.map +1 -1
  42. package/dist/navigation-builder.js +26 -1
  43. package/dist/permissions-manager.d.ts +9 -0
  44. package/dist/permissions-manager.d.ts.map +1 -1
  45. package/dist/permissions-manager.js +50 -17
  46. package/dist/print-document-button.d.ts.map +1 -1
  47. package/dist/print-document-button.js +9 -1
  48. package/dist/record-mutation-events.d.ts +14 -0
  49. package/dist/record-mutation-events.d.ts.map +1 -0
  50. package/dist/record-mutation-events.js +37 -0
  51. package/dist/types.d.ts +46 -0
  52. package/dist/types.d.ts.map +1 -1
  53. package/dist/use-print-document.d.ts +15 -1
  54. package/dist/use-print-document.d.ts.map +1 -1
  55. package/dist/use-print-document.js +52 -1
  56. package/dist/use-record-mutation-tick.d.ts +6 -0
  57. package/dist/use-record-mutation-tick.d.ts.map +1 -0
  58. package/dist/use-record-mutation-tick.js +11 -0
  59. package/package.json +3 -3
  60. package/src/__tests__/action-modal-feedback.test.tsx +116 -0
  61. package/src/__tests__/business-pickers.test.tsx +157 -0
  62. package/src/__tests__/document-form-dialog.test.tsx +190 -0
  63. package/src/__tests__/dynamic-table-mutation-refresh.test.tsx +116 -0
  64. package/src/__tests__/nav-permissions.test.tsx +76 -0
  65. package/src/__tests__/permissions-manager.test.tsx +21 -4
  66. package/src/__tests__/use-print-document-feedback.test.tsx +58 -0
  67. package/src/action-modal-dispatcher.tsx +124 -6
  68. package/src/business/catalog.ts +24 -0
  69. package/src/business/index.ts +3 -0
  70. package/src/business/print-send-dialog.tsx +175 -0
  71. package/src/business/relate-documents.tsx +182 -0
  72. package/src/business/vehicle-picker.tsx +188 -0
  73. package/src/cfdi-stamp-panel.tsx +146 -0
  74. package/src/dialogs/dynamic-record.tsx +5 -0
  75. package/src/document-form-dialog.tsx +357 -0
  76. package/src/dynamic-crud-page.tsx +21 -2
  77. package/src/dynamic-kanban.tsx +3 -1
  78. package/src/dynamic-table.tsx +6 -2
  79. package/src/index.ts +28 -0
  80. package/src/nav-permissions.ts +58 -0
  81. package/src/navigation-builder.tsx +27 -1
  82. package/src/permissions-manager.tsx +39 -26
  83. package/src/print-document-button.tsx +9 -1
  84. package/src/record-mutation-events.ts +50 -0
  85. package/src/types.ts +49 -0
  86. package/src/use-print-document.ts +66 -3
  87. package/src/use-record-mutation-tick.ts +12 -0
@@ -0,0 +1,36 @@
1
+ export interface VehicleResult {
2
+ id: string;
3
+ /** Placa tal como la capturó el taller (sin formato hardcodeado por país). */
4
+ plate?: string;
5
+ vin?: string;
6
+ make?: string;
7
+ model?: string;
8
+ year?: number | string;
9
+ color?: string;
10
+ /** Propietario, cuando el host lo expone. */
11
+ customer_id?: string;
12
+ customer_name?: string;
13
+ }
14
+ export interface VehiclePickerProps {
15
+ value: VehicleResult | null;
16
+ /** Evento único de cambio: vehículo elegido/creado, o `null` al limpiar. */
17
+ onChange: (vehicle: VehicleResult | null) => void;
18
+ /** Búsqueda por placa o VIN. Estable (useCallback). */
19
+ search: (q: string, signal: AbortSignal) => Promise<VehicleResult[]>;
20
+ /** Modelo del kernel para el alta rápida. Default `Vehicle`. */
21
+ model?: string;
22
+ /** Endpoint CRUD del alta. Default `/data/<model>/me`. */
23
+ endpoint?: string;
24
+ /** Convierte el registro guardado a `VehicleResult`. Default: id, placa, VIN, marca, modelo, año. */
25
+ mapRecord?: (rec: Record<string, unknown>) => VehicleResult;
26
+ /** Override de permiso (default `<model>.create` con useCan). */
27
+ canCreate?: boolean;
28
+ /** Valores iniciales del alta (p. ej. `customer_id` del cliente ya elegido). */
29
+ createDefaults?: Record<string, unknown>;
30
+ disabled?: boolean;
31
+ placeholder?: string;
32
+ }
33
+ /** Línea corta de un vehículo: «Nissan Versa 2019 · ABC-123 · VIN…». */
34
+ export declare function describeVehicle(v: VehicleResult): string;
35
+ export declare function VehiclePicker({ value, onChange, search, model, endpoint, mapRecord, canCreate, createDefaults, disabled, placeholder, }: VehiclePickerProps): import("react").JSX.Element;
36
+ //# sourceMappingURL=vehicle-picker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vehicle-picker.d.ts","sourceRoot":"","sources":["../../src/business/vehicle-picker.tsx"],"names":[],"mappings":"AAeA,MAAM,WAAW,aAAa;IAC1B,EAAE,EAAE,MAAM,CAAA;IACV,8EAA8E;IAC9E,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACtB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,6CAA6C;IAC7C,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,aAAa,CAAC,EAAE,MAAM,CAAA;CACzB;AAED,MAAM,WAAW,kBAAkB;IAC/B,KAAK,EAAE,aAAa,GAAG,IAAI,CAAA;IAC3B,4EAA4E;IAC5E,QAAQ,EAAE,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,KAAK,IAAI,CAAA;IACjD,uDAAuD;IACvD,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,aAAa,EAAE,CAAC,CAAA;IACpE,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,qGAAqG;IACrG,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,aAAa,CAAA;IAC3D,iEAAiE;IACjE,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,gFAAgF;IAChF,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACxC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;CACvB;AAQD,wEAAwE;AACxE,wBAAgB,eAAe,CAAC,CAAC,EAAE,aAAa,GAAG,MAAM,CAGxD;AAED,wBAAgB,aAAa,CAAC,EAC1B,KAAK,EACL,QAAQ,EACR,MAAM,EACN,KAAiB,EACjB,QAAQ,EACR,SAAS,EACT,SAAS,EACT,cAAc,EACd,QAAQ,EACR,WAAW,GACd,EAAE,kBAAkB,+BAmHpB"}
@@ -0,0 +1,61 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // VehiclePicker + alta rápida — busca por placa o VIN (y marca/modelo/cliente que
3
+ // el host decida indexar) y reutiliza el <CreateRecordDialog> dinámico del modelo
4
+ // vehículo para el alta rápida. Mismo contrato que <CustomerPicker>: controlado
5
+ // (`value` / `onChange`), `search` inyectado por el host, permisos con useCan
6
+ // (sin <PermissionsProvider> no bloquea nada: el backend autoriza).
7
+ import { useCallback, useState } from 'react';
8
+ import { useTranslation } from 'react-i18next';
9
+ import { Car, Plus, Search, X } from 'lucide-react';
10
+ import { Button, Input } from '@asteby/metacore-ui';
11
+ import { CreateRecordDialog } from '../dialogs/create-record-dialog';
12
+ import { useApi } from '../api-context';
13
+ import { useCan } from '../permissions-context';
14
+ import { useAsyncSearch } from './use-async-search';
15
+ import { EmptyState } from './feedback';
16
+ function snake(model) {
17
+ return model.replace(/([a-z0-9])([A-Z])/g, '$1_$2').toLowerCase();
18
+ }
19
+ const str = (v) => (typeof v === 'string' && v ? v : undefined);
20
+ /** Línea corta de un vehículo: «Nissan Versa 2019 · ABC-123 · VIN…». */
21
+ export function describeVehicle(v) {
22
+ const unit = [v.make, v.model, v.year].filter((x) => x != null && x !== '').join(' ');
23
+ return [unit, v.plate, v.vin].filter(Boolean).join(' · ');
24
+ }
25
+ export function VehiclePicker({ value, onChange, search, model = 'Vehicle', endpoint, mapRecord, canCreate, createDefaults, disabled, placeholder, }) {
26
+ const { t } = useTranslation();
27
+ const api = useApi();
28
+ const can = useCan();
29
+ const [text, setText] = useState('');
30
+ const [creating, setCreating] = useState(false);
31
+ const { results, loading, error } = useAsyncSearch(text, search, { minChars: 2 });
32
+ const mayCreate = canCreate ?? can(`${snake(model)}.create`);
33
+ const base = endpoint ?? `/data/${model}/me`;
34
+ const pick = (v) => {
35
+ onChange(v);
36
+ setText('');
37
+ };
38
+ const toResult = useCallback((rec) => mapRecord
39
+ ? mapRecord(rec)
40
+ : {
41
+ id: String(rec.id ?? ''),
42
+ plate: str(rec.plate),
43
+ vin: str(rec.vin),
44
+ make: str(rec.make),
45
+ model: str(rec.model),
46
+ year: typeof rec.year === 'number' || typeof rec.year === 'string' ? rec.year : undefined,
47
+ color: str(rec.color),
48
+ customer_id: str(rec.customer_id),
49
+ }, [mapRecord]);
50
+ if (value) {
51
+ return (_jsxs("div", { "data-slot": "vehicle-picker", className: "flex items-start justify-between gap-2 rounded-md border p-2", children: [_jsxs("div", { className: "flex items-start gap-2", children: [_jsx(Car, { className: "mt-0.5 size-4 text-muted-foreground", "aria-hidden": true }), _jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium", children: value.plate || value.vin || t('vehiclePicker.noPlate', { defaultValue: 'Sin placa' }) }), _jsx("p", { className: "text-xs text-muted-foreground", children: [[value.make, value.model, value.year].filter((x) => x != null && x !== '').join(' '), value.vin && value.plate ? value.vin : '', value.customer_name]
52
+ .filter(Boolean)
53
+ .join(' · ') })] })] }), !disabled && (_jsx(Button, { type: "button", size: "icon", variant: "ghost", onClick: () => onChange(null), "aria-label": t('vehiclePicker.clear', { defaultValue: 'Quitar vehículo' }), children: _jsx(X, { className: "size-4" }) }))] }));
54
+ }
55
+ return (_jsxs("div", { "data-slot": "vehicle-picker", className: "space-y-2", children: [_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsxs("div", { className: "relative flex-1", children: [_jsx(Search, { className: "pointer-events-none absolute left-2.5 top-2.5 size-4 text-muted-foreground", "aria-hidden": true }), _jsx(Input, { className: "pl-8", disabled: disabled, value: text, placeholder: placeholder ?? t('vehiclePicker.placeholder', { defaultValue: 'Placa o VIN' }), onChange: (e) => setText(e.target.value) })] }), mayCreate && (_jsx(Button, { type: "button", variant: "outline", size: "icon", disabled: disabled, onClick: () => setCreating(true), "aria-label": t('vehiclePicker.create', { defaultValue: 'Nuevo vehículo' }), children: _jsx(Plus, { className: "size-4" }) }))] }), loading && _jsx("p", { className: "text-sm text-muted-foreground", children: t('common.searching', { defaultValue: 'Buscando…' }) }), error && !loading && (_jsx("p", { role: "alert", className: "text-sm text-destructive", children: t('vehiclePicker.error', { defaultValue: 'No se pudo buscar vehículos. Inténtalo de nuevo.' }) })), !loading && !error && text.trim().length >= 2 && results.length === 0 && (_jsx(EmptyState, { title: t('vehiclePicker.empty', { defaultValue: 'No encontramos ese vehículo' }), action: mayCreate ? { label: t('vehiclePicker.createAction', { defaultValue: 'Crear vehículo' }), onClick: () => setCreating(true) } : undefined })), results.length > 0 && (_jsx("ul", { role: "listbox", className: "max-h-64 divide-y overflow-auto rounded-md border", children: results.map((v) => (_jsx("li", { role: "option", "aria-selected": false, children: _jsxs("button", { type: "button", className: "flex w-full flex-col px-2 py-1.5 text-left hover:bg-accent", onClick: () => pick(v), children: [_jsx("span", { className: "text-sm font-medium", children: v.plate || v.vin || v.id }), _jsx("span", { className: "text-xs text-muted-foreground", children: [describeVehicle({ ...v, plate: undefined }), v.customer_name].filter(Boolean).join(' · ') })] }) }, v.id))) })), creating && (_jsx(CreateRecordDialog, { modelKey: model, open: creating, onOpenChange: setCreating, endpoint: base, defaults: createDefaults ?? (text.trim() ? { plate: text.trim() } : undefined), onCreate: async (data) => {
56
+ const res = await api.post(base, data);
57
+ const rec = (res.data?.data ?? res.data);
58
+ pick(toResult(rec));
59
+ return rec.id != null ? { id: String(rec.id) } : undefined;
60
+ } }))] }));
61
+ }
@@ -0,0 +1,29 @@
1
+ export interface CfdiStampResult {
2
+ fiscalUuid?: string;
3
+ pdfUrl?: string;
4
+ xmlUrl?: string;
5
+ /** Optional human folio (`serie-folio`) when the server sends it. */
6
+ folio?: string;
7
+ documentId?: string;
8
+ }
9
+ /**
10
+ * Reads a stamp result out of a successful action response body. Looks at
11
+ * `body.data`, `body.result` and the body itself. Returns `undefined` unless
12
+ * the payload carries `fiscal_uuid` and/or `pdf_url` / `xml_url` — a response
13
+ * without those keys is never turned into a fiscal result.
14
+ */
15
+ export declare function extractStampResult(body: unknown): CfdiStampResult | undefined;
16
+ export interface CfdiStampPanelProps {
17
+ result: CfdiStampResult;
18
+ className?: string;
19
+ }
20
+ export declare function CfdiStampPanel({ result, className }: CfdiStampPanelProps): import("react").JSX.Element;
21
+ export interface CfdiStampResultDialogProps {
22
+ open: boolean;
23
+ onOpenChange: (open: boolean) => void;
24
+ result: CfdiStampResult;
25
+ title?: string;
26
+ }
27
+ /** Dialog shell used by the action modals after a successful stamp. */
28
+ export declare function CfdiStampResultDialog({ open, onOpenChange, result, title }: CfdiStampResultDialogProps): import("react").JSX.Element;
29
+ //# sourceMappingURL=cfdi-stamp-panel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cfdi-stamp-panel.d.ts","sourceRoot":"","sources":["../src/cfdi-stamp-panel.tsx"],"names":[],"mappings":"AAkBA,MAAM,WAAW,eAAe;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,qEAAqE;IACrE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,UAAU,CAAC,EAAE,MAAM,CAAA;CACtB;AAOD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,GAAG,eAAe,GAAG,SAAS,CAsB7E;AAED,MAAM,WAAW,mBAAmB;IAChC,MAAM,EAAE,eAAe,CAAA;IACvB,SAAS,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,wBAAgB,cAAc,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,mBAAmB,+BAkDxE;AAED,MAAM,WAAW,0BAA0B;IACvC,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,MAAM,EAAE,eAAe,CAAA;IACvB,KAAK,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,uEAAuE;AACvE,wBAAgB,qBAAqB,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,0BAA0B,+BAkBtG"}
@@ -0,0 +1,53 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // CfdiStampPanel — human-readable result of stamping (timbrar) a CFDI.
3
+ //
4
+ // A stamp action answers `{ document_id, fiscal_uuid, pdf_url, xml_url }`. The
5
+ // dialog used to dump that JSON; this panel shows the fiscal UUID as text and
6
+ // the PDF / XML as download links instead (#1024, FAC-3 / PIT-051). The SDK
7
+ // invents nothing: only the keys the server actually returned are rendered.
8
+ import { useTranslation } from 'react-i18next';
9
+ import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, Button, } from '@asteby/metacore-ui/primitives';
10
+ import { CheckCircle2, FileCode2, FileText } from 'lucide-react';
11
+ const str = (v) => typeof v === 'string' && v.trim() ? v.trim() : undefined;
12
+ const isSafeUrl = (u) => /^(https?:)?\/\//i.test(u) || u.startsWith('/') || u.startsWith('blob:');
13
+ /**
14
+ * Reads a stamp result out of a successful action response body. Looks at
15
+ * `body.data`, `body.result` and the body itself. Returns `undefined` unless
16
+ * the payload carries `fiscal_uuid` and/or `pdf_url` / `xml_url` — a response
17
+ * without those keys is never turned into a fiscal result.
18
+ */
19
+ export function extractStampResult(body) {
20
+ if (!body || typeof body !== 'object')
21
+ return undefined;
22
+ const b = body;
23
+ const candidates = [b.data, b.result, b];
24
+ for (const c of candidates) {
25
+ if (!c || typeof c !== 'object' || Array.isArray(c))
26
+ continue;
27
+ const o = c;
28
+ const fiscalUuid = str(o.fiscal_uuid) ?? str(o.fiscalUuid) ?? str(o.uuid);
29
+ const pdf = str(o.pdf_url) ?? str(o.pdfUrl);
30
+ const xml = str(o.xml_url) ?? str(o.xmlUrl);
31
+ // A bare `uuid` is too generic to call a fiscal result on its own.
32
+ const hasFiscal = !!str(o.fiscal_uuid) || !!str(o.fiscalUuid) || !!pdf || !!xml;
33
+ if (!hasFiscal)
34
+ continue;
35
+ return {
36
+ fiscalUuid,
37
+ pdfUrl: pdf && isSafeUrl(pdf) ? pdf : undefined,
38
+ xmlUrl: xml && isSafeUrl(xml) ? xml : undefined,
39
+ folio: str(o.folio) ?? str(o.number),
40
+ documentId: str(o.document_id) ?? str(o.documentId),
41
+ };
42
+ }
43
+ return undefined;
44
+ }
45
+ export function CfdiStampPanel({ result, className }) {
46
+ const { t } = useTranslation();
47
+ return (_jsxs("div", { "data-slot": "cfdi-stamp-panel", className: `space-y-4 ${className ?? ''}`, children: [_jsxs("div", { className: "flex items-center gap-2 text-sm font-medium text-emerald-600 dark:text-emerald-400", children: [_jsx(CheckCircle2, { className: "size-5", "aria-hidden": true }), t('cfdi.stamp.success', { defaultValue: 'CFDI timbrado correctamente' })] }), _jsxs("dl", { className: "space-y-2 rounded-md border bg-muted/40 p-3 text-sm", children: [result.folio && (_jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx("dt", { className: "text-xs uppercase tracking-wide text-muted-foreground", children: t('cfdi.stamp.folio', { defaultValue: 'Folio' }) }), _jsx("dd", { children: result.folio })] })), result.fiscalUuid && (_jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx("dt", { className: "text-xs uppercase tracking-wide text-muted-foreground", children: t('cfdi.stamp.uuid', { defaultValue: 'UUID fiscal' }) }), _jsx("dd", { className: "break-all font-mono", "data-slot": "cfdi-stamp-uuid", children: result.fiscalUuid })] }))] }), (result.pdfUrl || result.xmlUrl) && (_jsxs("div", { className: "flex flex-wrap gap-2", children: [result.pdfUrl && (_jsx(Button, { asChild: true, variant: "outline", size: "sm", children: _jsxs("a", { href: result.pdfUrl, target: "_blank", rel: "noopener noreferrer", "data-slot": "cfdi-stamp-pdf", children: [_jsx(FileText, { className: "mr-2 size-4", "aria-hidden": true }), t('cfdi.stamp.pdf', { defaultValue: 'Descargar PDF' })] }) })), result.xmlUrl && (_jsx(Button, { asChild: true, variant: "outline", size: "sm", children: _jsxs("a", { href: result.xmlUrl, target: "_blank", rel: "noopener noreferrer", "data-slot": "cfdi-stamp-xml", children: [_jsx(FileCode2, { className: "mr-2 size-4", "aria-hidden": true }), t('cfdi.stamp.xml', { defaultValue: 'Descargar XML' })] }) }))] }))] }));
48
+ }
49
+ /** Dialog shell used by the action modals after a successful stamp. */
50
+ export function CfdiStampResultDialog({ open, onOpenChange, result, title }) {
51
+ const { t } = useTranslation();
52
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: "sm:max-w-md", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: title ?? t('cfdi.stamp.title', { defaultValue: 'Resultado del timbrado' }) }), _jsx(DialogDescription, { className: "sr-only", children: t('cfdi.stamp.desc', { defaultValue: 'UUID fiscal y archivos del CFDI timbrado' }) })] }), _jsx(CfdiStampPanel, { result: result }), _jsx(DialogFooter, { children: _jsx(Button, { onClick: () => onOpenChange(false), children: t('common.close', { defaultValue: 'Cerrar' }) }) })] }) }));
53
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"dynamic-record.d.ts","sourceRoot":"","sources":["../../src/dialogs/dynamic-record.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dynamic-record.d.ts","sourceRoot":"","sources":["../../src/dialogs/dynamic-record.tsx"],"names":[],"mappings":"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"}
@@ -10,6 +10,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
10
10
  // flows through <ApiProvider> from runtime-react. Host-specific runtime values —
11
11
  // the image-url resolver and the org IANA timezone — are passed as props so the
12
12
  // SDK stays transport- and host-agnostic.
13
+ import { emitRecordMutation } from '../record-mutation-events';
13
14
  import { createContext, useCallback, useContext, useEffect, useId, useRef, useState } from 'react';
14
15
  import { useTranslation } from 'react-i18next';
15
16
  /** Model key of the open create/edit dialog — used to hide "+" on self-FK pickers
@@ -621,6 +622,7 @@ export function DynamicRecordDialog({ open, onOpenChange, nestedInlineCreateSelf
621
622
  if (isCreate && onCreate) {
622
623
  const created = await onCreate(payload);
623
624
  toast.success(modalMeta?.messages?.created || t('dynamic.create_success', { defaultValue: 'Registro creado correctamente' }));
625
+ emitRecordMutation(model, 'create');
624
626
  onSaved?.(created ?? undefined);
625
627
  onOpenChange(false);
626
628
  return;
@@ -628,6 +630,7 @@ export function DynamicRecordDialog({ open, onOpenChange, nestedInlineCreateSelf
628
630
  if (!isCreate && recordId && onUpdate) {
629
631
  const updated = await onUpdate(String(recordId), payload);
630
632
  toast.success(modalMeta?.messages?.updated || t('dynamic.update_success', { defaultValue: 'Guardado correctamente' }));
633
+ emitRecordMutation(model, 'update');
631
634
  onSaved?.(updated ?? undefined);
632
635
  onOpenChange(false);
633
636
  return;
@@ -652,6 +655,7 @@ export function DynamicRecordDialog({ open, onOpenChange, nestedInlineCreateSelf
652
655
  ? t('dynamic.create_success', { defaultValue: 'Registro creado correctamente' })
653
656
  : t('dynamic.update_success', { defaultValue: 'Guardado correctamente' })));
654
657
  // Hand the persisted record back so callers can auto-select it.
658
+ emitRecordMutation(model, isCreate ? 'create' : 'update');
655
659
  onSaved?.(res.data?.data ?? res.data ?? undefined);
656
660
  onOpenChange(false);
657
661
  }
@@ -675,6 +679,7 @@ export function DynamicRecordDialog({ open, onOpenChange, nestedInlineCreateSelf
675
679
  setDeleting(true);
676
680
  try {
677
681
  await onDelete();
682
+ emitRecordMutation(model, 'delete');
678
683
  onOpenChange(false);
679
684
  }
680
685
  catch (err) {
@@ -0,0 +1,23 @@
1
+ import { type DocumentLinesGridProps } from './business/line-items-editor';
2
+ import type { DocumentFormsManifest, TableMetadata } from './types';
3
+ export interface DocumentFormDialogProps {
4
+ open: boolean;
5
+ onOpenChange: (open: boolean) => void;
6
+ /** Modelo en el que se da de alta (p. ej. `fiscal_documents`). */
7
+ model: string;
8
+ /** Endpoint de alta. Default `/dynamic/<model>`. */
9
+ endpoint?: string;
10
+ forms: DocumentFormsManifest;
11
+ /** Tipo preseleccionado (se salta el selector). */
12
+ initialType?: string;
13
+ /** Se llama con el registro creado. */
14
+ onSaved?: (record?: unknown) => void;
15
+ /** Buscador de productos para el editor de renglones (mismo contrato que ProductPicker). */
16
+ searchProducts?: DocumentLinesGridProps['search'];
17
+ /** Moneda ISO para el editor de renglones (default: la de la org). */
18
+ currency?: string;
19
+ }
20
+ /** Manifest efectivo: la prop gana sobre el metadata; sin tipos devuelve undefined. */
21
+ export declare function resolveDocumentForms(metadata?: Pick<TableMetadata, 'document_forms'> | null, override?: DocumentFormsManifest | null): DocumentFormsManifest | undefined;
22
+ export declare function DocumentFormDialog({ open, onOpenChange, model, endpoint, forms, initialType, onSaved, searchProducts, currency, }: DocumentFormDialogProps): import("react").JSX.Element;
23
+ //# sourceMappingURL=document-form-dialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document-form-dialog.d.ts","sourceRoot":"","sources":["../src/document-form-dialog.tsx"],"names":[],"mappings":"AA4BA,OAAO,EAEH,KAAK,sBAAsB,EAE9B,MAAM,8BAA8B,CAAA;AAYrC,OAAO,KAAK,EAGR,qBAAqB,EAErB,aAAa,EAChB,MAAM,SAAS,CAAA;AAEhB,MAAM,WAAW,uBAAuB;IACpC,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,kEAAkE;IAClE,KAAK,EAAE,MAAM,CAAA;IACb,oDAAoD;IACpD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,qBAAqB,CAAA;IAC5B,mDAAmD;IACnD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,uCAAuC;IACvC,OAAO,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;IACpC,4FAA4F;IAC5F,cAAc,CAAC,EAAE,sBAAsB,CAAC,QAAQ,CAAC,CAAA;IACjD,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,uFAAuF;AACvF,wBAAgB,oBAAoB,CAChC,QAAQ,CAAC,EAAE,IAAI,CAAC,aAAa,EAAE,gBAAgB,CAAC,GAAG,IAAI,EACvD,QAAQ,CAAC,EAAE,qBAAqB,GAAG,IAAI,GACxC,qBAAqB,GAAG,SAAS,CAGnC;AAUD,wBAAgB,kBAAkB,CAAC,EAC/B,IAAI,EACJ,YAAY,EACZ,KAAK,EACL,QAAQ,EACR,KAAK,EACL,WAAW,EACX,OAAO,EACP,cAAc,EACd,QAAQ,GACX,EAAE,uBAAuB,+BAmQzB"}
@@ -0,0 +1,196 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // DocumentFormDialog — alta guiada por tipo de documento (FAC-12, #1022).
3
+ //
4
+ // "Crear documento fiscal" era el mismo formulario genérico para factura, nota
5
+ // de crédito, REP, global y traslado: los campos los dictaba el modelo, no el
6
+ // tipo. Aquí el metadata (`document_forms`) o una prop declara los tipos; el
7
+ // diálogo muestra tarjetas de tipo y, al elegir uno, SUS campos y (si lo declara)
8
+ // el paso de renglones con el editor de renglones del SDK. Un solo tipo se salta
9
+ // el selector; sin manifest el alta genérica sigue igual (DynamicCRUDPage decide).
10
+ //
11
+ // El SDK no hardcodea catálogos SAT: campos, valores fijos y el valor del tipo
12
+ // vienen del manifest que sirve el kernel o el addon.
13
+ import { useEffect, useMemo, useState } from 'react';
14
+ import { useTranslation } from 'react-i18next';
15
+ import { Button, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@asteby/metacore-ui/primitives';
16
+ import { Loader2 } from 'lucide-react';
17
+ import { toast } from 'sonner';
18
+ import { useApi } from './api-context';
19
+ import { DynamicIcon } from './dynamic-icon';
20
+ import { FieldCell, FieldGrid, FieldLabel } from './field-grid';
21
+ import { FormErrorBanner } from './business/feedback';
22
+ import { DocumentLinesGrid, } from './business/line-items-editor';
23
+ import { serializeLineItems, validateLineItems } from './business/line-items';
24
+ import { actionErrorBannerMessage, buildFieldDefaults, renderField, } from './action-modal-dispatcher';
25
+ import { extractFieldErrors, localizeFieldErrorMap } from './server-error';
26
+ import { validateValues, bagHasErrors } from './validator';
27
+ import { clearFieldErrorTree } from './field-validation-ui';
28
+ import { emitRecordMutation } from './record-mutation-events';
29
+ import { isLineItemsField, resolveWidget } from './dynamic-form-schema';
30
+ /** Manifest efectivo: la prop gana sobre el metadata; sin tipos devuelve undefined. */
31
+ export function resolveDocumentForms(metadata, override) {
32
+ const m = override ?? metadata?.document_forms;
33
+ return m && Array.isArray(m.types) && m.types.length > 0 ? m : undefined;
34
+ }
35
+ function linesConfig(type, forms) {
36
+ if (!type.lines)
37
+ return undefined;
38
+ const cfg = type.lines === true ? {} : type.lines;
39
+ return { ...cfg, field: cfg.field ?? forms.lines_field ?? 'lines' };
40
+ }
41
+ export function DocumentFormDialog({ open, onOpenChange, model, endpoint, forms, initialType, onSaved, searchProducts, currency, }) {
42
+ const { t, i18n } = useTranslation();
43
+ const api = useApi();
44
+ const types = forms.types;
45
+ const single = types.length === 1 ? types[0] : undefined;
46
+ const [typeKey, setTypeKey] = useState(initialType ?? single?.key ?? null);
47
+ const [step, setStep] = useState(initialType || single ? 'fields' : 'type');
48
+ const [formData, setFormData] = useState({});
49
+ const [lines, setLines] = useState([]);
50
+ const [fieldErrors, setFieldErrors] = useState({});
51
+ const [serverLineErrors, setServerLineErrors] = useState({});
52
+ const [formError, setFormError] = useState();
53
+ const [saving, setSaving] = useState(false);
54
+ const type = useMemo(() => types.find((x) => x.key === typeKey) ?? null, [types, typeKey]);
55
+ const lineCfg = type ? linesConfig(type, forms) : undefined;
56
+ // Reset every time the dialog (re)opens.
57
+ useEffect(() => {
58
+ if (!open)
59
+ return;
60
+ const start = initialType ?? single?.key ?? null;
61
+ setTypeKey(start);
62
+ setStep(start ? 'fields' : 'type');
63
+ setLines([]);
64
+ setFieldErrors({});
65
+ setServerLineErrors({});
66
+ setFormError(undefined);
67
+ }, [open, initialType, single?.key]);
68
+ // Seed field defaults for the chosen type.
69
+ useEffect(() => {
70
+ if (!type)
71
+ return;
72
+ setFormData({ ...buildFieldDefaults(type.fields, undefined), ...(type.defaults ?? {}) });
73
+ setFieldErrors({});
74
+ setFormError(undefined);
75
+ }, [type]);
76
+ const updateField = (key, value) => {
77
+ setFormData((prev) => ({ ...prev, [key]: value }));
78
+ setFieldErrors((prev) => clearFieldErrorTree(prev, key));
79
+ };
80
+ const tl = (s) => t(s, { defaultValue: s });
81
+ const lang = i18n?.language;
82
+ const chooseType = (k) => {
83
+ setTypeKey(k);
84
+ setStep('fields');
85
+ };
86
+ /** Validate the header fields; returns true when they pass. */
87
+ const validateHeader = () => {
88
+ if (!type)
89
+ return false;
90
+ const bag = validateValues(type.fields, formData);
91
+ if (!bagHasErrors(bag)) {
92
+ setFieldErrors({});
93
+ return true;
94
+ }
95
+ const labels = {};
96
+ for (const f of type.fields)
97
+ labels[f.key] = tl(f.label);
98
+ setFieldErrors(localizeFieldErrorMap(bag, t, { labels, language: lang }));
99
+ return false;
100
+ };
101
+ const goNext = () => {
102
+ if (validateHeader())
103
+ setStep('lines');
104
+ };
105
+ const submit = async () => {
106
+ if (!type)
107
+ return;
108
+ if (!validateHeader()) {
109
+ setStep('fields');
110
+ return;
111
+ }
112
+ if (lineCfg && (lineCfg.required ?? true)) {
113
+ const v = validateLineItems(lines, { requireItems: true });
114
+ if (!v.form && Object.keys(v.errors).length === 0) {
115
+ /* ok */
116
+ }
117
+ else {
118
+ setServerLineErrors(v.errors);
119
+ setFormError(v.form ?? t('documentForm.lines_invalid', { defaultValue: 'Revisa los renglones del documento.' }));
120
+ setStep('lines');
121
+ return;
122
+ }
123
+ }
124
+ setSaving(true);
125
+ setFormError(undefined);
126
+ try {
127
+ const payload = { ...(type.defaults ?? {}), ...formData };
128
+ if (forms.type_field)
129
+ payload[forms.type_field] = type.value ?? type.key;
130
+ if (lineCfg)
131
+ payload[lineCfg.field] = serializeLineItems(lines);
132
+ const url = type.endpoint ?? endpoint ?? `/dynamic/${model}`;
133
+ const res = await api.post(url, payload);
134
+ if (res.data?.success === false) {
135
+ handleError({ response: { data: res.data } });
136
+ return;
137
+ }
138
+ toast.success(t('dynamic.create_success', { defaultValue: 'Registro creado correctamente' }));
139
+ emitRecordMutation(model, 'create');
140
+ onSaved?.(res.data?.data ?? res.data ?? undefined);
141
+ onOpenChange(false);
142
+ }
143
+ catch (err) {
144
+ handleError(err);
145
+ }
146
+ finally {
147
+ setSaving(false);
148
+ }
149
+ };
150
+ /** 422 → marks the fields and shows the banner INSIDE the dialog; stays open. */
151
+ const handleError = (err) => {
152
+ setFormError(actionErrorBannerMessage(err, type?.fields, t, lang));
153
+ const map = extractFieldErrors(err);
154
+ if (map) {
155
+ const labels = {};
156
+ for (const f of type?.fields ?? [])
157
+ labels[f.key] = tl(f.label);
158
+ const localized = localizeFieldErrorMap(map, t, { labels, language: lang });
159
+ const header = {};
160
+ const lineErrs = {};
161
+ const prefix = lineCfg ? `${lineCfg.field}.` : null;
162
+ for (const [k, v] of Object.entries(localized)) {
163
+ if (prefix && k.startsWith(prefix))
164
+ lineErrs[k.slice(prefix.length)] = v;
165
+ else
166
+ header[k] = v;
167
+ }
168
+ setFieldErrors(header);
169
+ setServerLineErrors(lineErrs);
170
+ }
171
+ };
172
+ const canGoBackToTypes = types.length > 1 && !initialType;
173
+ const stepLabel = (s) => s === 'type'
174
+ ? t('documentForm.step_type', { defaultValue: 'Tipo de documento' })
175
+ : s === 'fields'
176
+ ? t('documentForm.step_data', { defaultValue: 'Datos' })
177
+ : tl(lineCfg?.title ?? 'Renglones');
178
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: "flex max-h-[90dvh] flex-col overflow-hidden sm:max-w-2xl", style: { maxHeight: '90dvh', ...(step === 'lines' ? { maxWidth: '980px', width: '95vw' } : {}) }, "data-slot": "document-form-dialog", children: [_jsxs(DialogHeader, { className: "shrink-0", children: [_jsx(DialogTitle, { children: type ? tl(type.label) : t('documentForm.title', { defaultValue: 'Nuevo documento' }) }), _jsx(DialogDescription, { children: stepLabel(step) })] }), _jsxs("div", { className: "-mx-1 min-h-0 flex-1 space-y-3 overflow-y-auto px-1 py-3", children: [_jsx(FormErrorBanner, { message: formError }), step === 'type' && (_jsx("div", { role: "radiogroup", className: "grid gap-3 sm:grid-cols-2", children: types.map((x) => (_jsxs("button", { type: "button", role: "radio", "aria-checked": x.key === typeKey, "data-type-card": x.key, onClick: () => chooseType(x.key), className: "flex items-start gap-3 rounded-lg border p-4 text-left transition-colors hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", children: [x.icon && _jsx(DynamicIcon, { name: x.icon, className: "mt-0.5 size-5 shrink-0 text-primary" }), _jsxs("span", { children: [_jsx("span", { className: "block text-sm font-medium", children: tl(x.label) }), x.description && (_jsx("span", { className: "block text-xs text-muted-foreground", children: tl(x.description) }))] })] }, x.key))) })), step === 'fields' && type && (_jsx(FieldGrid, { children: type.fields.map((field) => {
179
+ const fullWidth = isLineItemsField(field) ||
180
+ resolveWidget(field) === 'textarea' ||
181
+ resolveWidget(field) === 'richtext';
182
+ return (_jsxs(FieldCell, { fullWidth: fullWidth, children: [_jsx(FieldLabel, { htmlFor: field.key, required: field.required, children: tl(field.label) }), renderField(field, formData[field.key], (v) => updateField(field.key, v), formData, undefined, fieldErrors), fieldErrors[field.key] && (_jsx("p", { className: "mt-1 text-xs text-destructive", children: fieldErrors[field.key] }))] }, field.key));
183
+ }) })), step === 'lines' && type && lineCfg && (_jsx(DocumentLinesGrid, { value: lines, onChange: (next) => {
184
+ setLines(next);
185
+ setServerLineErrors({});
186
+ }, columns: lineCfg.columns ?? ['discount', 'tax'], priceSource: lineCfg.price_source ?? 'sale', serverErrors: serverLineErrors, search: searchProducts, currency: currency }))] }), _jsxs(DialogFooter, { className: "shrink-0", children: [step === 'type' ? (_jsx(Button, { type: "button", variant: "outline", onClick: () => onOpenChange(false), children: t('common.cancel', { defaultValue: 'Cancelar' }) })) : (_jsx(Button, { type: "button", variant: "outline", disabled: saving, onClick: () => {
187
+ if (step === 'lines')
188
+ setStep('fields');
189
+ else if (canGoBackToTypes)
190
+ setStep('type');
191
+ else
192
+ onOpenChange(false);
193
+ }, children: step === 'fields' && !canGoBackToTypes
194
+ ? t('common.cancel', { defaultValue: 'Cancelar' })
195
+ : t('common.back', { defaultValue: 'Atrás' }) })), step === 'fields' && type && lineCfg && (_jsx(Button, { type: "button", onClick: goNext, children: t('common.next', { defaultValue: 'Siguiente' }) })), ((step === 'fields' && type && !lineCfg) || step === 'lines') && (_jsxs(Button, { type: "button", onClick: submit, disabled: saving, children: [saving && _jsx(Loader2, { className: "mr-2 size-4 animate-spin", "aria-hidden": true }), type?.submit_label ? tl(type.submit_label) : t('common.create', { defaultValue: 'Crear' })] }))] })] }) }));
196
+ }
@@ -1,4 +1,5 @@
1
1
  import React from 'react';
2
+ import type { DocumentFormsManifest } from './types';
2
3
  export interface DynamicCRUDPageStrings {
3
4
  refresh?: string;
4
5
  export?: string;
@@ -39,6 +40,11 @@ export interface DynamicCRUDPageProps {
39
40
  classes?: DynamicCRUDPageClasses;
40
41
  /** Fired after a create/import/refresh successfully reloads the table. */
41
42
  onChange?: () => void;
43
+ /**
44
+ * Guided create by document type (FAC-12). Wins over `metadata.document_forms`;
45
+ * with neither, the generic record dialog is used as before.
46
+ */
47
+ documentForms?: DocumentFormsManifest;
42
48
  }
43
49
  /**
44
50
  * Page-level CRUD shell wired around <DynamicTable>. Hosts mount a single
@@ -1 +1 @@
1
- {"version":3,"file":"dynamic-crud-page.d.ts","sourceRoot":"","sources":["../src/dynamic-crud-page.tsx"],"names":[],"mappings":"AAwBA,OAAO,KAKN,MAAM,OAAO,CAAA;AAcd,MAAM,WAAW,sBAAsB;IACnC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;mDAC+C;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAA;CACrB;AASD,MAAM,WAAW,sBAAsB;IACnC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,YAAY,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,oBAAoB;IACjC,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAA;IACb,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,gFAAgF;IAChF,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,4EAA4E;IAC5E,IAAI,CAAC,EAAE,sBAAsB,CAAA;IAC7B,+EAA+E;IAC/E,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,2EAA2E;IAC3E,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC9B,8DAA8D;IAC9D,aAAa,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC/B,sDAAsD;IACtD,OAAO,CAAC,EAAE,sBAAsB,CAAA;IAChC,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;CACxB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,qBAgN1D"}
1
+ {"version":3,"file":"dynamic-crud-page.d.ts","sourceRoot":"","sources":["../src/dynamic-crud-page.tsx"],"names":[],"mappings":"AAwBA,OAAO,KAKN,MAAM,OAAO,CAAA;AAad,OAAO,KAAK,EAAE,qBAAqB,EAAiB,MAAM,SAAS,CAAA;AAEnE,MAAM,WAAW,sBAAsB;IACnC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;mDAC+C;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAA;CACrB;AASD,MAAM,WAAW,sBAAsB;IACnC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,YAAY,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,oBAAoB;IACjC,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAA;IACb,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,gFAAgF;IAChF,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,4EAA4E;IAC5E,IAAI,CAAC,EAAE,sBAAsB,CAAA;IAC7B,+EAA+E;IAC/E,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,2EAA2E;IAC3E,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC9B,8DAA8D;IAC9D,aAAa,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC/B,sDAAsD;IACtD,OAAO,CAAC,EAAE,sBAAsB,CAAA;IAChC,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB;;;OAGG;IACH,aAAa,CAAC,EAAE,qBAAqB,CAAA;CACxC;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,qBA6N1D"}
@@ -29,6 +29,7 @@ import { useApi } from './api-context';
29
29
  import { useMetadataCache } from './metadata-cache';
30
30
  import { DynamicTable } from './dynamic-table';
31
31
  import { DynamicRecordDialog } from './dialogs/dynamic-record';
32
+ import { DocumentFormDialog, resolveDocumentForms } from './document-form-dialog';
32
33
  import { ExportDialog } from './dialogs/export';
33
34
  import { ImportDialog } from './dialogs/import';
34
35
  import { getModelExtension } from './model-extension-registry';
@@ -47,7 +48,7 @@ const defaultStrings = {
47
48
  * metadata fetch, dialogs and toolbar.
48
49
  */
49
50
  export function DynamicCRUDPage(props) {
50
- const { model, endpoint, title: titleOverride, newLabel, i18n, hideCreate, hideExport, hideImport, hideRefresh, headerExtras, toolbarExtras, classes, onChange, } = props;
51
+ const { model, endpoint, title: titleOverride, newLabel, i18n, hideCreate, hideExport, hideImport, hideRefresh, headerExtras, toolbarExtras, classes, onChange, documentForms, } = props;
51
52
  const strings = { ...defaultStrings, ...(i18n ?? {}) };
52
53
  const dataEndpoint = endpoint ?? `/dynamic/${model}`;
53
54
  const ext = getModelExtension(model);
@@ -117,6 +118,7 @@ export function DynamicCRUDPage(props) {
117
118
  const showImport = enableCRUD && !effectiveHideImport && can(modelCapability(model, 'import'));
118
119
  const showExport = !effectiveHideExport && can(modelCapability(model, 'export'));
119
120
  const showRefresh = !effectiveHideRefresh;
121
+ const guidedForms = resolveDocumentForms(metadata, documentForms);
120
122
  const handleRefresh = useCallback(() => {
121
123
  setRefreshKey((k) => k + 1);
122
124
  onChange?.();
@@ -127,5 +129,5 @@ export function DynamicCRUDPage(props) {
127
129
  const titleCls = classes?.title ?? 'text-2xl font-bold tracking-tight';
128
130
  const toolbarCls = classes?.toolbar ?? 'flex items-center gap-2';
129
131
  const tableWrapperCls = classes?.tableWrapper ?? 'flex-1 min-h-0';
130
- return (_jsxs("div", { className: rootCls, children: [_jsxs("div", { className: containerCls, children: [ext?.headerExtras && _jsx(ext.headerExtras, { model: model, onRefresh: handleRefresh }), headerExtras, _jsxs("div", { className: headerCls, children: [metadata ? (_jsx("h1", { className: titleCls, children: title })) : (_jsx("div", { className: 'h-8 w-48 bg-muted rounded animate-pulse' })), _jsxs("div", { className: toolbarCls, children: [showRefresh && (_jsx("button", { type: 'button', onClick: handleRefresh, "aria-label": strings.refresh, className: 'inline-flex items-center justify-center size-9 rounded-md border border-border bg-background hover:bg-accent text-foreground', children: _jsx(RefreshCw, { className: 'size-4' }) })), metadata && showExport && (_jsxs("button", { type: 'button', onClick: () => setOpenExport(true), className: 'inline-flex items-center gap-2 h-9 px-3 rounded-md border border-border bg-background hover:bg-accent text-sm font-medium text-foreground', children: [_jsx(Download, { className: 'size-4' }), strings.export] })), metadata && showImport && (_jsxs("button", { type: 'button', onClick: () => setOpenImport(true), className: 'inline-flex items-center gap-2 h-9 px-3 rounded-md border border-border bg-background hover:bg-accent text-sm font-medium text-foreground', children: [_jsx(Upload, { className: 'size-4' }), strings.import] })), ext?.toolbarExtras && _jsx(ext.toolbarExtras, { model: model, onRefresh: handleRefresh }), toolbarExtras, _jsx(ModelActionToolbar, { model: model, endpoint: dataEndpoint, actions: metadata?.actions, primaryActionKey: ext?.primaryActionKey, onChange: handleRefresh }), showCreate && (_jsxs("button", { type: 'button', "data-primary": 'true', onClick: () => setOpenCreate(true), className: 'inline-flex items-center gap-2 h-9 px-3 rounded-md bg-primary text-primary-foreground hover:opacity-90 text-sm font-medium', children: [_jsx(Plus, { className: 'size-4' }), resolvedNewLabel ?? `${strings.newPrefix} ${singular}`] }))] })] }), _jsx("div", { className: tableWrapperCls, children: _jsx(DynamicTable, { model: model, endpoint: dataEndpoint, refreshTrigger: refreshKey }, model) })] }), showCreate && (_jsx(DynamicRecordDialog, { open: openCreate, onOpenChange: setOpenCreate, mode: 'create', model: model, endpoint: dataEndpoint, onSaved: handleRefresh })), metadata && showExport && (_jsx(ExportDialog, { open: openExport, onOpenChange: setOpenExport, model: model, metadata: metadata })), metadata && showImport && (_jsx(ImportDialog, { open: openImport, onOpenChange: setOpenImport, model: model, metadata: metadata, onImported: handleRefresh }))] }));
132
+ return (_jsxs("div", { className: rootCls, children: [_jsxs("div", { className: containerCls, children: [ext?.headerExtras && _jsx(ext.headerExtras, { model: model, onRefresh: handleRefresh }), headerExtras, _jsxs("div", { className: headerCls, children: [metadata ? (_jsx("h1", { className: titleCls, children: title })) : (_jsx("div", { className: 'h-8 w-48 bg-muted rounded animate-pulse' })), _jsxs("div", { className: toolbarCls, children: [showRefresh && (_jsx("button", { type: 'button', onClick: handleRefresh, "aria-label": strings.refresh, className: 'inline-flex items-center justify-center size-9 rounded-md border border-border bg-background hover:bg-accent text-foreground', children: _jsx(RefreshCw, { className: 'size-4' }) })), metadata && showExport && (_jsxs("button", { type: 'button', onClick: () => setOpenExport(true), className: 'inline-flex items-center gap-2 h-9 px-3 rounded-md border border-border bg-background hover:bg-accent text-sm font-medium text-foreground', children: [_jsx(Download, { className: 'size-4' }), strings.export] })), metadata && showImport && (_jsxs("button", { type: 'button', onClick: () => setOpenImport(true), className: 'inline-flex items-center gap-2 h-9 px-3 rounded-md border border-border bg-background hover:bg-accent text-sm font-medium text-foreground', children: [_jsx(Upload, { className: 'size-4' }), strings.import] })), ext?.toolbarExtras && _jsx(ext.toolbarExtras, { model: model, onRefresh: handleRefresh }), toolbarExtras, _jsx(ModelActionToolbar, { model: model, endpoint: dataEndpoint, actions: metadata?.actions, primaryActionKey: ext?.primaryActionKey, onChange: handleRefresh }), showCreate && (_jsxs("button", { type: 'button', "data-primary": 'true', onClick: () => setOpenCreate(true), className: 'inline-flex items-center gap-2 h-9 px-3 rounded-md bg-primary text-primary-foreground hover:opacity-90 text-sm font-medium', children: [_jsx(Plus, { className: 'size-4' }), resolvedNewLabel ?? `${strings.newPrefix} ${singular}`] }))] })] }), _jsx("div", { className: tableWrapperCls, children: _jsx(DynamicTable, { model: model, endpoint: dataEndpoint, refreshTrigger: refreshKey }, model) })] }), showCreate && guidedForms && (_jsx(DocumentFormDialog, { open: openCreate, onOpenChange: setOpenCreate, model: model, endpoint: dataEndpoint, forms: guidedForms, onSaved: handleRefresh })), showCreate && !guidedForms && (_jsx(DynamicRecordDialog, { open: openCreate, onOpenChange: setOpenCreate, mode: 'create', model: model, endpoint: dataEndpoint, onSaved: handleRefresh })), metadata && showExport && (_jsx(ExportDialog, { open: openExport, onOpenChange: setOpenExport, model: model, metadata: metadata })), metadata && showImport && (_jsx(ImportDialog, { open: openImport, onOpenChange: setOpenImport, model: model, metadata: metadata, onImported: handleRefresh }))] }));
131
133
  }
@@ -1 +1 @@
1
- {"version":3,"file":"dynamic-kanban.d.ts","sourceRoot":"","sources":["../src/dynamic-kanban.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dynamic-kanban.d.ts","sourceRoot":"","sources":["../src/dynamic-kanban.tsx"],"names":[],"mappings":"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"}
@@ -26,6 +26,7 @@ import { useFlipAnimation } from './use-flip-animation';
26
26
  import { PortalDragOverlay } from './portal-drag-overlay';
27
27
  import { objectLabel } from './dynamic-relation-helpers';
28
28
  import { useMetadataCache } from './metadata-cache';
29
+ import { useRecordMutationTick } from './use-record-mutation-tick';
29
30
  import { useOptionsResolver } from './use-options-resolver';
30
31
  import { ActivityValueRenderer } from './activity-value-renderer';
31
32
  import { isColumnVisibleInTable } from './column-visibility';
@@ -582,6 +583,7 @@ function DynamicKanbanBoard({ model, endpoint, refreshTrigger, realtime: realtim
582
583
  // Realtime refetch (opt-in) — debounced counter bumped by DATA_EVENTs for
583
584
  // this model; folded into the board refetch effect next to refreshTrigger.
584
585
  const realtimeDefault = useRealtimeDefault();
586
+ const mutationTick = useRecordMutationTick(model);
585
587
  const realtimeTick = useRealtimeTick({
586
588
  models: [model],
587
589
  enabled: realtimeProp ?? realtimeDefault,
@@ -953,7 +955,7 @@ function DynamicKanbanBoard({ model, endpoint, refreshTrigger, realtime: realtim
953
955
  return () => clearTimeout(handle);
954
956
  // realtimeTick: data events for this model (see the `realtime` prop).
955
957
  // eslint-disable-next-line react-hooks/exhaustive-deps
956
- }, [fetchData, metadata, refreshTrigger, realtimeTick]);
958
+ }, [fetchData, metadata, refreshTrigger, realtimeTick, mutationTick]);
957
959
  // Filterable fields for the toolbar, in metadata order (explicit filters
958
960
  // first, then filterable columns), each labeled from its metadata source.
959
961
  const filterFields = useMemo(() => {