@asteby/metacore-runtime-react 28.6.0 → 28.8.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 CHANGED
@@ -1,5 +1,37 @@
1
1
  # @asteby/metacore-runtime-react
2
2
 
3
+ ## 28.8.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 3db2705: Nuevo primitivo `EntitySelect`: combobox async de un modelo relacionado con las dos affordances del modal dinámico — "+" para CREAR (sin selección) que auto-selecciona el creado, y lápiz para EDITAR el seleccionado. Ambas gateadas por permisos del kernel (useCan: `<model>.create`/`.update`) y 100% dinámicas (el form sale de `/metadata/modal/:model` vía CreateRecordDialog). Reemplaza los pickers bespoke duplicados en POS/purchases por una sola implementación reusable.
8
+ - 8346d8d: PrintDocumentButton: botón reusable de impresión de documentos
9
+
10
+ Componente drop-in sobre usePrintDocument: renderiza un <button> (estilable con
11
+ className) que imprime/descarga/abre un documento del servidor, se deshabilita
12
+ mientras baja el PDF (aria-busy) y reporta errores por onError. Evita recablear
13
+ el hook + estado de carga en cada addon.
14
+
15
+ - 05bc470: usePrintDocument: primitivo reusable para imprimir documentos del servidor
16
+
17
+ Nuevo hook que cualquier addon federado usa para imprimir/descargar/abrir un
18
+ documento renderizado por el motor de docs de ops (ticket, comprobante, orden):
19
+ GET /api/data/:model/:id/documents/:key.pdf. Fetch con auth vía el ApiClient
20
+ inyectado (el endpoint es Bearer-gated, no se puede window.open directo), blob →
21
+ iframe print (modo default, ideal para ticket térmico), download u open. Era el
22
+ primitivo de impresión que faltaba en el SDK (había 0). Habilita el auto-print del
23
+ POS al cerrar venta.
24
+
25
+ ### Patch Changes
26
+
27
+ - 233d63b: DynamicTable: una celda `creator` sin actor resuelto (registro creado por el sistema, `created_by` null) muestra "Sistema" en vez de "N/A", consistente con el fallback del historial de actividad. `user`/`avatar`/`search` mantienen "N/A" (vacío = sin asignar).
28
+
29
+ ## 28.7.0
30
+
31
+ ### Minor Changes
32
+
33
+ - 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.
34
+
3
35
  ## 28.6.0
4
36
 
5
37
  ### Minor Changes
