@asteby/metacore-runtime-react 28.5.0 → 28.7.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/CHANGELOG.md +12 -0
- package/dist/dialogs/dynamic-record.js +1 -1
- package/dist/dialogs/import.d.ts.map +1 -1
- package/dist/dialogs/import.js +17 -6
- package/dist/dynamic-form.js +1 -1
- package/dist/dynamic-table.d.ts +10 -1
- package/dist/dynamic-table.d.ts.map +1 -1
- package/dist/dynamic-table.js +10 -3
- package/dist/form-layout.d.ts +15 -1
- package/dist/form-layout.d.ts.map +1 -1
- package/dist/form-layout.js +23 -3
- package/dist/types.d.ts +29 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/form-layout.test.ts +88 -0
- package/src/dialogs/dynamic-record.tsx +1 -1
- package/src/dialogs/import.tsx +48 -18
- package/src/dynamic-form.tsx +2 -2
- package/src/dynamic-table.tsx +24 -4
- package/src/form-layout.ts +33 -2
- package/src/types.ts +31 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @asteby/metacore-runtime-react
|
|
2
2
|
|
|
3
|
+
## 28.7.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 1c8c4fe: Unifica el flujo de importación alrededor de la `ImportSpec` que sirve el kernel. `starter-core` traía una copia byte a byte del `ImportDialog` de `runtime-react` (414 líneas) que nadie importaba y cuyos endpoints ya habían divergido (`/data/` vs `/dynamic/`): se elimina y el de `runtime-react` queda canónico. El diálogo acepta `.xlsx` además de CSV/JSON, y normaliza los errores por fila del backend nuevo (`{row, column, message}` en validate, `{row, column, error}` en import) sin romper el formato anterior (`{row, field, message}`), que antes renderizaba celdas vacías. `TableMetadata.import` queda tipado (`ImportSpecMeta`/`ImportColumnMeta`): cuando el kernel sirve la spec, la acción de importar aparece sin que el host encienda ninguna bandera, porque el modelo declaró sus columnas o el kernel las derivó de su formulario. Se agregan `hideImport` y `hideExport` por vista, para las tablas con alcance por rol que no quieren el punto de entrada masivo aunque el modelo lo soporte.
|
|
8
|
+
|
|
9
|
+
## 28.6.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- 21c5268: Soporta `visible_when` a nivel de SECCIÓN en `form_layout` (kernel v0.84.0). Cuando una sección declara un predicado `visible_when` que evalúa falso contra los valores actuales del formulario, la sección se oculta por completo junto con sus campos; en modo wizard (steps) ese paso desaparece de la secuencia de navegación sin romper Anterior/Siguiente ni el submit del último paso. Reutiliza el mismo evaluador (`getVisibleWhen`/`evaluateVisibleWhen`) que ya usan los campos, tolerando snake_case y camelCase. El `visible_when` de cada campo se sigue respetando dentro de una sección visible, y sin `visible_when` de sección el comportamiento es idéntico al actual.
|
|
14
|
+
|
|
3
15
|
## 28.5.0
|
|
4
16
|
|
|
5
17
|
### Minor Changes
|
|
@@ -598,7 +598,7 @@ export function DynamicRecordDialog({ open, onOpenChange, mode, model, recordId,
|
|
|
598
598
|
// their section. Empty sections drop out for free. Steps mode only drives a
|
|
599
599
|
// wizard in editable modes — view mode always stacks the sections.
|
|
600
600
|
const formLayout = modalMeta?.form_layout ?? modalMeta?.formLayout;
|
|
601
|
-
const groups = groupFieldsBySection(visibleFields, formLayout);
|
|
601
|
+
const groups = groupFieldsBySection(visibleFields, formLayout, formValues);
|
|
602
602
|
const isSteps = isEditable && formLayout?.mode === 'steps' && groups.length > 1;
|
|
603
603
|
const clampedStep = Math.min(stepIndex, Math.max(groups.length - 1, 0));
|
|
604
604
|
const isLastStep = clampedStep === groups.length - 1;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"import.d.ts","sourceRoot":"","sources":["../../src/dialogs/import.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"import.d.ts","sourceRoot":"","sources":["../../src/dialogs/import.tsx"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAG7C,UAAU,iBAAiB;IACvB,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,aAAa,CAAA;IACvB,UAAU,CAAC,EAAE,MAAM,IAAI,CAAA;CAC1B;AAqCD,wBAAgB,YAAY,CAAC,EACzB,IAAI,EACJ,YAAY,EACZ,KAAK,EACL,QAAQ,EACR,UAAU,GACb,EAAE,iBAAiB,+BAkWnB"}
|
package/dist/dialogs/import.js
CHANGED
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
// ImportDialog — three-step CSV/JSON import flow (upload → validate →
|
|
3
|
-
// with per-row error report).
|
|
2
|
+
// ImportDialog — three-step XLSX/CSV/JSON import flow (upload → validate →
|
|
3
|
+
// import with per-row error report). The template, the accepted columns and
|
|
4
|
+
// the validation rules all come from the model's ImportSpec served by the
|
|
5
|
+
// kernel, so this dialog carries no per-model knowledge. Axios-like client is
|
|
6
|
+
// provided by <ApiProvider>.
|
|
4
7
|
import { useState, useEffect, useRef } from 'react';
|
|
5
8
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, Button, Input, Label, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@asteby/metacore-ui/primitives';
|
|
6
9
|
import { Progress } from './_primitives';
|
|
7
10
|
import { toast } from 'sonner';
|
|
8
11
|
import { FileDown, Loader2, Check, AlertCircle } from 'lucide-react';
|
|
9
12
|
import { useApi } from '../api-context';
|
|
13
|
+
function errorColumn(e) {
|
|
14
|
+
return e.column ?? e.field ?? '';
|
|
15
|
+
}
|
|
16
|
+
function errorMessage(e) {
|
|
17
|
+
return e.message ?? e.error ?? '';
|
|
18
|
+
}
|
|
10
19
|
export function ImportDialog({ open, onOpenChange, model, metadata, onImported, }) {
|
|
11
20
|
const api = useApi();
|
|
12
21
|
const [step, setStep] = useState('upload');
|
|
@@ -42,7 +51,7 @@ export function ImportDialog({ open, onOpenChange, model, metadata, onImported,
|
|
|
42
51
|
const url = window.URL.createObjectURL(response.data);
|
|
43
52
|
const link = document.createElement('a');
|
|
44
53
|
link.href = url;
|
|
45
|
-
link.download = `${model}-plantilla.
|
|
54
|
+
link.download = `${model}-plantilla.xlsx`;
|
|
46
55
|
document.body.appendChild(link);
|
|
47
56
|
link.click();
|
|
48
57
|
document.body.removeChild(link);
|
|
@@ -66,7 +75,8 @@ export function ImportDialog({ open, onOpenChange, model, metadata, onImported,
|
|
|
66
75
|
});
|
|
67
76
|
const data = res.data?.data ?? res.data;
|
|
68
77
|
setValidationResult({
|
|
69
|
-
valid: data.valid ?? 0,
|
|
78
|
+
valid: data.rowCount ?? data.valid ?? 0,
|
|
79
|
+
skipped: data.skipped ?? 0,
|
|
70
80
|
errors: data.errors ?? [],
|
|
71
81
|
});
|
|
72
82
|
setStep('validation');
|
|
@@ -98,7 +108,8 @@ export function ImportDialog({ open, onOpenChange, model, metadata, onImported,
|
|
|
98
108
|
const data = res.data?.data ?? res.data;
|
|
99
109
|
setImportResult({
|
|
100
110
|
created: data.created ?? 0,
|
|
101
|
-
|
|
111
|
+
skipped: data.skipped ?? 0,
|
|
112
|
+
errors: data.failures ?? data.errors ?? [],
|
|
102
113
|
});
|
|
103
114
|
setStep('results');
|
|
104
115
|
if ((data.created ?? 0) > 0) {
|
|
@@ -122,6 +133,6 @@ export function ImportDialog({ open, onOpenChange, model, metadata, onImported,
|
|
|
122
133
|
validation: 'Validacion',
|
|
123
134
|
results: 'Resultados',
|
|
124
135
|
};
|
|
125
|
-
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: "sm:max-w-lg max-h-[90vh] flex flex-col p-0 gap-0 overflow-hidden", children: [_jsxs(DialogHeader, { className: "p-6 pb-4 border-b shrink-0", children: [_jsxs(DialogTitle, { children: ["Importar ", metadata.title] }), _jsx(DialogDescription, { children: stepTitle[step] })] }), _jsxs("div", { className: "flex-1 overflow-y-auto p-6", children: [step === 'upload' && (_jsxs("div", { className: "space-y-6", children: [_jsxs("div", { children: [_jsxs(Button, { variant: "link", className: "px-0 h-auto text-sm", onClick: handleDownloadTemplate, children: [_jsx(FileDown, { className: "h-4 w-4 mr-1" }), "Descargar plantilla
|
|
136
|
+
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: "sm:max-w-lg max-h-[90vh] flex flex-col p-0 gap-0 overflow-hidden", children: [_jsxs(DialogHeader, { className: "p-6 pb-4 border-b shrink-0", children: [_jsxs(DialogTitle, { children: ["Importar ", metadata.title] }), _jsx(DialogDescription, { children: stepTitle[step] })] }), _jsxs("div", { className: "flex-1 overflow-y-auto p-6", children: [step === 'upload' && (_jsxs("div", { className: "space-y-6", children: [_jsxs("div", { children: [_jsxs(Button, { variant: "link", className: "px-0 h-auto text-sm", onClick: handleDownloadTemplate, children: [_jsx(FileDown, { className: "h-4 w-4 mr-1" }), "Descargar plantilla"] }), _jsx("p", { className: "text-xs text-muted-foreground mt-1", children: "La plantilla trae las columnas, un ejemplo y las instrucciones. Borra la fila de ejemplo antes de subir." })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "import-file", className: "text-sm font-medium", children: "Archivo" }), _jsx(Input, { ref: fileInputRef, id: "import-file", type: "file", accept: ".xlsx,.xls,.csv,.json", onChange: handleFileChange, className: "cursor-pointer" }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Formatos aceptados: Excel, CSV, JSON" })] })] })), step === 'validation' && validationResult && (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "flex items-center gap-4", children: [_jsxs("div", { className: "flex items-center gap-2 text-sm", children: [_jsx(Check, { className: "h-4 w-4 text-green-600" }), _jsxs("span", { children: [_jsx("strong", { children: validationResult.valid }), " valido(s)"] })] }), validationResult.errors.length > 0 && (_jsxs("div", { className: "flex items-center gap-2 text-sm", children: [_jsx(AlertCircle, { className: "h-4 w-4 text-destructive" }), _jsxs("span", { children: [_jsx("strong", { children: validationResult.errors.length }), " error(es)"] })] }))] }), validationResult.skipped > 0 && (_jsxs("p", { className: "text-xs text-muted-foreground", children: ["Se ignoraron ", validationResult.skipped, " fila(s) de ejemplo de la plantilla."] })), validationResult.errors.length > 0 && (_jsx("div", { className: "border rounded-md max-h-60 overflow-auto", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { className: "w-16", children: "Fila" }), _jsx(TableHead, { children: "Campo" }), _jsx(TableHead, { children: "Error" })] }) }), _jsx(TableBody, { children: validationResult.errors.map((error, idx) => (_jsxs(TableRow, { children: [_jsx(TableCell, { className: "font-mono text-xs", children: error.row }), _jsx(TableCell, { className: "text-sm", children: errorColumn(error) }), _jsx(TableCell, { className: "text-sm text-destructive", children: errorMessage(error) })] }, idx))) })] }) })), importing && (_jsxs("div", { className: "space-y-2", children: [_jsx(Progress, { value: progress }), _jsxs("p", { className: "text-xs text-muted-foreground text-center", children: ["Importando... ", progress > 0 ? `${progress}%` : ''] })] }))] })), step === 'results' && importResult && (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "flex items-center gap-4", children: [importResult.created > 0 && (_jsxs("div", { className: "flex items-center gap-2 text-sm", children: [_jsx(Check, { className: "h-4 w-4 text-green-600" }), _jsxs("span", { children: [_jsx("strong", { children: importResult.created }), " creado(s)"] })] })), importResult.errors.length > 0 && (_jsxs("div", { className: "flex items-center gap-2 text-sm", children: [_jsx(AlertCircle, { className: "h-4 w-4 text-destructive" }), _jsxs("span", { children: [_jsx("strong", { children: importResult.errors.length }), " error(es)"] })] }))] }), importResult.created > 0 && importResult.errors.length === 0 && (_jsxs("div", { className: "flex items-center gap-2 rounded-md bg-green-50 dark:bg-green-950/20 p-3 text-sm text-green-700 dark:text-green-400", children: [_jsx(Check, { className: "h-4 w-4 shrink-0" }), "Todos los registros fueron importados correctamente."] })), importResult.errors.length > 0 && (_jsx("div", { className: "border rounded-md max-h-60 overflow-auto", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { className: "w-16", children: "Fila" }), _jsx(TableHead, { children: "Campo" }), _jsx(TableHead, { children: "Error" })] }) }), _jsx(TableBody, { children: importResult.errors.map((error, idx) => (_jsxs(TableRow, { children: [_jsx(TableCell, { className: "font-mono text-xs", children: error.row }), _jsx(TableCell, { className: "text-sm", children: errorColumn(error) }), _jsx(TableCell, { className: "text-sm text-destructive", children: errorMessage(error) })] }, idx))) })] }) }))] }))] }), _jsxs(DialogFooter, { className: "p-4 border-t shrink-0", children: [step === 'upload' && (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "outline", onClick: handleClose, children: "Cancelar" }), _jsxs(Button, { onClick: handleValidate, disabled: !file || validating, children: [validating && (_jsx(Loader2, { className: "h-4 w-4 mr-1 animate-spin" })), validating ? 'Validando...' : 'Validar'] })] })), step === 'validation' && (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "outline", onClick: () => setStep('upload'), disabled: importing, children: "Atras" }), _jsxs(Button, { onClick: handleImport, disabled: importing ||
|
|
126
137
|
(validationResult !== null && validationResult.valid === 0), children: [importing && (_jsx(Loader2, { className: "h-4 w-4 mr-1 animate-spin" })), importing ? 'Importando...' : 'Importar'] })] })), step === 'results' && (_jsx(Button, { onClick: handleClose, children: "Cerrar" }))] })] }) }));
|
|
127
138
|
}
|
package/dist/dynamic-form.js
CHANGED
|
@@ -53,7 +53,7 @@ export function DynamicForm({ fields, initialValues, onSubmit, onCancel, submitL
|
|
|
53
53
|
// because visibleFields is already visibility-filtered). Without a layout
|
|
54
54
|
// this is a single default group → the render below collapses to the legacy
|
|
55
55
|
// flat grid, byte-for-byte.
|
|
56
|
-
const groups = useMemo(() => groupFieldsBySection(visibleFields, formLayout), [visibleFields, formLayout]);
|
|
56
|
+
const groups = useMemo(() => groupFieldsBySection(visibleFields, formLayout, values), [visibleFields, formLayout, values]);
|
|
57
57
|
const isSteps = formLayout?.mode === 'steps' && groups.length > 1;
|
|
58
58
|
// Wizard step cursor (steps mode only). Clamped whenever the group count
|
|
59
59
|
// shrinks (a visible_when flip can empty a trailing step's section).
|
package/dist/dynamic-table.d.ts
CHANGED
|
@@ -10,6 +10,15 @@ export interface DynamicTableProps {
|
|
|
10
10
|
*/
|
|
11
11
|
mutationEndpoint?: string;
|
|
12
12
|
enableUrlSync?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Hide the import action on THIS view even when the model supports it.
|
|
15
|
+
* A role-scoped view (e.g. a rep seeing only their own records) usually
|
|
16
|
+
* wants the table without a bulk-import entry point, while the same model
|
|
17
|
+
* keeps it on the admin view. Absent → the model's metadata decides.
|
|
18
|
+
*/
|
|
19
|
+
hideImport?: boolean;
|
|
20
|
+
/** Hide the export action on this view. See `hideImport`. */
|
|
21
|
+
hideExport?: boolean;
|
|
13
22
|
hiddenColumns?: string[];
|
|
14
23
|
onAction?: (action: string, row: any) => void;
|
|
15
24
|
/**
|
|
@@ -64,5 +73,5 @@ export interface DynamicTableProps {
|
|
|
64
73
|
*/
|
|
65
74
|
infiniteScroll?: boolean;
|
|
66
75
|
}
|
|
67
|
-
export declare function DynamicTable({ model, endpoint, mutationEndpoint, enableUrlSync, hiddenColumns, onAction, onRowClick, refreshTrigger, defaultFilters, extraColumns, getDynamicColumns, timeZone, currency, pagination: paginationMode, infiniteScroll: infiniteScrollProp, }: DynamicTableProps): import("react").JSX.Element;
|
|
76
|
+
export declare function DynamicTable({ model, endpoint, mutationEndpoint, enableUrlSync, hideImport, hideExport, hiddenColumns, onAction, onRowClick, refreshTrigger, defaultFilters, extraColumns, getDynamicColumns, timeZone, currency, pagination: paginationMode, infiniteScroll: infiniteScrollProp, }: DynamicTableProps): import("react").JSX.Element;
|
|
68
77
|
//# sourceMappingURL=dynamic-table.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dynamic-table.d.ts","sourceRoot":"","sources":["../src/dynamic-table.tsx"],"names":[],"mappings":"AAgBA,OAAO,EAKH,KAAK,SAAS,EAajB,MAAM,uBAAuB,CAAA;AAgC9B,OAAO,KAAK,EAAsB,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AA2FnF,MAAM,WAAW,iBAAiB;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,IAAI,CAAA;IAC7C;;;;;OAKG;IACH,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,CAAA;IAC/B,cAAc,CAAC,EAAE,GAAG,CAAA;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACpC,YAAY,CAAC,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,CAAA;IAC/B;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;IACrC;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;;;;;;;;;;OAaG;IACH,UAAU,CAAC,EAAE,OAAO,GAAG,UAAU,CAAA;IACjC;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;CAC3B;AAED,wBAAgB,YAAY,CAAC,EACzB,KAAK,EACL,QAAQ,EACR,gBAAgB,EAChB,aAAoB,EACpB,aAAkB,EAClB,QAAQ,EACR,UAAU,EACV,cAAc,EACd,cAAc,EACd,YAAiB,EACjB,iBAA4C,EAC5C,QAAQ,EACR,QAAQ,EACR,UAAU,EAAE,cAAc,EAC1B,cAAc,EAAE,kBAA0B,GAC7C,EAAE,iBAAiB,+
|
|
1
|
+
{"version":3,"file":"dynamic-table.d.ts","sourceRoot":"","sources":["../src/dynamic-table.tsx"],"names":[],"mappings":"AAgBA,OAAO,EAKH,KAAK,SAAS,EAajB,MAAM,uBAAuB,CAAA;AAgC9B,OAAO,KAAK,EAAsB,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AA2FnF,MAAM,WAAW,iBAAiB;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,6DAA6D;IAC7D,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,IAAI,CAAA;IAC7C;;;;;OAKG;IACH,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,CAAA;IAC/B,cAAc,CAAC,EAAE,GAAG,CAAA;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACpC,YAAY,CAAC,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,CAAA;IAC/B;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;IACrC;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;;;;;;;;;;OAaG;IACH,UAAU,CAAC,EAAE,OAAO,GAAG,UAAU,CAAA;IACjC;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;CAC3B;AAED,wBAAgB,YAAY,CAAC,EACzB,KAAK,EACL,QAAQ,EACR,gBAAgB,EAChB,aAAoB,EACpB,UAAU,EACV,UAAU,EACV,aAAkB,EAClB,QAAQ,EACR,UAAU,EACV,cAAc,EACd,cAAc,EACd,YAAiB,EACjB,iBAA4C,EAC5C,QAAQ,EACR,QAAQ,EACR,UAAU,EAAE,cAAc,EAC1B,cAAc,EAAE,kBAA0B,GAC7C,EAAE,iBAAiB,+BAgvCnB"}
|
package/dist/dynamic-table.js
CHANGED
|
@@ -103,7 +103,7 @@ function writeStoredPageSize(model, size) {
|
|
|
103
103
|
// quota / private mode — persistence is a nicety, never fatal
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
|
-
export function DynamicTable({ model, endpoint, mutationEndpoint, enableUrlSync = true, hiddenColumns = [], onAction, onRowClick, refreshTrigger, defaultFilters, extraColumns = [], getDynamicColumns = defaultGetDynamicColumns, timeZone, currency, pagination: paginationMode, infiniteScroll: infiniteScrollProp = false, }) {
|
|
106
|
+
export function DynamicTable({ model, endpoint, mutationEndpoint, enableUrlSync = true, hideImport, hideExport, hiddenColumns = [], onAction, onRowClick, refreshTrigger, defaultFilters, extraColumns = [], getDynamicColumns = defaultGetDynamicColumns, timeZone, currency, pagination: paginationMode, infiniteScroll: infiniteScrollProp = false, }) {
|
|
107
107
|
// The explicit `pagination` prop wins; the legacy `infiniteScroll` boolean
|
|
108
108
|
// still selects the mode when `pagination` is absent (back-compat).
|
|
109
109
|
const infiniteScroll = paginationMode ? paginationMode === 'infinite' : infiniteScrollProp;
|
|
@@ -519,6 +519,13 @@ export function DynamicTable({ model, endpoint, mutationEndpoint, enableUrlSync
|
|
|
519
519
|
return metadata;
|
|
520
520
|
return gateTableMetadata(metadata, model, can, (key, fallback) => t(key, { defaultValue: fallback }));
|
|
521
521
|
}, [metadata, permissionsActive, can, model, t]);
|
|
522
|
+
// Importing is offered when the host opts in explicitly (`canImport`) or,
|
|
523
|
+
// when it says nothing, whenever the kernel served an import spec for the
|
|
524
|
+
// model — which it does for every model with at least one importable form
|
|
525
|
+
// field. That makes the flow work out of the box instead of requiring each
|
|
526
|
+
// host to flip a flag per view.
|
|
527
|
+
const importEnabled = !hideImport && (viewMetadata?.canImport ?? Boolean(viewMetadata?.import?.columns?.length));
|
|
528
|
+
const exportEnabled = !hideExport && Boolean(viewMetadata?.canExport);
|
|
522
529
|
const buildFilterParams = useCallback(() => {
|
|
523
530
|
const params = {};
|
|
524
531
|
if (sorting.length > 0) {
|
|
@@ -1028,7 +1035,7 @@ export function DynamicTable({ model, endpoint, mutationEndpoint, enableUrlSync
|
|
|
1028
1035
|
if (!metadata) {
|
|
1029
1036
|
return _jsx("div", { className: "text-center text-muted-foreground py-8", children: "Error al cargar la configuraci\u00F3n de la tabla." });
|
|
1030
1037
|
}
|
|
1031
|
-
return (_jsxs(OptionsContext.Provider, { value: { optionsMap }, children: [_jsxs("div", { className: 'flex flex-col h-full min-h-0 w-full', children: [_jsx("div", { className: 'pb-4 shrink-0', children: _jsx(DataTableToolbar, { table: table, searchPlaceholder: metadata.searchPlaceholder || 'Buscar...', filters: filters, activeFilters: dynamicFilters, onDynamicFilterChange: handleDynamicFilterChange, dateFilter: { value: dateRange, onChange: setDateRange, placeholder: 'Filtrar por fecha' }, perPageOptions: metadata.perPageOptions, onRefresh: handleRefresh, isLoading: loadingData, selectedCount: Object.keys(rowSelection).length, onBulkDelete: () => setShowBulkDeleteConfirm(true), extraActions: _jsxs(_Fragment, { children: [
|
|
1038
|
+
return (_jsxs(OptionsContext.Provider, { value: { optionsMap }, children: [_jsxs("div", { className: 'flex flex-col h-full min-h-0 w-full', children: [_jsx("div", { className: 'pb-4 shrink-0', children: _jsx(DataTableToolbar, { table: table, searchPlaceholder: metadata.searchPlaceholder || 'Buscar...', filters: filters, activeFilters: dynamicFilters, onDynamicFilterChange: handleDynamicFilterChange, dateFilter: { value: dateRange, onChange: setDateRange, placeholder: 'Filtrar por fecha' }, perPageOptions: metadata.perPageOptions, onRefresh: handleRefresh, isLoading: loadingData, selectedCount: Object.keys(rowSelection).length, onBulkDelete: () => setShowBulkDeleteConfirm(true), extraActions: _jsxs(_Fragment, { children: [exportEnabled && (_jsxs(Button, { variant: "outline", size: "sm", className: "h-8", onClick: () => setExportOpen(true), children: [_jsx(Download, { className: "h-4 w-4 mr-1" }), " Exportar"] })), importEnabled && (_jsxs(Button, { variant: "outline", size: "sm", className: "h-8", onClick: () => setImportOpen(true), children: [_jsx(Upload, { className: "h-4 w-4 mr-1" }), " Importar"] }))] }) }) }), _jsxs("div", { ref: infDesktopRoot, className: 'hidden sm:block flex-1 min-h-0 overflow-auto border rounded-md bg-card', children: [_jsxs(Table, { noWrapper: true, className: cn('min-w-max w-full', aggregateColumns.length > 0 && Object.keys(footerTotals).length > 0 && 'h-full'), children: [_jsx(TableHeader, { className: 'sticky top-0 z-10', children: table.getHeaderGroups().map((headerGroup) => (_jsx(TableRow, { className: 'border-b-0 hover:bg-transparent', children: headerGroup.headers.map((header) => {
|
|
1032
1039
|
const isActionsColumn = header.id === 'actions';
|
|
1033
1040
|
return (_jsx(TableHead, { colSpan: header.colSpan, style: header.column.columnDef.size ? { width: header.column.columnDef.size } : undefined, className: cn('bg-card border-b h-10', isActionsColumn && 'sticky right-0 z-20 bg-card shadow-[-2px_0_5px_-2px_rgba(0,0,0,0.1)]'), children: header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext()) }, header.id));
|
|
1034
1041
|
}) }, headerGroup.id))) }), _jsx(TableBody, { children: loadingData && data.length === 0 ? (_jsx(TableSkeleton, {})) : table.getRowModel().rows?.length ? (_jsxs(_Fragment, { children: [table.getRowModel().rows.map((row) => (_jsx(TableRow, { "data-state": row.getIsSelected() && 'selected', className: cn(onRowClick && 'cursor-pointer'), onClick: onRowClick ? () => onRowClick(row.original) : undefined, children: row.getVisibleCells().map((cell) => {
|
|
@@ -1065,5 +1072,5 @@ export function DynamicTable({ model, endpoint, mutationEndpoint, enableUrlSync
|
|
|
1065
1072
|
defaultValue: '{{count}} de {{total}}',
|
|
1066
1073
|
count: data.length,
|
|
1067
1074
|
total: rowCount,
|
|
1068
|
-
}) }))) : (_jsx(DataTablePagination, { table: table, pageSizeOptions: metadata.perPageOptions })) })] }), rowActionDialogs, _jsx(AlertDialog, { open: showBulkDeleteConfirm, onOpenChange: (open) => !open && !isBulkDeleting && setShowBulkDeleteConfirm(false), children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: isBulkDeleting ? 'Eliminando registros...' : '¿Eliminar múltiples registros?' }), _jsx(AlertDialogDescription, { children: isBulkDeleting ? (_jsxs("div", { className: "space-y-4 mt-4", children: [_jsx(Progress, { value: (bulkDeleteProgress / bulkDeleteTotal) * 100 }), _jsxs("p", { className: "text-center text-sm", children: ["Procesando ", bulkDeleteProgress, " de ", bulkDeleteTotal, " registros..."] })] })) : (_jsxs(_Fragment, { children: ["Esta acci\u00F3n no se puede deshacer. Se eliminar\u00E1n permanentemente ", _jsx("strong", { children: Object.keys(rowSelection).length }), " registro(s) de nuestros servidores."] })) })] }), !isBulkDeleting && (_jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { children: t('common.cancel') }), _jsx(AlertDialogAction, { onClick: (e) => { e.preventDefault(); confirmBulkDelete(); }, className: "bg-red-600 hover:bg-red-700", children: "Eliminar todos" })] }))] }) }),
|
|
1075
|
+
}) }))) : (_jsx(DataTablePagination, { table: table, pageSizeOptions: metadata.perPageOptions })) })] }), rowActionDialogs, _jsx(AlertDialog, { open: showBulkDeleteConfirm, onOpenChange: (open) => !open && !isBulkDeleting && setShowBulkDeleteConfirm(false), children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: isBulkDeleting ? 'Eliminando registros...' : '¿Eliminar múltiples registros?' }), _jsx(AlertDialogDescription, { children: isBulkDeleting ? (_jsxs("div", { className: "space-y-4 mt-4", children: [_jsx(Progress, { value: (bulkDeleteProgress / bulkDeleteTotal) * 100 }), _jsxs("p", { className: "text-center text-sm", children: ["Procesando ", bulkDeleteProgress, " de ", bulkDeleteTotal, " registros..."] })] })) : (_jsxs(_Fragment, { children: ["Esta acci\u00F3n no se puede deshacer. Se eliminar\u00E1n permanentemente ", _jsx("strong", { children: Object.keys(rowSelection).length }), " registro(s) de nuestros servidores."] })) })] }), !isBulkDeleting && (_jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { children: t('common.cancel') }), _jsx(AlertDialogAction, { onClick: (e) => { e.preventDefault(); confirmBulkDelete(); }, className: "bg-red-600 hover:bg-red-700", children: "Eliminar todos" })] }))] }) }), exportEnabled && (_jsx(ExportDialog, { open: exportOpen, onOpenChange: setExportOpen, model: model, metadata: metadata, currentFilters: buildFilterParams(), hasActiveFilters: hasActiveFilters })), importEnabled && (_jsx(ImportDialog, { open: importOpen, onOpenChange: setImportOpen, model: model, metadata: metadata, onImported: handleRefresh })), _jsx(DataTableBulkActions, { table: table, entityName: "registro", children: _jsxs(Button, { variant: "destructive", size: "sm", className: "h-8", onClick: () => setShowBulkDeleteConfirm(true), children: [_jsx(Trash2, { className: "h-4 w-4 mr-1.5" }), " Eliminar"] }) })] }));
|
|
1069
1076
|
}
|
package/dist/form-layout.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { VisibleWhen } from './types';
|
|
1
2
|
/** One declared section of a model form. `title`/`description` arrive already
|
|
2
3
|
* localized from the kernel and are rendered verbatim. */
|
|
3
4
|
export interface FormSection {
|
|
@@ -6,6 +7,12 @@ export interface FormSection {
|
|
|
6
7
|
description?: string;
|
|
7
8
|
/** Sections mode only: render this section collapsed on first paint. */
|
|
8
9
|
collapsed?: boolean;
|
|
10
|
+
/** Section-level visibility gate (kernel v0.84.0). When present and its
|
|
11
|
+
* predicate evaluates false against the live form values, the whole section
|
|
12
|
+
* is omitted (and in steps mode its wizard step drops from the sequence).
|
|
13
|
+
* Tolerates the camelCase alias, same as fields. */
|
|
14
|
+
visible_when?: VisibleWhen;
|
|
15
|
+
visibleWhen?: VisibleWhen;
|
|
9
16
|
}
|
|
10
17
|
/** Model-level layout directive. `mode` defaults to `"sections"`. */
|
|
11
18
|
export interface FormLayout {
|
|
@@ -47,8 +54,15 @@ export declare function getFieldSection(field: {
|
|
|
47
54
|
* section whose only members are hidden by `visible_when` never renders an
|
|
48
55
|
* empty shell (and never yields an empty wizard step). Because the caller
|
|
49
56
|
* passes the already visibility-filtered list, this falls out for free.
|
|
57
|
+
* - A section declaring its OWN `visible_when` is dropped whole (before its
|
|
58
|
+
* fields are even collected) whenever the predicate evaluates false against
|
|
59
|
+
* `values`, reusing the SAME evaluator the fields use. In steps mode the
|
|
60
|
+
* caller derives the wizard sequence from these groups, so a hidden section
|
|
61
|
+
* simply never becomes a step. Section-less / unknown-section fields are
|
|
62
|
+
* never gated by any section predicate. Without a section `visible_when`
|
|
63
|
+
* (or without `values`), behaviour is byte-for-byte the legacy path.
|
|
50
64
|
*/
|
|
51
65
|
export declare function groupFieldsBySection<F extends {
|
|
52
66
|
section?: string;
|
|
53
|
-
}>(fields: F[], layout: FormLayout | undefined): FieldGroup<F>[];
|
|
67
|
+
}>(fields: F[], layout: FormLayout | undefined, values?: Record<string, any> | null): FieldGroup<F>[];
|
|
54
68
|
//# sourceMappingURL=form-layout.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"form-layout.d.ts","sourceRoot":"","sources":["../src/form-layout.ts"],"names":[],"mappings":"AAgBA;0DAC0D;AAC1D,MAAM,WAAW,WAAW;IACxB,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,wEAAwE;IACxE,SAAS,CAAC,EAAE,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"form-layout.d.ts","sourceRoot":"","sources":["../src/form-layout.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAG1C;0DAC0D;AAC1D,MAAM,WAAW,WAAW;IACxB,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,wEAAwE;IACxE,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB;;;wDAGoD;IACpD,YAAY,CAAC,EAAE,WAAW,CAAA;IAC1B,WAAW,CAAC,EAAE,WAAW,CAAA;CAC5B;AAED,qEAAqE;AACrE,MAAM,WAAW,UAAU;IACvB,IAAI,CAAC,EAAE,UAAU,GAAG,OAAO,CAAA;IAC3B,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAA;CAC3B;AAED;oDACoD;AACpD,MAAM,WAAW,UAAU,CAAC,CAAC;IACzB,0DAA0D;IAC1D,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,kFAAkF;IAClF,SAAS,EAAE,OAAO,CAAA;IAClB,MAAM,EAAE,CAAC,EAAE,CAAA;CACd;AAED,kFAAkF;AAClF,eAAO,MAAM,mBAAmB,gBAAgB,CAAA;AAEhD,yEAAyE;AACzE,wBAAgB,eAAe,CAC3B,KAAK,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,GACjE,MAAM,GAAG,SAAS,CAIpB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,SAAS;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,EAC/D,MAAM,EAAE,CAAC,EAAE,EACX,MAAM,EAAE,UAAU,GAAG,SAAS,EAC9B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,GACpC,UAAU,CAAC,CAAC,CAAC,EAAE,CAoDjB"}
|
package/dist/form-layout.js
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
// (`dynamic-form.tsx`, `dialogs/dynamic-record.tsx`) share one tested
|
|
14
14
|
// implementation and the modal-render machinery never has to be booted under
|
|
15
15
|
// happy-dom just to assert the grouping (same approach as PR #673).
|
|
16
|
+
import { evaluateVisibleWhen, getVisibleWhen } from './dynamic-form-schema';
|
|
16
17
|
/** The synthetic key of the orphan group (fields with no / unknown `section`). */
|
|
17
18
|
export const DEFAULT_SECTION_KEY = '__default__';
|
|
18
19
|
/** Reads a field's `section` reference, tolerating a camelCase alias. */
|
|
@@ -38,12 +39,27 @@ export function getFieldSection(field) {
|
|
|
38
39
|
* section whose only members are hidden by `visible_when` never renders an
|
|
39
40
|
* empty shell (and never yields an empty wizard step). Because the caller
|
|
40
41
|
* passes the already visibility-filtered list, this falls out for free.
|
|
42
|
+
* - A section declaring its OWN `visible_when` is dropped whole (before its
|
|
43
|
+
* fields are even collected) whenever the predicate evaluates false against
|
|
44
|
+
* `values`, reusing the SAME evaluator the fields use. In steps mode the
|
|
45
|
+
* caller derives the wizard sequence from these groups, so a hidden section
|
|
46
|
+
* simply never becomes a step. Section-less / unknown-section fields are
|
|
47
|
+
* never gated by any section predicate. Without a section `visible_when`
|
|
48
|
+
* (or without `values`), behaviour is byte-for-byte the legacy path.
|
|
41
49
|
*/
|
|
42
|
-
export function groupFieldsBySection(fields, layout) {
|
|
50
|
+
export function groupFieldsBySection(fields, layout, values) {
|
|
43
51
|
if (!layout?.sections?.length) {
|
|
44
52
|
return [{ key: DEFAULT_SECTION_KEY, isDefault: true, fields }];
|
|
45
53
|
}
|
|
46
|
-
|
|
54
|
+
// Sections whose own `visible_when` predicate evaluates false are hidden
|
|
55
|
+
// WHOLESALE: the section never emits a group/step AND its member fields are
|
|
56
|
+
// dropped (they must not leak into the default group). Reuses the same
|
|
57
|
+
// field-level evaluator — no duplicated logic. A `declared` set keeps a
|
|
58
|
+
// field targeting a hidden section from being treated as "unknown section"
|
|
59
|
+
// and orphaned.
|
|
60
|
+
const declared = new Set(layout.sections.map((s) => s.key));
|
|
61
|
+
const visibleSections = layout.sections.filter((s) => evaluateVisibleWhen(getVisibleWhen(s), values));
|
|
62
|
+
const known = new Set(visibleSections.map((s) => s.key));
|
|
47
63
|
const bySection = new Map();
|
|
48
64
|
const orphans = [];
|
|
49
65
|
for (const f of fields) {
|
|
@@ -55,6 +71,10 @@ export function groupFieldsBySection(fields, layout) {
|
|
|
55
71
|
else
|
|
56
72
|
bySection.set(sec, [f]);
|
|
57
73
|
}
|
|
74
|
+
else if (sec && declared.has(sec)) {
|
|
75
|
+
// Belongs to a declared-but-hidden section → drop the field.
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
58
78
|
else {
|
|
59
79
|
orphans.push(f);
|
|
60
80
|
}
|
|
@@ -64,7 +84,7 @@ export function groupFieldsBySection(fields, layout) {
|
|
|
64
84
|
if (orphans.length) {
|
|
65
85
|
groups.push({ key: DEFAULT_SECTION_KEY, isDefault: true, fields: orphans });
|
|
66
86
|
}
|
|
67
|
-
for (const s of
|
|
87
|
+
for (const s of visibleSections) {
|
|
68
88
|
const secFields = bySection.get(s.key);
|
|
69
89
|
if (!secFields || secFields.length === 0)
|
|
70
90
|
continue; // hide empty section
|
package/dist/types.d.ts
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
/** One spreadsheet column of a model's import template. Mirrors the kernel's
|
|
2
|
+
* `modelbase.ImportColumn`. */
|
|
3
|
+
export interface ImportColumnMeta {
|
|
4
|
+
key: string;
|
|
5
|
+
header: string;
|
|
6
|
+
aliases?: string[];
|
|
7
|
+
required?: boolean;
|
|
8
|
+
type?: string;
|
|
9
|
+
example?: string;
|
|
10
|
+
hint?: string;
|
|
11
|
+
generator?: string;
|
|
12
|
+
}
|
|
13
|
+
/** A model's spreadsheet-import declaration. Mirrors `modelbase.ImportSpec`. */
|
|
14
|
+
export interface ImportSpecMeta {
|
|
15
|
+
columns: ImportColumnMeta[];
|
|
16
|
+
maxRows?: number;
|
|
17
|
+
sheetName?: string;
|
|
18
|
+
instructions?: string[];
|
|
19
|
+
}
|
|
1
20
|
export interface TableMetadata {
|
|
2
21
|
title: string;
|
|
3
22
|
endpoint: string;
|
|
@@ -12,6 +31,16 @@ export interface TableMetadata {
|
|
|
12
31
|
canExport?: boolean;
|
|
13
32
|
canImport?: boolean;
|
|
14
33
|
canCreate?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* The model's spreadsheet-import declaration, served by the kernel: the
|
|
36
|
+
* columns of the generated template and the headers accepted when parsing
|
|
37
|
+
* a filled file back in. The kernel derives it from the model's form
|
|
38
|
+
* fields when the model declares nothing, and omits the field entirely
|
|
39
|
+
* when the model has no importable column — so its presence is the signal
|
|
40
|
+
* that importing is meaningful for this model. Purely additive; older
|
|
41
|
+
* kernels omit it.
|
|
42
|
+
*/
|
|
43
|
+
import?: ImportSpecMeta;
|
|
15
44
|
/**
|
|
16
45
|
* Child relations of this model, served by the kernel (>= v0.41.0). A
|
|
17
46
|
* generic detail page renders one `DynamicRelation` panel per entry via
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -111,3 +111,91 @@ describe('groupFieldsBySection — steps', () => {
|
|
|
111
111
|
expect(groups.map(g => g.key)).toEqual(['who', 'pay'])
|
|
112
112
|
})
|
|
113
113
|
})
|
|
114
|
+
|
|
115
|
+
describe('groupFieldsBySection — section-level visible_when (kernel v0.84.0)', () => {
|
|
116
|
+
// A discriminator field `kind` gates the `company` section: only visible for
|
|
117
|
+
// business customers. Tolerate both snake_case and camelCase authoring.
|
|
118
|
+
const gatedLayout: FormLayout = {
|
|
119
|
+
mode: 'sections',
|
|
120
|
+
sections: [
|
|
121
|
+
{ key: 'general', title: 'General' },
|
|
122
|
+
{
|
|
123
|
+
key: 'company',
|
|
124
|
+
title: 'Empresa',
|
|
125
|
+
visible_when: { field: 'kind', equals: 'business' },
|
|
126
|
+
},
|
|
127
|
+
],
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const fields: F[] = [
|
|
131
|
+
{ key: 'name', section: 'general' },
|
|
132
|
+
{ key: 'tax_id', section: 'company' },
|
|
133
|
+
]
|
|
134
|
+
|
|
135
|
+
it('hides the section AND its fields when the predicate is false', () => {
|
|
136
|
+
const groups = groupFieldsBySection(fields, gatedLayout, { kind: 'person' })
|
|
137
|
+
// `company` never appears, and `tax_id` is not leaked into any group.
|
|
138
|
+
expect(groups.map(g => g.key)).toEqual(['general'])
|
|
139
|
+
const allFields = groups.flatMap(g => g.fields.map(f => f.key))
|
|
140
|
+
expect(allFields).toEqual(['name'])
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
it('shows the section when the predicate is satisfied', () => {
|
|
144
|
+
const groups = groupFieldsBySection(fields, gatedLayout, { kind: 'business' })
|
|
145
|
+
expect(groups.map(g => g.key)).toEqual(['general', 'company'])
|
|
146
|
+
expect(groups[1].fields.map(f => f.key)).toEqual(['tax_id'])
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
it('tolerates the camelCase visibleWhen alias', () => {
|
|
150
|
+
const camelLayout: FormLayout = {
|
|
151
|
+
mode: 'sections',
|
|
152
|
+
sections: [
|
|
153
|
+
{ key: 'general', title: 'General' },
|
|
154
|
+
{ key: 'company', visibleWhen: { field: 'kind', in: ['business'] } },
|
|
155
|
+
],
|
|
156
|
+
}
|
|
157
|
+
expect(groupFieldsBySection(fields, camelLayout, { kind: 'person' }).map(g => g.key)).toEqual([
|
|
158
|
+
'general',
|
|
159
|
+
])
|
|
160
|
+
expect(groupFieldsBySection(fields, camelLayout, { kind: 'business' }).map(g => g.key)).toEqual([
|
|
161
|
+
'general',
|
|
162
|
+
'company',
|
|
163
|
+
])
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
it('without values (or without a section predicate) behaves exactly like before', () => {
|
|
167
|
+
// No values passed → a predicated section reads its gate field as '' and
|
|
168
|
+
// stays hidden (equals miss), while an ungated layout is untouched.
|
|
169
|
+
const plain = groupFieldsBySection(fields, {
|
|
170
|
+
mode: 'sections',
|
|
171
|
+
sections: [{ key: 'general' }, { key: 'company' }],
|
|
172
|
+
})
|
|
173
|
+
expect(plain.map(g => g.key)).toEqual(['general', 'company'])
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
it('drops a wizard step whose section is gated off, preserving navigable order', () => {
|
|
177
|
+
const stepsGated: FormLayout = {
|
|
178
|
+
mode: 'steps',
|
|
179
|
+
sections: [
|
|
180
|
+
{ key: 'who', title: 'Cliente' },
|
|
181
|
+
{
|
|
182
|
+
key: 'company',
|
|
183
|
+
title: 'Empresa',
|
|
184
|
+
visible_when: { field: 'kind', equals: 'business' },
|
|
185
|
+
},
|
|
186
|
+
{ key: 'pay', title: 'Pago' },
|
|
187
|
+
],
|
|
188
|
+
}
|
|
189
|
+
const stepFields: F[] = [
|
|
190
|
+
{ key: 'customer_id', section: 'who' },
|
|
191
|
+
{ key: 'tax_id', section: 'company' },
|
|
192
|
+
{ key: 'amount', section: 'pay' },
|
|
193
|
+
]
|
|
194
|
+
// person → company step removed: wizard is who → pay, submit still on last.
|
|
195
|
+
const hidden = groupFieldsBySection(stepFields, stepsGated, { kind: 'person' })
|
|
196
|
+
expect(hidden.map(g => g.key)).toEqual(['who', 'pay'])
|
|
197
|
+
// business → all three steps in order.
|
|
198
|
+
const shown = groupFieldsBySection(stepFields, stepsGated, { kind: 'business' })
|
|
199
|
+
expect(shown.map(g => g.key)).toEqual(['who', 'company', 'pay'])
|
|
200
|
+
})
|
|
201
|
+
})
|
|
@@ -884,7 +884,7 @@ export function DynamicRecordDialog({
|
|
|
884
884
|
// their section. Empty sections drop out for free. Steps mode only drives a
|
|
885
885
|
// wizard in editable modes — view mode always stacks the sections.
|
|
886
886
|
const formLayout = modalMeta?.form_layout ?? modalMeta?.formLayout
|
|
887
|
-
const groups = groupFieldsBySection(visibleFields, formLayout)
|
|
887
|
+
const groups = groupFieldsBySection(visibleFields, formLayout, formValues)
|
|
888
888
|
const isSteps = isEditable && formLayout?.mode === 'steps' && groups.length > 1
|
|
889
889
|
const clampedStep = Math.min(stepIndex, Math.max(groups.length - 1, 0))
|
|
890
890
|
const isLastStep = clampedStep === groups.length - 1
|
package/src/dialogs/import.tsx
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
// ImportDialog — three-step CSV/JSON import flow (upload → validate →
|
|
2
|
-
// with per-row error report).
|
|
1
|
+
// ImportDialog — three-step XLSX/CSV/JSON import flow (upload → validate →
|
|
2
|
+
// import with per-row error report). The template, the accepted columns and
|
|
3
|
+
// the validation rules all come from the model's ImportSpec served by the
|
|
4
|
+
// kernel, so this dialog carries no per-model knowledge. Axios-like client is
|
|
5
|
+
// provided by <ApiProvider>.
|
|
3
6
|
import { useState, useEffect, useRef } from 'react'
|
|
4
7
|
import {
|
|
5
8
|
Dialog,
|
|
@@ -32,20 +35,37 @@ interface ImportDialogProps {
|
|
|
32
35
|
onImported?: () => void
|
|
33
36
|
}
|
|
34
37
|
|
|
35
|
-
|
|
38
|
+
// RowError is one problem the backend reports against one spreadsheet row.
|
|
39
|
+
// The kernel returns `{row, column, message}` on validate and
|
|
40
|
+
// `{row, column, error}` on import; older backends returned `{row, field,
|
|
41
|
+
// message}`. Normalising here keeps the dialog working against every one of
|
|
42
|
+
// them instead of rendering blank cells.
|
|
43
|
+
interface RowError {
|
|
36
44
|
row: number
|
|
37
|
-
|
|
38
|
-
|
|
45
|
+
column?: string
|
|
46
|
+
field?: string
|
|
47
|
+
message?: string
|
|
48
|
+
error?: string
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function errorColumn(e: RowError): string {
|
|
52
|
+
return e.column ?? e.field ?? ''
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function errorMessage(e: RowError): string {
|
|
56
|
+
return e.message ?? e.error ?? ''
|
|
39
57
|
}
|
|
40
58
|
|
|
41
59
|
interface ValidationResult {
|
|
42
60
|
valid: number
|
|
43
|
-
|
|
61
|
+
skipped: number
|
|
62
|
+
errors: RowError[]
|
|
44
63
|
}
|
|
45
64
|
|
|
46
65
|
interface ImportResult {
|
|
47
66
|
created: number
|
|
48
|
-
|
|
67
|
+
skipped: number
|
|
68
|
+
errors: RowError[]
|
|
49
69
|
}
|
|
50
70
|
|
|
51
71
|
type Step = 'upload' | 'validation' | 'results'
|
|
@@ -93,7 +113,7 @@ export function ImportDialog({
|
|
|
93
113
|
const url = window.URL.createObjectURL(response.data)
|
|
94
114
|
const link = document.createElement('a')
|
|
95
115
|
link.href = url
|
|
96
|
-
link.download = `${model}-plantilla.
|
|
116
|
+
link.download = `${model}-plantilla.xlsx`
|
|
97
117
|
document.body.appendChild(link)
|
|
98
118
|
link.click()
|
|
99
119
|
document.body.removeChild(link)
|
|
@@ -120,7 +140,8 @@ export function ImportDialog({
|
|
|
120
140
|
|
|
121
141
|
const data = res.data?.data ?? res.data
|
|
122
142
|
setValidationResult({
|
|
123
|
-
valid: data.valid ?? 0,
|
|
143
|
+
valid: data.rowCount ?? data.valid ?? 0,
|
|
144
|
+
skipped: data.skipped ?? 0,
|
|
124
145
|
errors: data.errors ?? [],
|
|
125
146
|
})
|
|
126
147
|
setStep('validation')
|
|
@@ -157,7 +178,8 @@ export function ImportDialog({
|
|
|
157
178
|
const data = res.data?.data ?? res.data
|
|
158
179
|
setImportResult({
|
|
159
180
|
created: data.created ?? 0,
|
|
160
|
-
|
|
181
|
+
skipped: data.skipped ?? 0,
|
|
182
|
+
errors: data.failures ?? data.errors ?? [],
|
|
161
183
|
})
|
|
162
184
|
setStep('results')
|
|
163
185
|
|
|
@@ -206,10 +228,11 @@ export function ImportDialog({
|
|
|
206
228
|
onClick={handleDownloadTemplate}
|
|
207
229
|
>
|
|
208
230
|
<FileDown className="h-4 w-4 mr-1" />
|
|
209
|
-
Descargar plantilla
|
|
231
|
+
Descargar plantilla
|
|
210
232
|
</Button>
|
|
211
233
|
<p className="text-xs text-muted-foreground mt-1">
|
|
212
|
-
|
|
234
|
+
La plantilla trae las columnas, un ejemplo y las
|
|
235
|
+
instrucciones. Borra la fila de ejemplo antes de subir.
|
|
213
236
|
</p>
|
|
214
237
|
</div>
|
|
215
238
|
|
|
@@ -221,12 +244,12 @@ export function ImportDialog({
|
|
|
221
244
|
ref={fileInputRef}
|
|
222
245
|
id="import-file"
|
|
223
246
|
type="file"
|
|
224
|
-
accept=".csv,.json"
|
|
247
|
+
accept=".xlsx,.xls,.csv,.json"
|
|
225
248
|
onChange={handleFileChange}
|
|
226
249
|
className="cursor-pointer"
|
|
227
250
|
/>
|
|
228
251
|
<p className="text-xs text-muted-foreground">
|
|
229
|
-
Formatos aceptados: CSV, JSON
|
|
252
|
+
Formatos aceptados: Excel, CSV, JSON
|
|
230
253
|
</p>
|
|
231
254
|
</div>
|
|
232
255
|
</div>
|
|
@@ -251,6 +274,13 @@ export function ImportDialog({
|
|
|
251
274
|
)}
|
|
252
275
|
</div>
|
|
253
276
|
|
|
277
|
+
{validationResult.skipped > 0 && (
|
|
278
|
+
<p className="text-xs text-muted-foreground">
|
|
279
|
+
Se ignoraron {validationResult.skipped} fila(s) de ejemplo
|
|
280
|
+
de la plantilla.
|
|
281
|
+
</p>
|
|
282
|
+
)}
|
|
283
|
+
|
|
254
284
|
{validationResult.errors.length > 0 && (
|
|
255
285
|
<div className="border rounded-md max-h-60 overflow-auto">
|
|
256
286
|
<Table>
|
|
@@ -268,10 +298,10 @@ export function ImportDialog({
|
|
|
268
298
|
{error.row}
|
|
269
299
|
</TableCell>
|
|
270
300
|
<TableCell className="text-sm">
|
|
271
|
-
{error
|
|
301
|
+
{errorColumn(error)}
|
|
272
302
|
</TableCell>
|
|
273
303
|
<TableCell className="text-sm text-destructive">
|
|
274
|
-
{error
|
|
304
|
+
{errorMessage(error)}
|
|
275
305
|
</TableCell>
|
|
276
306
|
</TableRow>
|
|
277
307
|
))}
|
|
@@ -336,10 +366,10 @@ export function ImportDialog({
|
|
|
336
366
|
{error.row}
|
|
337
367
|
</TableCell>
|
|
338
368
|
<TableCell className="text-sm">
|
|
339
|
-
{error
|
|
369
|
+
{errorColumn(error)}
|
|
340
370
|
</TableCell>
|
|
341
371
|
<TableCell className="text-sm text-destructive">
|
|
342
|
-
{error
|
|
372
|
+
{errorMessage(error)}
|
|
343
373
|
</TableCell>
|
|
344
374
|
</TableRow>
|
|
345
375
|
))}
|
package/src/dynamic-form.tsx
CHANGED
|
@@ -113,8 +113,8 @@ export function DynamicForm({
|
|
|
113
113
|
// this is a single default group → the render below collapses to the legacy
|
|
114
114
|
// flat grid, byte-for-byte.
|
|
115
115
|
const groups = useMemo(
|
|
116
|
-
() => groupFieldsBySection(visibleFields, formLayout),
|
|
117
|
-
[visibleFields, formLayout],
|
|
116
|
+
() => groupFieldsBySection(visibleFields, formLayout, values),
|
|
117
|
+
[visibleFields, formLayout, values],
|
|
118
118
|
)
|
|
119
119
|
const isSteps = formLayout?.mode === 'steps' && groups.length > 1
|
|
120
120
|
|
package/src/dynamic-table.tsx
CHANGED
|
@@ -165,6 +165,15 @@ export interface DynamicTableProps {
|
|
|
165
165
|
*/
|
|
166
166
|
mutationEndpoint?: string
|
|
167
167
|
enableUrlSync?: boolean
|
|
168
|
+
/**
|
|
169
|
+
* Hide the import action on THIS view even when the model supports it.
|
|
170
|
+
* A role-scoped view (e.g. a rep seeing only their own records) usually
|
|
171
|
+
* wants the table without a bulk-import entry point, while the same model
|
|
172
|
+
* keeps it on the admin view. Absent → the model's metadata decides.
|
|
173
|
+
*/
|
|
174
|
+
hideImport?: boolean
|
|
175
|
+
/** Hide the export action on this view. See `hideImport`. */
|
|
176
|
+
hideExport?: boolean
|
|
168
177
|
hiddenColumns?: string[]
|
|
169
178
|
onAction?: (action: string, row: any) => void
|
|
170
179
|
/**
|
|
@@ -225,6 +234,8 @@ export function DynamicTable({
|
|
|
225
234
|
endpoint,
|
|
226
235
|
mutationEndpoint,
|
|
227
236
|
enableUrlSync = true,
|
|
237
|
+
hideImport,
|
|
238
|
+
hideExport,
|
|
228
239
|
hiddenColumns = [],
|
|
229
240
|
onAction,
|
|
230
241
|
onRowClick,
|
|
@@ -644,6 +655,15 @@ export function DynamicTable({
|
|
|
644
655
|
return gateTableMetadata(metadata, model, can, (key, fallback) => t(key, { defaultValue: fallback }))
|
|
645
656
|
}, [metadata, permissionsActive, can, model, t])
|
|
646
657
|
|
|
658
|
+
// Importing is offered when the host opts in explicitly (`canImport`) or,
|
|
659
|
+
// when it says nothing, whenever the kernel served an import spec for the
|
|
660
|
+
// model — which it does for every model with at least one importable form
|
|
661
|
+
// field. That makes the flow work out of the box instead of requiring each
|
|
662
|
+
// host to flip a flag per view.
|
|
663
|
+
const importEnabled =
|
|
664
|
+
!hideImport && (viewMetadata?.canImport ?? Boolean(viewMetadata?.import?.columns?.length))
|
|
665
|
+
const exportEnabled = !hideExport && Boolean(viewMetadata?.canExport)
|
|
666
|
+
|
|
647
667
|
const buildFilterParams = useCallback(() => {
|
|
648
668
|
const params: Record<string, any> = {}
|
|
649
669
|
if (sorting.length > 0) {
|
|
@@ -1202,12 +1222,12 @@ export function DynamicTable({
|
|
|
1202
1222
|
onBulkDelete={() => setShowBulkDeleteConfirm(true)}
|
|
1203
1223
|
extraActions={
|
|
1204
1224
|
<>
|
|
1205
|
-
{
|
|
1225
|
+
{exportEnabled && (
|
|
1206
1226
|
<Button variant="outline" size="sm" className="h-8" onClick={() => setExportOpen(true)}>
|
|
1207
1227
|
<Download className="h-4 w-4 mr-1" /> Exportar
|
|
1208
1228
|
</Button>
|
|
1209
1229
|
)}
|
|
1210
|
-
{
|
|
1230
|
+
{importEnabled && (
|
|
1211
1231
|
<Button variant="outline" size="sm" className="h-8" onClick={() => setImportOpen(true)}>
|
|
1212
1232
|
<Upload className="h-4 w-4 mr-1" /> Importar
|
|
1213
1233
|
</Button>
|
|
@@ -1478,10 +1498,10 @@ export function DynamicTable({
|
|
|
1478
1498
|
</AlertDialogContent>
|
|
1479
1499
|
</AlertDialog>
|
|
1480
1500
|
|
|
1481
|
-
{
|
|
1501
|
+
{exportEnabled && (
|
|
1482
1502
|
<ExportDialog open={exportOpen} onOpenChange={setExportOpen} model={model} metadata={metadata} currentFilters={buildFilterParams()} hasActiveFilters={hasActiveFilters} />
|
|
1483
1503
|
)}
|
|
1484
|
-
{
|
|
1504
|
+
{importEnabled && (
|
|
1485
1505
|
<ImportDialog open={importOpen} onOpenChange={setImportOpen} model={model} metadata={metadata} onImported={handleRefresh} />
|
|
1486
1506
|
)}
|
|
1487
1507
|
<DataTableBulkActions table={table} entityName="registro">
|
package/src/form-layout.ts
CHANGED
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
// implementation and the modal-render machinery never has to be booted under
|
|
15
15
|
// happy-dom just to assert the grouping (same approach as PR #673).
|
|
16
16
|
|
|
17
|
+
import type { VisibleWhen } from './types'
|
|
18
|
+
import { evaluateVisibleWhen, getVisibleWhen } from './dynamic-form-schema'
|
|
19
|
+
|
|
17
20
|
/** One declared section of a model form. `title`/`description` arrive already
|
|
18
21
|
* localized from the kernel and are rendered verbatim. */
|
|
19
22
|
export interface FormSection {
|
|
@@ -22,6 +25,12 @@ export interface FormSection {
|
|
|
22
25
|
description?: string
|
|
23
26
|
/** Sections mode only: render this section collapsed on first paint. */
|
|
24
27
|
collapsed?: boolean
|
|
28
|
+
/** Section-level visibility gate (kernel v0.84.0). When present and its
|
|
29
|
+
* predicate evaluates false against the live form values, the whole section
|
|
30
|
+
* is omitted (and in steps mode its wizard step drops from the sequence).
|
|
31
|
+
* Tolerates the camelCase alias, same as fields. */
|
|
32
|
+
visible_when?: VisibleWhen
|
|
33
|
+
visibleWhen?: VisibleWhen
|
|
25
34
|
}
|
|
26
35
|
|
|
27
36
|
/** Model-level layout directive. `mode` defaults to `"sections"`. */
|
|
@@ -71,16 +80,35 @@ export function getFieldSection(
|
|
|
71
80
|
* section whose only members are hidden by `visible_when` never renders an
|
|
72
81
|
* empty shell (and never yields an empty wizard step). Because the caller
|
|
73
82
|
* passes the already visibility-filtered list, this falls out for free.
|
|
83
|
+
* - A section declaring its OWN `visible_when` is dropped whole (before its
|
|
84
|
+
* fields are even collected) whenever the predicate evaluates false against
|
|
85
|
+
* `values`, reusing the SAME evaluator the fields use. In steps mode the
|
|
86
|
+
* caller derives the wizard sequence from these groups, so a hidden section
|
|
87
|
+
* simply never becomes a step. Section-less / unknown-section fields are
|
|
88
|
+
* never gated by any section predicate. Without a section `visible_when`
|
|
89
|
+
* (or without `values`), behaviour is byte-for-byte the legacy path.
|
|
74
90
|
*/
|
|
75
91
|
export function groupFieldsBySection<F extends { section?: string }>(
|
|
76
92
|
fields: F[],
|
|
77
93
|
layout: FormLayout | undefined,
|
|
94
|
+
values?: Record<string, any> | null,
|
|
78
95
|
): FieldGroup<F>[] {
|
|
79
96
|
if (!layout?.sections?.length) {
|
|
80
97
|
return [{ key: DEFAULT_SECTION_KEY, isDefault: true, fields }]
|
|
81
98
|
}
|
|
82
99
|
|
|
83
|
-
|
|
100
|
+
// Sections whose own `visible_when` predicate evaluates false are hidden
|
|
101
|
+
// WHOLESALE: the section never emits a group/step AND its member fields are
|
|
102
|
+
// dropped (they must not leak into the default group). Reuses the same
|
|
103
|
+
// field-level evaluator — no duplicated logic. A `declared` set keeps a
|
|
104
|
+
// field targeting a hidden section from being treated as "unknown section"
|
|
105
|
+
// and orphaned.
|
|
106
|
+
const declared = new Set(layout.sections.map((s) => s.key))
|
|
107
|
+
const visibleSections = layout.sections.filter((s) =>
|
|
108
|
+
evaluateVisibleWhen(getVisibleWhen(s), values),
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
const known = new Set(visibleSections.map((s) => s.key))
|
|
84
112
|
const bySection = new Map<string, F[]>()
|
|
85
113
|
const orphans: F[] = []
|
|
86
114
|
|
|
@@ -90,6 +118,9 @@ export function groupFieldsBySection<F extends { section?: string }>(
|
|
|
90
118
|
const arr = bySection.get(sec)
|
|
91
119
|
if (arr) arr.push(f)
|
|
92
120
|
else bySection.set(sec, [f])
|
|
121
|
+
} else if (sec && declared.has(sec)) {
|
|
122
|
+
// Belongs to a declared-but-hidden section → drop the field.
|
|
123
|
+
continue
|
|
93
124
|
} else {
|
|
94
125
|
orphans.push(f)
|
|
95
126
|
}
|
|
@@ -100,7 +131,7 @@ export function groupFieldsBySection<F extends { section?: string }>(
|
|
|
100
131
|
if (orphans.length) {
|
|
101
132
|
groups.push({ key: DEFAULT_SECTION_KEY, isDefault: true, fields: orphans })
|
|
102
133
|
}
|
|
103
|
-
for (const s of
|
|
134
|
+
for (const s of visibleSections) {
|
|
104
135
|
const secFields = bySection.get(s.key)
|
|
105
136
|
if (!secFields || secFields.length === 0) continue // hide empty section
|
|
106
137
|
groups.push({
|
package/src/types.ts
CHANGED
|
@@ -1,6 +1,27 @@
|
|
|
1
1
|
// Shared metadata shape consumed by every host. Some hosts add a `link`
|
|
2
2
|
// action type with a `linkUrl` template — represented here as part of the
|
|
3
3
|
// `type` union so the SDK can render it uniformly.
|
|
4
|
+
/** One spreadsheet column of a model's import template. Mirrors the kernel's
|
|
5
|
+
* `modelbase.ImportColumn`. */
|
|
6
|
+
export interface ImportColumnMeta {
|
|
7
|
+
key: string
|
|
8
|
+
header: string
|
|
9
|
+
aliases?: string[]
|
|
10
|
+
required?: boolean
|
|
11
|
+
type?: string
|
|
12
|
+
example?: string
|
|
13
|
+
hint?: string
|
|
14
|
+
generator?: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** A model's spreadsheet-import declaration. Mirrors `modelbase.ImportSpec`. */
|
|
18
|
+
export interface ImportSpecMeta {
|
|
19
|
+
columns: ImportColumnMeta[]
|
|
20
|
+
maxRows?: number
|
|
21
|
+
sheetName?: string
|
|
22
|
+
instructions?: string[]
|
|
23
|
+
}
|
|
24
|
+
|
|
4
25
|
export interface TableMetadata {
|
|
5
26
|
title: string
|
|
6
27
|
endpoint: string
|
|
@@ -15,6 +36,16 @@ export interface TableMetadata {
|
|
|
15
36
|
canExport?: boolean
|
|
16
37
|
canImport?: boolean
|
|
17
38
|
canCreate?: boolean
|
|
39
|
+
/**
|
|
40
|
+
* The model's spreadsheet-import declaration, served by the kernel: the
|
|
41
|
+
* columns of the generated template and the headers accepted when parsing
|
|
42
|
+
* a filled file back in. The kernel derives it from the model's form
|
|
43
|
+
* fields when the model declares nothing, and omits the field entirely
|
|
44
|
+
* when the model has no importable column — so its presence is the signal
|
|
45
|
+
* that importing is meaningful for this model. Purely additive; older
|
|
46
|
+
* kernels omit it.
|
|
47
|
+
*/
|
|
48
|
+
import?: ImportSpecMeta
|
|
18
49
|
/**
|
|
19
50
|
* Child relations of this model, served by the kernel (>= v0.41.0). A
|
|
20
51
|
* generic detail page renders one `DynamicRelation` panel per entry via
|