@@ -1 +1 @@
1
- {"version":3,"file":"import.d.ts","sourceRoot":"","sources":["../../src/dialogs/import.tsx"],"names":[],"mappings":"AAuBA,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;AAoBD,wBAAgB,YAAY,CAAC,EACzB,IAAI,EACJ,YAAY,EACZ,KAAK,EACL,QAAQ,EACR,UAAU,GACb,EAAE,iBAAiB,+BAwVnB"}
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"}
@@ -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 → import
3
- // with per-row error report). Axios-like client is provided by <ApiProvider>.
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.csv`;
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
- errors: data.errors ?? [],
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 CSV"] }), _jsx("p", { className: "text-xs text-muted-foreground mt-1", children: "Descarga la plantilla para asegurar el formato correcto." })] }), _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: ".csv,.json", onChange: handleFileChange, className: "cursor-pointer" }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Formatos aceptados: 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.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: error.field }), _jsx(TableCell, { className: "text-sm text-destructive", children: error.message })] }, 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: error.field }), _jsx(TableCell, { className: "text-sm text-destructive", children: error.message })] }, 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 ||
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
  }
@@ -1 +1 @@
1
- {"version":3,"file":"dynamic-columns.d.ts","sourceRoot":"","sources":["../src/dynamic-columns.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EAAU,KAAK,MAAM,EAAE,MAAM,UAAU,CAAA;AAwC9C,OAAO,KAAK,EAAiB,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAE9D,OAAO,KAAK,EAER,iBAAiB,EACpB,MAAM,wBAAwB,CAAA;AAE/B,qEAAqE;AACrE,MAAM,WAAW,qBAAqB;IAClC;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;IACtC;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACtB;AA0BD;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAAI,KAAK,gBAAgB,EAAE,cAAc,MAAM,KAAG,MACzB,CAAA;AAQrD;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,KAAK,gBAAgB,KAAG,MAAM,GAAG,SAG5D,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,GAC7B,KAAK,gBAAgB,EACrB,OAAO,OAAO,EACd,WAAW,MAAM,EACjB,SAAS,MAAM,KAChB,MAyBF,CAAA;AAyCD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,0BAA0B,GAAI,QAAQ,GAAG,EAAE,KAAK,GAAG,KAAG,OAMlE,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAAI,QAAQ,GAAG,EAAE,KAAK,GAAG,KAAG,OAiB5D,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAAI,QAAQ,GAAG,EAAE,KAAK,GAAG,KAAG,OACqB,CAAA;AAwFhF;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,GAAI,KAAK,IAAI,CAAC,gBAAgB,EAAE,KAAK,CAAC,KAAG,MAGnE,CAAA;AAED,6EAA6E;AAC7E,eAAO,MAAM,eAAe,2DAA4D,CAAA;AAExF;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,cAAc,CAC1B,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,MAAM,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE,MAAM,GAClB;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CA6C5C;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,GAAI,KAAK,gBAAgB,EAAE,KAAK,GAAG,KAAG,MAWtE,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,GAAI,KAAK,gBAAgB,EAAE,KAAK,GAAG,KAAG,MAOtE,CAAA;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,gBAAgB,GACzB,KAAK,gBAAgB,EACrB,KAAK,GAAG,EACR,OAAO,GAAG,EACV,mBAAe,KAChB,MAAM,GAAG,SAcX,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,GAAI,KAAK,gBAAgB,EAAE,KAAK,GAAG,KAAG,MAOzE,CAAA;AA+ID;;;;GAIG;AACH;;;;;GAKG;AACH,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC;IAC7B,KAAK,EAAE,OAAO,CAAA;IACd,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;CACxC,CAqBA,CAAA;AAED,wBAAgB,4BAA4B,CACxC,OAAO,GAAE,qBAA0B,GACpC,iBAAiB,CAkmBnB;AAED;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,EAAE,iBACL,CAAA"}
1
+ {"version":3,"file":"dynamic-columns.d.ts","sourceRoot":"","sources":["../src/dynamic-columns.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EAAU,KAAK,MAAM,EAAE,MAAM,UAAU,CAAA;AAwC9C,OAAO,KAAK,EAAiB,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAE9D,OAAO,KAAK,EAER,iBAAiB,EACpB,MAAM,wBAAwB,CAAA;AAE/B,qEAAqE;AACrE,MAAM,WAAW,qBAAqB;IAClC;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;IACtC;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACtB;AA0BD;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAAI,KAAK,gBAAgB,EAAE,cAAc,MAAM,KAAG,MACzB,CAAA;AAQrD;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,KAAK,gBAAgB,KAAG,MAAM,GAAG,SAG5D,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,GAC7B,KAAK,gBAAgB,EACrB,OAAO,OAAO,EACd,WAAW,MAAM,EACjB,SAAS,MAAM,KAChB,MAyBF,CAAA;AAyCD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,0BAA0B,GAAI,QAAQ,GAAG,EAAE,KAAK,GAAG,KAAG,OAMlE,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAAI,QAAQ,GAAG,EAAE,KAAK,GAAG,KAAG,OAiB5D,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAAI,QAAQ,GAAG,EAAE,KAAK,GAAG,KAAG,OACqB,CAAA;AAwFhF;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,GAAI,KAAK,IAAI,CAAC,gBAAgB,EAAE,KAAK,CAAC,KAAG,MAGnE,CAAA;AAED,6EAA6E;AAC7E,eAAO,MAAM,eAAe,2DAA4D,CAAA;AAExF;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,cAAc,CAC1B,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,MAAM,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE,MAAM,GAClB;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CA6C5C;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,GAAI,KAAK,gBAAgB,EAAE,KAAK,GAAG,KAAG,MAWtE,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,GAAI,KAAK,gBAAgB,EAAE,KAAK,GAAG,KAAG,MAOtE,CAAA;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,gBAAgB,GACzB,KAAK,gBAAgB,EACrB,KAAK,GAAG,EACR,OAAO,GAAG,EACV,mBAAe,KAChB,MAAM,GAAG,SAcX,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,GAAI,KAAK,gBAAgB,EAAE,KAAK,GAAG,KAAG,MAOzE,CAAA;AA+ID;;;;GAIG;AACH;;;;;GAKG;AACH,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC;IAC7B,KAAK,EAAE,OAAO,CAAA;IACd,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;CACxC,CAqBA,CAAA;AAED,wBAAgB,4BAA4B,CACxC,OAAO,GAAE,qBAA0B,GACpC,iBAAiB,CA+mBnB;AAED;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,EAAE,iBACL,CAAA"}
@@ -622,7 +622,19 @@ export function makeDefaultGetDynamicColumns(helpers = {}) {
622
622
  col.tooltip ||
623
623
  col.displayField ||
624
624
  col.key;
625
- const name = getNestedValue(row.original, namePath) || 'N/A';
625
+ // A `creator` cell with no resolved actor means the
626
+ // record was created by the SYSTEM (e.g. an owner /
627
+ // seed row whose `created_by` is null), not a missing
628
+ // value — show "Sistema" rather than "N/A". Matches
629
+ // the record-history actor fallback. `user`/`avatar`/
630
+ // `search` keep "N/A" (empty there means unassigned).
631
+ const resolvedName = getNestedValue(row.original, namePath);
632
+ const name = resolvedName ||
633
+ (renderAs === 'creator'
634
+ ? t
635
+ ? t('common.system', { defaultValue: 'Sistema' })
636
+ : 'Sistema'
637
+ : 'N/A');
626
638
  const desc = getNestedValue(row.original, col.description || '');
627
639
  const avatarSrc = resolveAvatarSrc(col, row.original, value, apiBaseUrl);
628
640
  return (_jsx(AvatarCell, { name: String(name), desc: desc ? String(desc) : undefined, avatarSrc: avatarSrc, getImageUrl: getImageUrl }));
@@ -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,+BAuuCnB"}
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"}
@@ -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: [viewMetadata?.canExport && (_jsxs(Button, { variant: "outline", size: "sm", className: "h-8", onClick: () => setExportOpen(true), children: [_jsx(Download, { className: "h-4 w-4 mr-1" }), " Exportar"] })), viewMetadata?.canImport && (_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) => {
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" })] }))] }) }), viewMetadata?.canExport && (_jsx(ExportDialog, { open: exportOpen, onOpenChange: setExportOpen, model: model, metadata: metadata, currentFilters: buildFilterParams(), hasActiveFilters: hasActiveFilters })), viewMetadata?.canImport && (_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"] }) })] }));
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
  }
@@ -0,0 +1,43 @@
1
+ import { type LucideIcon } from 'lucide-react';
2
+ /** One searchable option: `value` is the id, `label` the display text. */
3
+ export interface EntitySelectOption {
4
+ value: string;
5
+ label: string;
6
+ description?: string;
7
+ }
8
+ export interface EntitySelectProps {
9
+ /** Kernel model key (e.g. "Supplier", "Warehouse", "Category"). */
10
+ model: string;
11
+ /** Currently selected id (or null). */
12
+ value: string | null;
13
+ /** Label of the selected record (rendered without a re-fetch). */
14
+ label: string | null;
15
+ /** Called with (id, label) on select/create/clear. */
16
+ onSelect: (id: string | null, label: string | null) => void;
17
+ /** Async search over the model. Callers pass their `/api/options/<model>` fetcher. */
18
+ fetcher: (q: string, signal: AbortSignal) => Promise<EntitySelectOption[]>;
19
+ icon?: LucideIcon;
20
+ placeholder?: string;
21
+ searchPlaceholder?: string;
22
+ emptyText?: string;
23
+ /** Preload first results on open and drop the 2-char gate. */
24
+ preload?: boolean;
25
+ /**
26
+ * Permission overrides. By default create/edit are gated by the kernel
27
+ * permissions `<model>.create` / `<model>.update` (useCan). Pass explicit
28
+ * booleans to force them (e.g. a read-only surface).
29
+ */
30
+ canCreate?: boolean;
31
+ canEdit?: boolean;
32
+ /**
33
+ * CRUD endpoint base for the create/edit dialog. Defaults to the standard
34
+ * org-scoped `/data/<model>/me`, with edit at `/data/<model>/me/<id>`.
35
+ */
36
+ endpoint?: string;
37
+ /** Record field used as the label after create/edit (default "name"). */
38
+ labelField?: string;
39
+ /** Disable the whole control. */
40
+ disabled?: boolean;
41
+ }
42
+ export declare function EntitySelect({ model, value, label, onSelect, fetcher, icon: Icon, placeholder, searchPlaceholder, emptyText, preload, canCreate, canEdit, endpoint, labelField, disabled, }: EntitySelectProps): import("react").JSX.Element;
43
+ //# sourceMappingURL=entity-select.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entity-select.d.ts","sourceRoot":"","sources":["../src/entity-select.tsx"],"names":[],"mappings":"AAiBA,OAAO,EAA2B,KAAK,UAAU,EAAE,MAAM,cAAc,CAAA;AAiBvE,0EAA0E;AAC1E,MAAM,WAAW,kBAAkB;IAC/B,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,iBAAiB;IAC9B,mEAAmE;IACnE,KAAK,EAAE,MAAM,CAAA;IACb,uCAAuC;IACvC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,kEAAkE;IAClE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,sDAAsD;IACtD,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;IAC3D,sFAAsF;IACtF,OAAO,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAAA;IAE1E,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,8DAA8D;IAC9D,OAAO,CAAC,EAAE,OAAO,CAAA;IAEjB;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,OAAO,CAAC,EAAE,OAAO,CAAA;IAEjB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,iCAAiC;IACjC,QAAQ,CAAC,EAAE,OAAO,CAAA;CACrB;AAOD,wBAAgB,YAAY,CAAC,EACzB,KAAK,EACL,KAAK,EACL,KAAK,EACL,QAAQ,EACR,OAAO,EACP,IAAI,EAAE,IAAI,EACV,WAA4B,EAC5B,iBAA6B,EAC7B,SAA4B,EAC5B,OAAe,EACf,SAAS,EACT,OAAO,EACP,QAAQ,EACR,UAAmB,EACnB,QAAgB,GACnB,EAAE,iBAAiB,+BAuNnB"}
@@ -0,0 +1,124 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // EntitySelect — the shared, permission-aware single-select for a related model.
3
+ //
4
+ // A searchable async combobox over a kernel model's records, plus the two
5
+ // affordances every "pick a related record" control should have, exactly like
6
+ // the dynamic create modal's relation fields (Categoría/Marca) do:
7
+ //
8
+ // - nothing selected → a "+" that opens the model's CREATE dialog, and
9
+ // auto-selects the record it creates;
10
+ // - a record selected → a pencil that opens that record's EDIT dialog.
11
+ //
12
+ // Both affordances are gated by the kernel permissions (useCan): the "+" only
13
+ // shows when the user can create the model, the pencil only when they can edit
14
+ // it. Everything is DYNAMIC — the create/edit form comes from the model's
15
+ // `/metadata/modal/:model` schema via <CreateRecordDialog>, so no per-model form
16
+ // code is needed. This lives in the SDK so POS, purchases and any future addon
17
+ // share ONE implementation instead of each re-porting a bespoke picker.
18
+ import { useCallback, useEffect, useState } from 'react';
19
+ import { Search, X, Plus, Pencil } from 'lucide-react';
20
+ import { Button, Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, Popover, PopoverContent, PopoverTrigger, } from '@asteby/metacore-ui';
21
+ import { CreateRecordDialog } from './dialogs/create-record-dialog';
22
+ import { useCan } from './permissions-context';
23
+ import { useApi } from './api-context';
24
+ /** Lowercase model → permission capability namespace (Supplier → supplier). */
25
+ function capabilityNamespace(model) {
26
+ return model.replace(/([a-z0-9])([A-Z])/g, '$1_$2').toLowerCase();
27
+ }
28
+ export function EntitySelect({ model, value, label, onSelect, fetcher, icon: Icon, placeholder = 'Seleccionar…', searchPlaceholder = 'Buscar…', emptyText = 'Sin resultados', preload = false, canCreate, canEdit, endpoint, labelField = 'name', disabled = false, }) {
29
+ const can = useCan();
30
+ const api = useApi();
31
+ const ns = capabilityNamespace(model);
32
+ const mayCreate = canCreate ?? can(`${ns}.create`);
33
+ const mayEdit = canEdit ?? can(`${ns}.update`);
34
+ const base = endpoint ?? `/data/${model}/me`;
35
+ const [open, setOpen] = useState(false);
36
+ const [dialogOpen, setDialogOpen] = useState(false);
37
+ const [dialogRecordId, setDialogRecordId] = useState(undefined);
38
+ const [searchTerm, setSearchTerm] = useState('');
39
+ const [results, setResults] = useState([]);
40
+ const [isLoading, setIsLoading] = useState(false);
41
+ const minChars = preload ? 0 : 2;
42
+ const run = useCallback(async (q, signal) => {
43
+ if (q.length < minChars) {
44
+ setResults([]);
45
+ return;
46
+ }
47
+ setIsLoading(true);
48
+ try {
49
+ const rows = await fetcher(q, signal);
50
+ if (!signal.aborted)
51
+ setResults(rows);
52
+ }
53
+ catch {
54
+ if (!signal.aborted)
55
+ setResults([]);
56
+ }
57
+ finally {
58
+ if (!signal.aborted)
59
+ setIsLoading(false);
60
+ }
61
+ }, [fetcher, minChars]);
62
+ useEffect(() => {
63
+ if (!open)
64
+ return;
65
+ const controller = new AbortController();
66
+ const timeout = setTimeout(() => run(searchTerm, controller.signal), 250);
67
+ return () => {
68
+ clearTimeout(timeout);
69
+ controller.abort();
70
+ };
71
+ }, [searchTerm, run, open]);
72
+ const pick = (row) => {
73
+ onSelect(row.value, row.label);
74
+ setOpen(false);
75
+ setSearchTerm('');
76
+ };
77
+ const clear = (e) => {
78
+ e.stopPropagation();
79
+ onSelect(null, null);
80
+ };
81
+ const openCreate = (e) => {
82
+ e.stopPropagation();
83
+ setDialogRecordId(undefined);
84
+ setDialogOpen(true);
85
+ };
86
+ const openEdit = (e) => {
87
+ e.stopPropagation();
88
+ if (!value)
89
+ return;
90
+ setDialogRecordId(value);
91
+ setDialogOpen(true);
92
+ };
93
+ // Read the {id,label} off a saved record and select it. The transport
94
+ // matches the standard org-scoped CRUD the dynamic modal uses, so create/edit
95
+ // stay consistent with the rest of the app.
96
+ const selectSaved = (rec) => {
97
+ if (!rec)
98
+ return;
99
+ const id = rec.id != null ? String(rec.id) : value ?? '';
100
+ const lbl = (rec[labelField] != null && String(rec[labelField])) ||
101
+ (rec.name != null && String(rec.name)) ||
102
+ label ||
103
+ id;
104
+ onSelect(id, lbl);
105
+ };
106
+ return (_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsxs(Popover, { open: open, onOpenChange: disabled ? undefined : setOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs(Button, { variant: "outline", disabled: disabled, className: "w-full flex-1 justify-start gap-2 font-normal", children: [Icon && _jsx(Icon, { className: "text-muted-foreground size-4 shrink-0" }), _jsx("span", { className: "flex-1 truncate text-left", children: label ?? placeholder }), value && (_jsx("span", { role: "button", tabIndex: 0, onClick: clear, onKeyDown: (e) => {
107
+ if (e.key === 'Enter' || e.key === ' ')
108
+ clear(e);
109
+ }, className: "hover:bg-accent ml-auto shrink-0 rounded-sm p-0.5", children: _jsx(X, { className: "size-3.5" }) }))] }) }), _jsx(PopoverContent, { className: "p-0", align: "start", style: { width: 'var(--radix-popover-trigger-width)' }, children: _jsxs(Command, { shouldFilter: false, children: [_jsx(CommandInput, { placeholder: searchPlaceholder, value: searchTerm, onValueChange: setSearchTerm }), _jsxs(CommandList, { children: [isLoading && (_jsx("div", { className: "text-muted-foreground py-4 text-center text-sm", children: "Buscando\u2026" })), !isLoading &&
110
+ searchTerm.length >= minChars &&
111
+ results.length === 0 && _jsx(CommandEmpty, { children: emptyText }), !isLoading && results.length > 0 && (_jsx(CommandGroup, { className: "max-h-64 overflow-auto", children: results.map((row) => (_jsxs(CommandItem, { value: row.value, onSelect: () => pick(row), className: "flex flex-col items-start gap-0.5", children: [_jsx("span", { className: "text-sm font-medium", children: row.label }), row.description && (_jsx("span", { className: "text-muted-foreground text-xs", children: row.description }))] }, row.value))) })), !isLoading && !preload && searchTerm.length < minChars && (_jsxs("div", { className: "text-muted-foreground flex flex-col items-center gap-1 py-6", children: [_jsx(Search, { className: "size-5" }), _jsx("span", { className: "text-xs", children: "Escribe al menos 2 caracteres" })] }))] })] }) })] }), value
112
+ ? mayEdit && (_jsx(Button, { type: "button", variant: "outline", size: "icon", disabled: disabled, onClick: openEdit, "aria-label": "Editar", title: "Editar", className: "shrink-0", children: _jsx(Pencil, { className: "size-4" }) }))
113
+ : mayCreate && (_jsx(Button, { type: "button", variant: "outline", size: "icon", disabled: disabled, onClick: openCreate, "aria-label": "Crear", title: "Crear", className: "shrink-0", children: _jsx(Plus, { className: "size-4" }) })), dialogOpen && (_jsx(CreateRecordDialog, { modelKey: model, open: dialogOpen, onOpenChange: setDialogOpen, recordId: dialogRecordId, endpoint: base, onCreate: async (data) => {
114
+ const res = await api.post(base, data);
115
+ const rec = (res.data?.data ?? res.data);
116
+ selectSaved(rec);
117
+ return rec.id != null ? { id: String(rec.id) } : undefined;
118
+ }, onUpdate: async (id, data) => {
119
+ const res = await api.put(`${base}/${id}`, data);
120
+ const rec = (res.data?.data ?? res.data);
121
+ selectSaved({ id, ...rec });
122
+ return { id: String(id) };
123
+ } }))] }));
124
+ }
package/dist/index.d.ts CHANGED
@@ -29,6 +29,8 @@ export * from './navigation-builder';
29
29
  export * from './i18n-provider';
30
30
  export * from './api-context';
31
31
  export * from './use-addon-settings';
32
+ export * from './use-print-document';
33
+ export * from './print-document-button';
32
34
  export * from './metadata-cache';
33
35
  export { ADDON_MANIFEST_CHANGED_TYPE, wireHotSwapInvalidation, useManifestHotSwapSubscriber, type AddonManifestChangedMessage, type ManifestHotSwapClient, type WireHotSwapInvalidationOptions, } from './manifest-hotswap-subscriber';
34
36
  export { useHotSwapReload, applyHotSwapReload, withVersionParam, clearFederationContainer, shortenHash, type HotSwapReloadStrategy, type HotSwapReloadConfig, type HotSwapReloadAction, type HotSwapReloadDeps, type UseHotSwapReloadResult, } from './hotswap-reload-policy';
@@ -43,6 +45,8 @@ export { DynamicRecordDialog, ViewValue } from './dialogs/dynamic-record';
43
45
  export { normalizeRefFieldsForSubmit } from './dialogs/normalize-submit';
44
46
  export type { DynamicRecordDialogProps, FieldDef, FieldOption, GetImageUrl } from './dialogs/dynamic-record';
45
47
  export { CreateRecordDialog } from './dialogs/create-record-dialog';
48
+ export { EntitySelect } from './entity-select';
49
+ export type { EntitySelectProps, EntitySelectOption } from './entity-select';
46
50
  export { ViewRecordDialog } from './dialogs/view-record-dialog';
47
51
  export type { ModelKey, ModelSchema, CreateResult, RecordDialogProps, CreateRecordDialogProps, ViewRecordDialogProps, } from './dialogs/types';
48
52
  export { ExportDialog } from './dialogs/export';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,cAAc,SAAS,CAAA;AACvB,OAAO,EACH,WAAW,EACX,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,cAAc,EACd,KAAK,gBAAgB,EACrB,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,EAC5B,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,eAAe,GACvB,MAAM,WAAW,CAAA;AAClB,cAAc,mBAAmB,CAAA;AACjC,OAAO,EACH,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EAClB,KAAK,cAAc,EACnB,KAAK,SAAS,GACjB,MAAM,gBAAgB,CAAA;AACvB,cAAc,iBAAiB,CAAA;AAC/B,OAAO,EACH,aAAa,EACb,KAAK,kBAAkB,EACvB,YAAY,EACZ,YAAY,EACZ,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,EACjB,eAAe,GAClB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACH,mBAAmB,EACnB,sBAAsB,EACtB,WAAW,EACX,sBAAsB,EACtB,uBAAuB,EACvB,qBAAqB,EACrB,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,KAAK,yBAAyB,EAC9B,KAAK,kBAAkB,EACvB,KAAK,yBAAyB,EAC9B,KAAK,2BAA2B,GACnC,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EACH,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,uBAAuB,EACvB,uBAAuB,EACvB,uBAAuB,EACvB,eAAe,EACf,sBAAsB,EACtB,cAAc,EACd,mBAAmB,EACnB,iBAAiB,EACjB,uBAAuB,EACvB,qBAAqB,EACrB,iBAAiB,EACjB,mBAAmB,EACnB,SAAS,EACT,mBAAmB,EACnB,uBAAuB,EACvB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,0BAA0B,EAC/B,KAAK,sBAAsB,GAC9B,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACH,cAAc,EACd,KAAK,WAAW,EAChB,KAAK,oBAAoB,GAC5B,MAAM,gBAAgB,CAAA;AACvB,OAAO,EACH,iBAAiB,EACjB,KAAK,kBAAkB,EACvB,KAAK,uBAAuB,GAC/B,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EACH,WAAW,EACX,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,EACjB,KAAK,gBAAgB,GACxB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EACH,iBAAiB,EACjB,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,GAC/B,MAAM,uBAAuB,CAAA;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,eAAe,CAAA;AAC7B,cAAc,kBAAkB,CAAA;AAChC,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAC/D,OAAO,EACH,qBAAqB,EACrB,KAAK,gBAAgB,GACxB,MAAM,2BAA2B,CAAA;AAClC,OAAO,EACH,kBAAkB,EAClB,eAAe,EACf,KAAK,uBAAuB,EAC5B,KAAK,eAAe,GACvB,MAAM,wBAAwB,CAAA;AAC/B,cAAc,gBAAgB,CAAA;AAC9B,OAAO,EACH,mBAAmB,EACnB,cAAc,EACd,qBAAqB,EACrB,qBAAqB,EACrB,KAAK,WAAW,EAChB,KAAK,wBAAwB,GAChC,MAAM,wBAAwB,CAAA;AAC/B,cAAc,QAAQ,CAAA;AACtB,cAAc,mBAAmB,CAAA;AACjC,OAAO,EACH,mBAAmB,EACnB,MAAM,EACN,oBAAoB,EACpB,OAAO,EACP,sBAAsB,EACtB,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,KAAK,KAAK,EACV,KAAK,wBAAwB,GAChC,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EACH,oBAAoB,EACpB,KAAK,0BAA0B,EAC/B,KAAK,iBAAiB,GACzB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EACH,kBAAkB,EAClB,sBAAsB,EACtB,kBAAkB,EAClB,qBAAqB,EACrB,mBAAmB,EACnB,iBAAiB,EACjB,sBAAsB,EACtB,aAAa,EACb,kBAAkB,EAClB,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,EACvB,KAAK,yBAAyB,EAC9B,KAAK,sBAAsB,EAC3B,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,OAAO,EACZ,KAAK,SAAS,GACjB,MAAM,uBAAuB,CAAA;AAC9B,cAAc,uBAAuB,CAAA;AACrC,cAAc,wBAAwB,CAAA;AACtC,cAAc,sBAAsB,CAAA;AACpC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,eAAe,CAAA;AAC7B,cAAc,sBAAsB,CAAA;AACpC,cAAc,sBAAsB,CAAA;AACpC,cAAc,yBAAyB,CAAA;AACvC,cAAc,kBAAkB,CAAA;AAChC,OAAO,EACH,2BAA2B,EAC3B,uBAAuB,EACvB,4BAA4B,EAC5B,KAAK,2BAA2B,EAChC,KAAK,qBAAqB,EAC1B,KAAK,8BAA8B,GACtC,MAAM,+BAA+B,CAAA;AACtC,OAAO,EACH,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,EAChB,wBAAwB,EACxB,WAAW,EACX,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,KAAK,sBAAsB,GAC9B,MAAM,yBAAyB,CAAA;AAChC,cAAc,gBAAgB,CAAA;AAC9B,YAAY,EACR,kBAAkB,EAClB,YAAY,IAAI,yBAAyB,EACzC,iBAAiB,EACjB,oBAAoB,GACvB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EACH,wBAAwB,EACxB,4BAA4B,EAC5B,cAAc,EACd,gBAAgB,EAChB,oBAAoB,EACpB,KAAK,qBAAqB,GAC7B,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAA;AACzD,OAAO,EACH,OAAO,EACP,QAAQ,EACR,cAAc,EACd,UAAU,EACV,QAAQ,EACR,WAAW,EACX,WAAW,EACX,UAAU,EACV,SAAS,EACT,UAAU,EACV,aAAa,EACb,eAAe,EACf,kBAAkB,EAClB,KAAK,OAAO,EACZ,KAAK,cAAc,GACtB,MAAM,YAAY,CAAA;AACnB,OAAO,EACH,cAAc,EACd,YAAY,EACZ,WAAW,EACX,UAAU,EACV,KAAK,mBAAmB,EACxB,KAAK,SAAS,IAAI,uBAAuB,GAC5C,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAClE,OAAO,EAAE,mBAAmB,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAA;AACzE,OAAO,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAA;AACxE,YAAY,EAAE,wBAAwB,EAAE,QAAQ,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AAC5G,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAA;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9C,YAAY,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AAC5E,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAA;AAC/D,YAAY,EACR,QAAQ,EACR,WAAW,EACX,YAAY,EACZ,iBAAiB,EACjB,uBAAuB,EACvB,qBAAqB,GACxB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EACH,eAAe,EACf,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,GAC9B,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EACH,eAAe,EACf,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC3B,KAAK,mBAAmB,EACxB,yBAAyB,EACzB,kBAAkB,EAClB,wBAAwB,EACxB,cAAc,GACjB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EACH,gBAAgB,EAChB,eAAe,EACf,oBAAoB,EACpB,KAAK,qBAAqB,GAC7B,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EACH,sBAAsB,EACtB,iBAAiB,EACjB,oBAAoB,EACpB,KAAK,cAAc,EACnB,KAAK,mBAAmB,GAC3B,MAAM,4BAA4B,CAAA;AACnC,OAAO,EACH,sBAAsB,EACtB,sBAAsB,EACtB,6BAA6B,EAC7B,0BAA0B,EAC1B,uBAAuB,GAC1B,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EACH,kBAAkB,EAClB,aAAa,EACb,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,sBAAsB,EAC3B,KAAK,wBAAwB,GAChC,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EACH,kBAAkB,EAClB,kBAAkB,EAClB,qBAAqB,EACrB,KAAK,eAAe,GACvB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACH,iBAAiB,EACjB,YAAY,EACZ,mBAAmB,EACnB,gBAAgB,EAChB,oBAAoB,EACpB,cAAc,EACd,mBAAmB,GACtB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EACH,qBAAqB,EACrB,KAAK,0BAA0B,GAClC,MAAM,2BAA2B,CAAA;AAClC,OAAO,EACH,YAAY,EACZ,KAAK,aAAa,EAClB,KAAK,iBAAiB,GACzB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACH,aAAa,EACb,KAAK,kBAAkB,GAC1B,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACH,gBAAgB,EAChB,KAAK,qBAAqB,GAC7B,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EACH,aAAa,EACb,eAAe,GAClB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAA;AAC/D,YAAY,EACR,UAAU,EACV,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,aAAa,EACb,oBAAoB,EACpB,sBAAsB,EACtB,mBAAmB,EACnB,iBAAiB,EACjB,UAAU,EACV,oBAAoB,EACpB,cAAc,EACd,kBAAkB,EAClB,oBAAoB,GACvB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EACH,UAAU,EACV,SAAS,EACT,UAAU,EACV,UAAU,EACV,SAAS,EACT,WAAW,EACX,UAAU,EACV,cAAc,EACd,KAAK,iBAAiB,GACzB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EACH,cAAc,EACd,cAAc,EACd,SAAS,EACT,UAAU,EACV,KAAK,mBAAmB,GAC3B,MAAM,2BAA2B,CAAA;AAClC,OAAO,EACH,UAAU,EACV,SAAS,EACT,WAAW,EACX,WAAW,EACX,KAAK,eAAe,GACvB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EACH,iBAAiB,EACjB,cAAc,EACd,WAAW,EACX,aAAa,EACb,YAAY,EACZ,aAAa,EACb,KAAK,aAAa,EAClB,KAAK,eAAe,GACvB,MAAM,yBAAyB,CAAA"}
package/dist/index.js CHANGED
@@ -34,6 +34,8 @@ export * from './navigation-builder';
34
34
  export * from './i18n-provider';
35
35
  export * from './api-context';
36
36
  export * from './use-addon-settings';
37
+ export * from './use-print-document';
38
+ export * from './print-document-button';
37
39
  export * from './metadata-cache';
38
40
  export { ADDON_MANIFEST_CHANGED_TYPE, wireHotSwapInvalidation, useManifestHotSwapSubscriber, } from './manifest-hotswap-subscriber';
39
41
  export { useHotSwapReload, applyHotSwapReload, withVersionParam, clearFederationContainer, shortenHash, } from './hotswap-reload-policy';
@@ -46,6 +48,7 @@ export { NIL_UUID, isNilUuid, normalizeNilUuid } from './nil-uuid';
46
48
  export { DynamicRecordDialog, ViewValue } from './dialogs/dynamic-record';
47
49
  export { normalizeRefFieldsForSubmit } from './dialogs/normalize-submit';
48
50
  export { CreateRecordDialog } from './dialogs/create-record-dialog';
51
+ export { EntitySelect } from './entity-select';
49
52
  export { ViewRecordDialog } from './dialogs/view-record-dialog';
50
53
  export { ExportDialog } from './dialogs/export';
51
54
  export { ImportDialog } from './dialogs/import';
@@ -0,0 +1,21 @@
1
+ import React from 'react';
2
+ import { type PrintDocumentArgs } from './use-print-document';
3
+ export interface PrintDocumentButtonProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'onError'> {
4
+ /** Model KEY the document is declared against (e.g. "SalesOrder"). */
5
+ model: string;
6
+ /** Record id. */
7
+ id: string;
8
+ /** Document key from contributions.documents[].key (e.g. "sale_ticket"). */
9
+ documentKey: string;
10
+ /** print (default) | download | open — see usePrintDocument. */
11
+ mode?: PrintDocumentArgs['mode'];
12
+ /** Download filename (mode="download"). */
13
+ filename?: string;
14
+ /** Called if the fetch/print fails (surface it to a toast in the host). */
15
+ onError?: (err: unknown) => void;
16
+ /** Rendered while the PDF is being fetched, in place of children. */
17
+ pendingLabel?: React.ReactNode;
18
+ children?: React.ReactNode;
19
+ }
20
+ export declare function PrintDocumentButton({ model, id, documentKey, mode, filename, onError, pendingLabel, children, disabled, onClick, ...rest }: PrintDocumentButtonProps): React.JSX.Element;
21
+ //# sourceMappingURL=print-document-button.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"print-document-button.d.ts","sourceRoot":"","sources":["../src/print-document-button.tsx"],"names":[],"mappings":"AASA,OAAO,KAAgC,MAAM,OAAO,CAAA;AACpD,OAAO,EAAoB,KAAK,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AAE/E,MAAM,WAAW,wBACb,SAAQ,IAAI,CAAC,KAAK,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,SAAS,CAAC;IACtE,sEAAsE;IACtE,KAAK,EAAE,MAAM,CAAA;IACb,iBAAiB;IACjB,EAAE,EAAE,MAAM,CAAA;IACV,4EAA4E;IAC5E,WAAW,EAAE,MAAM,CAAA;IACnB,gEAAgE;IAChE,IAAI,CAAC,EAAE,iBAAiB,CAAC,MAAM,CAAC,CAAA;IAChC,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,2EAA2E;IAC3E,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAA;IAChC,qEAAqE;IACrE,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC9B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAC7B;AAED,wBAAgB,mBAAmB,CAAC,EAChC,KAAK,EACL,EAAE,EACF,WAAW,EACX,IAAI,EACJ,QAAQ,EACR,OAAO,EACP,YAAY,EACZ,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,GAAG,IAAI,EACV,EAAE,wBAAwB,qBA+B1B"}
@@ -0,0 +1,32 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // PrintDocumentButton — a drop-in button that prints/downloads a server-rendered
3
+ // document via usePrintDocument, so an addon doesn't re-wire the hook + loading
4
+ // state every time. Headless-friendly: it renders a plain <button> you style
5
+ // with `className`, disables itself while the PDF is fetching, and reports
6
+ // failures through `onError` (no toast dependency baked in).
7
+ //
8
+ // Example:
9
+ // <PrintDocumentButton model="SalesOrder" id={sale.id} documentKey="sale_ticket"
10
+ // className="btn">Imprimir ticket</PrintDocumentButton>
11
+ import { useCallback, useState } from 'react';
12
+ import { usePrintDocument } from './use-print-document';
13
+ export function PrintDocumentButton({ model, id, documentKey, mode, filename, onError, pendingLabel, children, disabled, onClick, ...rest }) {
14
+ const printDocument = usePrintDocument();
15
+ const [busy, setBusy] = useState(false);
16
+ const handleClick = useCallback(async (e) => {
17
+ onClick?.(e);
18
+ if (e.defaultPrevented)
19
+ return;
20
+ setBusy(true);
21
+ try {
22
+ await printDocument({ model, id, key: documentKey, mode, filename });
23
+ }
24
+ catch (err) {
25
+ onError?.(err);
26
+ }
27
+ finally {
28
+ setBusy(false);
29
+ }
30
+ }, [printDocument, model, id, documentKey, mode, filename, onError, onClick]);
31
+ return (_jsx("button", { type: "button", ...rest, disabled: disabled || busy, "aria-busy": busy || undefined, onClick: handleClick, children: busy && pendingLabel != null ? pendingLabel : children }));
32
+ }
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