@asteby/metacore-runtime-react 49.3.1 → 49.5.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 +14 -0
- package/dist/api-context.d.ts +9 -1
- package/dist/api-context.d.ts.map +1 -1
- package/dist/api-context.js +6 -4
- package/dist/business/document-editor-panels.d.ts +2 -1
- package/dist/business/document-editor-panels.d.ts.map +1 -1
- package/dist/business/document-editor-panels.js +6 -6
- package/dist/business/document-editor.d.ts.map +1 -1
- package/dist/business/document-editor.js +23 -9
- package/dist/business/index.d.ts +1 -0
- package/dist/business/index.d.ts.map +1 -1
- package/dist/business/index.js +1 -0
- package/dist/business/line-items-editor.d.ts +6 -1
- package/dist/business/line-items-editor.d.ts.map +1 -1
- package/dist/business/line-items-editor.js +109 -83
- package/dist/business/line-product-cell.d.ts +32 -0
- package/dist/business/line-product-cell.d.ts.map +1 -0
- package/dist/business/line-product-cell.js +100 -0
- package/dist/dynamic-select-field.d.ts +2 -1
- package/dist/dynamic-select-field.d.ts.map +1 -1
- package/dist/dynamic-select-field.js +49 -19
- package/dist/dynamic-table.d.ts +31 -1
- package/dist/dynamic-table.d.ts.map +1 -1
- package/dist/dynamic-table.js +37 -4
- package/dist/entity-select.d.ts.map +1 -1
- package/dist/entity-select.js +13 -11
- package/dist/field-grid.d.ts +7 -1
- package/dist/field-grid.d.ts.map +1 -1
- package/dist/field-grid.js +4 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/query-batch.d.ts.map +1 -1
- package/dist/query-batch.js +17 -1
- package/dist/record-picker-actions.d.ts +27 -0
- package/dist/record-picker-actions.d.ts.map +1 -0
- package/dist/record-picker-actions.js +32 -0
- package/package.json +1 -1
- package/src/__tests__/api-batch-opt-out.test.tsx +83 -0
- package/src/__tests__/document-editor-inline-selects.test.tsx +202 -0
- package/src/__tests__/dynamic-table-bulk-actions.test.tsx +140 -0
- package/src/__tests__/dynamic-table-load-error.test.tsx +97 -0
- package/src/api-context.tsx +19 -4
- package/src/business/document-editor-panels.tsx +13 -10
- package/src/business/document-editor.tsx +72 -20
- package/src/business/index.ts +1 -0
- package/src/business/line-items-editor.tsx +229 -174
- package/src/business/line-product-cell.tsx +219 -0
- package/src/dynamic-select-field.tsx +51 -22
- package/src/dynamic-table.tsx +91 -4
- package/src/entity-select.tsx +17 -39
- package/src/field-grid.tsx +15 -1
- package/src/index.ts +2 -0
- package/src/query-batch.ts +18 -1
- package/src/record-picker-actions.tsx +81 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @asteby/metacore-runtime-react
|
|
2
2
|
|
|
3
|
+
## 49.5.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 2fca21f: `ApiProvider` acepta `batch={false}` para no agrupar lecturas en `POST /q`: un host cuyo backend no implementa `/q` ya no paga un POST fallido por cada lectura. Además, con el batch activo, si `/q` responde 404/405 el cliente deja de intentarlo tras el primer fallo y la lectura se resuelve por el GET normal. El comportamiento por defecto no cambia para hosts que sí tienen `/q`.
|
|
8
|
+
- 2575411: DynamicTable: nueva primitiva de acciones masivas. `extraBulkActions` (nodo o función `({ selectedRows, selectedIds, clearSelection, refresh }) => nodo`) pinta las acciones del host en la barra flotante de selección, junto al "Eliminar" integrado; `hideBulkDelete` oculta ese "Eliminar". La selección ahora se indexa por `id` del registro (antes por posición en la página) y, con paginación clásica, se vacía al cambiar de página, tamaño, orden, búsqueda o filtros de columna, de modo que una acción masiva nunca actúa sobre filas que el usuario ya no ve.
|
|
9
|
+
- e030a86: DynamicTable: un fallo del endpoint de lista que no es 403 (500, red o `success: false`) ya no se ve como "No se encontraron resultados": muestra "No se pudieron cargar los datos" con un botón "Reintentar" que vuelve a pedir la lista (claves `dynamic.load_error_title`, `dynamic.load_error_hint`, `dynamic.retry`). Nueva prop opcional `emptyState` para mostrar contenido de primer uso cuando la lista carga bien, está vacía y no hay búsqueda ni filtros activos; nunca tapa un error ni un 403.
|
|
10
|
+
|
|
11
|
+
## 49.4.0
|
|
12
|
+
|
|
13
|
+
### Minor Changes
|
|
14
|
+
|
|
15
|
+
- e95a6a4: DocumentEditor con selects «pro»: el buscador de producto vive en la celda «Descripción» de cada renglón (con un renglón vacío siempre al final, Enter elige y pasa a «Cant.»), los resultados salen en un portal collision-aware que nunca recorta la tabla ni el modal, y los dynamic_select con `ref` traen crear/editar unidos al trigger (`RecordPickerAction`, compartido con `EntitySelect`; el lápiz despacha `metacore:edit-record`). Pulido visual del editor: cabecera agrupada en tres columnas, etiquetas en sentence case, «Cargar desde» segmentado con estado vacío, tabla densa con totales alineados a «Importe», panel de totales con estado vacío y atajo ⌘/Ctrl+Enter para guardar.
|
|
16
|
+
|
|
3
17
|
## 49.3.1
|
|
4
18
|
|
|
5
19
|
### Patch Changes
|
package/dist/api-context.d.ts
CHANGED
|
@@ -20,9 +20,17 @@ export interface ApiClient {
|
|
|
20
20
|
}
|
|
21
21
|
export interface ApiProviderProps {
|
|
22
22
|
client: ApiClient;
|
|
23
|
+
/**
|
|
24
|
+
* Groups list, metadata and options reads into one `POST /q`. Defaults to
|
|
25
|
+
* `true`. Pass `false` when the host backend does not implement `/q`: every
|
|
26
|
+
* read then goes straight to its own GET and no POST is attempted. When
|
|
27
|
+
* left on and `/q` answers 404/405, the batch turns itself off for that
|
|
28
|
+
* client after the first failed attempt and the read falls back to GET.
|
|
29
|
+
*/
|
|
30
|
+
batch?: boolean;
|
|
23
31
|
children: React.ReactNode;
|
|
24
32
|
}
|
|
25
|
-
export declare function ApiProvider({ client, children }: ApiProviderProps): React.JSX.Element;
|
|
33
|
+
export declare function ApiProvider({ client, batch, children }: ApiProviderProps): React.JSX.Element;
|
|
26
34
|
/** Returns the host-injected api client. Throws if no <ApiProvider> is mounted. */
|
|
27
35
|
export declare function useApi(): ApiClient;
|
|
28
36
|
/** Optional branch context — hosts that support tenant branches can supply
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-context.d.ts","sourceRoot":"","sources":["../src/api-context.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAwD,MAAM,OAAO,CAAA;AAI5E,uEAAuE;AACvE,MAAM,WAAW,SAAS;IACtB,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,KAAK,OAAO,CAAC;QAAE,IAAI,EAAE,GAAG,CAAC;QAAC,OAAO,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC,CAAA;IACzE,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,EAAE,GAAG,KAAK,OAAO,CAAC;QAAE,IAAI,EAAE,GAAG,CAAC;QAAC,OAAO,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC,CAAA;IACtF,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,EAAE,GAAG,KAAK,OAAO,CAAC;QAAE,IAAI,EAAE,GAAG,CAAC;QAAC,OAAO,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC,CAAA;IACrF,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,KAAK,OAAO,CAAC;QAAE,IAAI,EAAE,GAAG,CAAC;QAAC,OAAO,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC,CAAA;CAC/E;
|
|
1
|
+
{"version":3,"file":"api-context.d.ts","sourceRoot":"","sources":["../src/api-context.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAwD,MAAM,OAAO,CAAA;AAI5E,uEAAuE;AACvE,MAAM,WAAW,SAAS;IACtB,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,KAAK,OAAO,CAAC;QAAE,IAAI,EAAE,GAAG,CAAC;QAAC,OAAO,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC,CAAA;IACzE,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,EAAE,GAAG,KAAK,OAAO,CAAC;QAAE,IAAI,EAAE,GAAG,CAAC;QAAC,OAAO,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC,CAAA;IACtF,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,EAAE,GAAG,KAAK,OAAO,CAAC;QAAE,IAAI,EAAE,GAAG,CAAC;QAAC,OAAO,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC,CAAA;IACrF,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,KAAK,OAAO,CAAC;QAAE,IAAI,EAAE,GAAG,CAAC;QAAC,OAAO,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC,CAAA;CAC/E;AAKD,MAAM,WAAW,gBAAgB;IAC7B,MAAM,EAAE,SAAS,CAAA;IACjB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAC5B;AAED,wBAAgB,WAAW,CAAC,EAAE,MAAM,EAAE,KAAY,EAAE,QAAQ,EAAE,EAAE,gBAAgB,qBA2B/E;AAmCD,mFAAmF;AACnF,wBAAgB,MAAM,IAAI,SAAS,CAoBlC;AAED;;wEAEwE;AACxE,MAAM,WAAW,WAAW;IACxB,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;CACzC;AAID,MAAM,WAAW,mBAAmB;IAChC,MAAM,EAAE,WAAW,CAAA;IACnB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAC5B;AAED,wBAAgB,cAAc,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,mBAAmB,qBAEvE;AAED,wBAAgB,gBAAgB,IAAI,WAAW,CAE9C"}
|
package/dist/api-context.js
CHANGED
|
@@ -7,7 +7,8 @@ import { createContext, useContext, useEffect, useMemo } from 'react';
|
|
|
7
7
|
import { batchGet, invalidateQueryBatchData } from './query-batch';
|
|
8
8
|
import { invalidateOptionsCache } from './use-options-resolver';
|
|
9
9
|
const ApiContext = createContext(null);
|
|
10
|
-
|
|
10
|
+
const BatchContext = createContext(true);
|
|
11
|
+
export function ApiProvider({ client, batch = true, children }) {
|
|
11
12
|
// Hosts also write through their own client (native dialogs, settings
|
|
12
13
|
// screens) without going through useApi(). When the client is axios,
|
|
13
14
|
// every non-GET response drops what the runtime remembered as well.
|
|
@@ -28,7 +29,7 @@ export function ApiProvider({ client, children }) {
|
|
|
28
29
|
});
|
|
29
30
|
return () => interceptors.eject?.(id);
|
|
30
31
|
}, [client]);
|
|
31
|
-
return _jsx(ApiContext.Provider, { value: client, children: children });
|
|
32
|
+
return (_jsx(ApiContext.Provider, { value: client, children: _jsx(BatchContext.Provider, { value: batch, children: children }) }));
|
|
32
33
|
}
|
|
33
34
|
// POSTs that do not change any record a list or picker shows: the read batch
|
|
34
35
|
// (/q) and the bell inbox the host writes on every control toast
|
|
@@ -54,6 +55,7 @@ function mutating(request) {
|
|
|
54
55
|
/** Returns the host-injected api client. Throws if no <ApiProvider> is mounted. */
|
|
55
56
|
export function useApi() {
|
|
56
57
|
const ctx = useContext(ApiContext);
|
|
58
|
+
const batch = useContext(BatchContext);
|
|
57
59
|
if (!ctx) {
|
|
58
60
|
throw new Error('useApi() requires an <ApiProvider> ancestor. Hosts must inject an axios-like client via runtime-react ApiProvider.');
|
|
59
61
|
}
|
|
@@ -64,11 +66,11 @@ export function useApi() {
|
|
|
64
66
|
return useMemo(() => ({
|
|
65
67
|
// Each call reaches the host client with the arguments it was given —
|
|
66
68
|
// never a trailing `undefined` it didn't ask for.
|
|
67
|
-
get: (...args) => batchGet(ctx, ...args),
|
|
69
|
+
get: (...args) => batch ? batchGet(ctx, ...args) : ctx.get(...args),
|
|
68
70
|
post: (...args) => isWrite('post', args[0]) ? mutating(ctx.post(...args)) : ctx.post(...args),
|
|
69
71
|
put: (...args) => mutating(ctx.put(...args)),
|
|
70
72
|
delete: (...args) => mutating(ctx.delete(...args)),
|
|
71
|
-
}), [ctx]);
|
|
73
|
+
}), [ctx, batch]);
|
|
72
74
|
}
|
|
73
75
|
const BranchContext = createContext({ id: undefined });
|
|
74
76
|
export function BranchProvider({ branch, children }) {
|
|
@@ -44,8 +44,9 @@ export interface TotalsRow {
|
|
|
44
44
|
emphasis?: boolean;
|
|
45
45
|
}
|
|
46
46
|
/** Totales alineados a la derecha; la última fila con `emphasis` es el total. */
|
|
47
|
-
export declare function TotalsPanel({ rows }: {
|
|
47
|
+
export declare function TotalsPanel({ rows, muted }: {
|
|
48
48
|
rows: TotalsRow[];
|
|
49
|
+
muted?: boolean;
|
|
49
50
|
}): import("react").JSX.Element;
|
|
50
51
|
/** Lista de revisión: errores (rojo) y avisos (ámbar). Vacía → no pinta nada. */
|
|
51
52
|
export declare function ValidationChecklist({ issues, title }: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"document-editor-panels.d.ts","sourceRoot":"","sources":["../../src/business/document-editor-panels.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAmB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAGvD,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AACxE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AACzC,OAAO,EAAe,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAA;AAEzD,4EAA4E;AAC5E,wBAAgB,aAAa,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE;IAAE,KAAK,CAAC,EAAE,SAAS,CAAC;IAAC,IAAI,CAAC,EAAE,SAAS,CAAC;IAAC,QAAQ,EAAE,SAAS,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,+BAYzI;AAED,MAAM,WAAW,uBAAuB;IACpC,KAAK,EAAE,SAAS,CAAA;IAChB,qEAAqE;IACrE,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACtC,QAAQ,EAAE,SAAS,CAAA;IACnB,IAAI,CAAC,EAAE,MAAM,CAAA;CAChB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,WAAmB,EAAE,YAAY,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,uBAAuB,+BA4BtI;AAED,MAAM,WAAW,cAAc;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC1D,MAAM,CAAC,EAAE,YAAY,GAAG,IAAI,CAAA;IAC5B,GAAG,EAAE,SAAS,CAAA;IACd,QAAQ,CAAC,EAAE,SAAS,CAAA;CACvB;AAED,uFAAuF;AACvF,wBAAgB,SAAS,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,cAAc,+BA4B9E;AAED,MAAM,WAAW,SAAS;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,OAAO,CAAA;CACrB;AAED,iFAAiF;AACjF,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,SAAS,EAAE,CAAA;CAAE,+
|
|
1
|
+
{"version":3,"file":"document-editor-panels.d.ts","sourceRoot":"","sources":["../../src/business/document-editor-panels.tsx"],"names":[],"mappings":"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"}
|
|
@@ -9,7 +9,7 @@ import { AlertTriangle, ChevronDown, XCircle } from 'lucide-react';
|
|
|
9
9
|
import { computeLine } from './line-items';
|
|
10
10
|
/** Bloque con título discreto y espaciado generoso (sin bordes pesados). */
|
|
11
11
|
export function EditorSection({ title, hint, children, slot }) {
|
|
12
|
-
return (_jsxs("section", { className: "space-y-3", "data-slot": slot, children: [(title || hint) && (_jsxs("header", { className: "flex items-baseline justify-between gap-3", children: [title && _jsx("h3", { className: "text-sm font-semibold", children: title }), hint && _jsx("span", { className: "text-xs text-muted-foreground", children: hint })] })), children] }));
|
|
12
|
+
return (_jsxs("section", { className: "space-y-3", "data-slot": slot, children: [(title || hint) && (_jsxs("header", { className: "flex flex-wrap items-baseline justify-between gap-x-3 gap-y-1", children: [title && _jsx("h3", { className: "whitespace-nowrap text-sm font-semibold", children: title }), hint && _jsx("span", { className: "text-xs text-muted-foreground", children: hint })] })), children] }));
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
15
15
|
* Sección plegable. El contenido queda MONTADO aunque esté cerrada (`hidden`):
|
|
@@ -24,25 +24,25 @@ export function CollapsibleSection({ title, summary, open, defaultOpen = false,
|
|
|
24
24
|
setInner(!isOpen);
|
|
25
25
|
onOpenChange?.(!isOpen);
|
|
26
26
|
};
|
|
27
|
-
return (_jsxs("section", { className: "rounded-
|
|
27
|
+
return (_jsxs("section", { className: "rounded-xl border border-dashed", "data-slot": slot, "data-state": isOpen ? 'open' : 'closed', children: [_jsxs("button", { type: "button", "aria-expanded": isOpen, "aria-controls": id, onClick: toggle, className: "flex w-full items-center justify-between gap-3 rounded-xl px-4 py-3 text-left text-sm transition-colors hover:bg-muted/30 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50", children: [_jsx("span", { className: "font-medium", children: title }), _jsxs("span", { className: "flex min-w-0 items-center gap-2 text-xs text-muted-foreground", children: [!isOpen && summary && _jsx("span", { className: "truncate", children: summary }), _jsx(ChevronDown, { className: `size-4 shrink-0 transition-transform ${isOpen ? 'rotate-180' : ''}`, "aria-hidden": true })] })] }), _jsx("div", { id: id, hidden: !isOpen, className: "space-y-4 px-4 pb-4", children: children })] }));
|
|
28
28
|
}
|
|
29
29
|
/** Tarjeta compacta de la contraparte: nombre, 3-6 datos clave y estado de crédito. */
|
|
30
30
|
export function PartyCard({ name, rows, credit, fmt, children }) {
|
|
31
|
-
return (_jsxs("div", { className: "rounded-
|
|
31
|
+
return (_jsxs("div", { className: "rounded-lg bg-muted/40 px-4 py-3 text-sm", "data-slot": "party-card", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [name && _jsx("span", { className: "font-medium", children: name }), credit && (_jsx(Badge, { variant: credit.level === 'block' ? 'destructive' : credit.level === 'warn' ? 'secondary' : 'outline', "data-slot": "party-credit", children: credit.holdReason
|
|
32
32
|
? credit.holdReason
|
|
33
33
|
: credit.available != null
|
|
34
34
|
? `Crédito disponible ${fmt.money(credit.available)}`
|
|
35
35
|
: `Saldo ${fmt.money(credit.balance ?? 0)}` }))] }), rows.length > 0 && (_jsx("dl", { className: "mt-2 grid grid-cols-2 gap-x-6 gap-y-1 sm:grid-cols-3", children: rows.map((r) => (_jsxs("div", { className: "min-w-0", children: [_jsx("dt", { className: "text-xs text-muted-foreground", children: r.label }), _jsx("dd", { className: "truncate", children: r.value })] }, r.key))) })), children] }));
|
|
36
36
|
}
|
|
37
37
|
/** Totales alineados a la derecha; la última fila con `emphasis` es el total. */
|
|
38
|
-
export function TotalsPanel({ rows }) {
|
|
39
|
-
return (_jsx("dl", { className:
|
|
38
|
+
export function TotalsPanel({ rows, muted = false }) {
|
|
39
|
+
return (_jsx("dl", { className: `ml-auto w-full max-w-xs space-y-1.5 text-sm tabular-nums ${muted ? 'text-muted-foreground' : ''}`, "data-slot": "totals", children: rows.map((r, i) => (_jsxs("div", { className: `flex items-baseline justify-between gap-6 ${r.emphasis ? `${i > 0 ? 'border-t pt-2.5' : ''} text-lg font-semibold` : ''}`, children: [_jsx("dt", { className: r.emphasis ? '' : 'text-muted-foreground', children: r.label }), _jsx("dd", { children: r.value })] }, r.label))) }));
|
|
40
40
|
}
|
|
41
41
|
/** Lista de revisión: errores (rojo) y avisos (ámbar). Vacía → no pinta nada. */
|
|
42
42
|
export function ValidationChecklist({ issues, title = 'Revisa antes de guardar' }) {
|
|
43
43
|
if (issues.length === 0)
|
|
44
44
|
return null;
|
|
45
|
-
return (_jsxs("div", { className: "rounded-md bg-muted/30 px-4 py-3", "data-slot": "validation-checklist", role: "status", children: [_jsx("p", { className: "mb-1 text-xs font-medium text-muted-foreground", children: title }), _jsx("ul", { className: "space-y-1 text-sm", children: issues.map((i, k) => (_jsxs("li", { className: `flex items-start gap-2 ${i.severity === 'error' ? 'text-destructive' : 'text-amber-700'}`, children: [i.severity === 'error' ? _jsx(XCircle, { className: "mt-0.5 size-4 shrink-0", "aria-hidden": true }) : _jsx(AlertTriangle, { className: "mt-0.5 size-4 shrink-0", "aria-hidden": true }), i.message] }, `${i.field ?? ''}:${k}`))) })] }));
|
|
45
|
+
return (_jsxs("div", { className: "rounded-md bg-muted/30 px-4 py-3", "data-slot": "validation-checklist", role: "status", children: [_jsx("p", { className: "mb-1 text-xs font-medium text-muted-foreground", children: title }), _jsx("ul", { className: "space-y-1 text-sm", children: issues.map((i, k) => (_jsxs("li", { className: `flex items-start gap-2 ${i.severity === 'error' ? 'text-destructive' : 'text-amber-700 dark:text-amber-400'}`, children: [i.severity === 'error' ? _jsx(XCircle, { className: "mt-0.5 size-4 shrink-0", "aria-hidden": true }) : _jsx(AlertTriangle, { className: "mt-0.5 size-4 shrink-0", "aria-hidden": true }), i.message] }, `${i.field ?? ''}:${k}`))) })] }));
|
|
46
46
|
}
|
|
47
47
|
/** Contenido de la vista previa (PDF, HTML o XML, en ese orden de preferencia). */
|
|
48
48
|
export function PreviewPanel({ preview }) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"document-editor.d.ts","sourceRoot":"","sources":["../../src/business/document-editor.tsx"],"names":[],"mappings":"AA4BA,OAAO,EAAqB,KAAK,sBAAsB,EAAwB,MAAM,qBAAqB,CAAA;AAkD1G,OAAO,KAAK,EAAsC,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAA;AAE3G,MAAM,WAAW,mBAAmB;IAChC,KAAK,EAAE,MAAM,CAAA;IACb,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,qBAAqB,CAAA;IAC5B,IAAI,EAAE,gBAAgB,CAAA;IACtB,sFAAsF;IACtF,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC5B,oEAAoE;IACpE,aAAa,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAA;IAC3C,OAAO,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;IACpC,QAAQ,EAAE,MAAM,IAAI,CAAA;IACpB,cAAc,CAAC,EAAE,sBAAsB,CAAC,QAAQ,CAAC,CAAA;IACjD,+EAA+E;IAC/E,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,6DAA6D;IAC7D,KAAK,CAAC,EAAE,IAAI,CAAA;IACZ;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;CACvB;AAID,wBAAgB,cAAc,CAAC,EAC3B,KAAK,EACL,QAAQ,EACR,KAAK,EACL,IAAI,EACJ,MAAM,EACN,aAAa,EACb,OAAO,EACP,QAAQ,EACR,cAAc,EACd,YAAY,EACZ,cAAc,EACd,QAAQ,EACR,KAAK,EACL,UAAkB,GACrB,EAAE,mBAAmB,+
|
|
1
|
+
{"version":3,"file":"document-editor.d.ts","sourceRoot":"","sources":["../../src/business/document-editor.tsx"],"names":[],"mappings":"AA4BA,OAAO,EAAqB,KAAK,sBAAsB,EAAwB,MAAM,qBAAqB,CAAA;AAkD1G,OAAO,KAAK,EAAsC,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAA;AAE3G,MAAM,WAAW,mBAAmB;IAChC,KAAK,EAAE,MAAM,CAAA;IACb,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,qBAAqB,CAAA;IAC5B,IAAI,EAAE,gBAAgB,CAAA;IACtB,sFAAsF;IACtF,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC5B,oEAAoE;IACpE,aAAa,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAA;IAC3C,OAAO,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;IACpC,QAAQ,EAAE,MAAM,IAAI,CAAA;IACpB,cAAc,CAAC,EAAE,sBAAsB,CAAC,QAAQ,CAAC,CAAA;IACjD,+EAA+E;IAC/E,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,6DAA6D;IAC7D,KAAK,CAAC,EAAE,IAAI,CAAA;IACZ;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;CACvB;AAID,wBAAgB,cAAc,CAAC,EAC3B,KAAK,EACL,QAAQ,EACR,KAAK,EACL,IAAI,EACJ,MAAM,EACN,aAAa,EACb,OAAO,EACP,QAAQ,EACR,cAAc,EACd,YAAY,EACZ,cAAc,EACd,QAAQ,EACR,KAAK,EACL,UAAkB,GACrB,EAAE,mBAAmB,+BA4xBrB"}
|
|
@@ -21,7 +21,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
21
21
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
22
22
|
import { useTranslation } from 'react-i18next';
|
|
23
23
|
import { Button } from '@asteby/metacore-ui/primitives';
|
|
24
|
-
import { Loader2 } from 'lucide-react';
|
|
24
|
+
import { Loader2, ReceiptText } from 'lucide-react';
|
|
25
25
|
import { toast } from 'sonner';
|
|
26
26
|
import { useApi } from '../api-context';
|
|
27
27
|
import { useOrgTaxRate, useTimeZone } from '../org-runtime-context';
|
|
@@ -482,7 +482,7 @@ export function DocumentEditor({ model, endpoint, forms, type, record, initialSo
|
|
|
482
482
|
setFieldErrors((prev) => clearFieldErrorTree(prev, key));
|
|
483
483
|
};
|
|
484
484
|
const fieldRecord = useMemo(() => seedRecord(seeds, record), [seeds, record]);
|
|
485
|
-
const fieldCell = (field, fullWidth = false) => visible(field) ? (_jsxs(FieldCell, { fullWidth: fullWidth, children: [_jsx(FieldLabel, { htmlFor: field.key, required: field.required, children: tl(field.label) }), renderField(withFriendlyOptions(gatedOptions(field, header)), header[field.key], (v) => updateField(field.key, v), header, fieldRecord, fieldErrors), fieldErrors[field.key] && _jsx("p", { className: "mt-1 text-xs text-destructive", children: fieldErrors[field.key] })] }, field.key)) : null;
|
|
485
|
+
const fieldCell = (field, fullWidth = false, className) => visible(field) ? (_jsxs(FieldCell, { fullWidth: fullWidth, className: className, children: [_jsx(FieldLabel, { htmlFor: field.key, required: field.required, tone: "sentence", children: tl(field.label) }), renderField(withFriendlyOptions(gatedOptions(field, header)), header[field.key], (v) => updateField(field.key, v), header, fieldRecord, fieldErrors), fieldErrors[field.key] && _jsx("p", { className: "mt-1 text-xs text-destructive", children: fieldErrors[field.key] })] }, field.key)) : null;
|
|
486
486
|
const summaryRows = partySummaryRows(party, type.party?.summary, {
|
|
487
487
|
...Object.fromEntries(Object.entries(partyLabels).map(([k, v]) => [k, tl(v)])),
|
|
488
488
|
...Object.fromEntries(allFields.map((f) => [f.key, tl(f.label)])),
|
|
@@ -525,21 +525,28 @@ export function DocumentEditor({ model, endpoint, forms, type, record, initialSo
|
|
|
525
525
|
// Sin acción de vista previa del servidor, un documento con renglones ofrece
|
|
526
526
|
// la suya local (encabezado, renglones y totales): sin borrador ni red.
|
|
527
527
|
const localPreview = !previewEnabled && !type.preview && !!lineCfg && !isAllocation && kind !== 'credit';
|
|
528
|
-
const essentials = (_jsxs(EditorSection, { slot: "editor-essentials", children: [_jsxs(FieldGrid, { children: [groups.party && fieldCell(groups.party), groups.essential.map((f) => fieldCell(f))] }), party && (_jsx(PartyCard, { name: party.name ?? party.legal_name, rows: summaryRows, credit: credit, fmt: fmt, children: partyContribs.map(({ id, component: C }) => (_jsx(C, { ...contribProps }, id))) }))] }));
|
|
529
|
-
const loadFrom = sources.length > 0 && !isAllocation && (_jsx(EditorSection, { slot: "load-from-source", title: t('documentEditor.load_from', { defaultValue: 'Cargar desde…' }), hint: busy === 'source'
|
|
528
|
+
const essentials = (_jsxs(EditorSection, { slot: "editor-essentials", children: [_jsx("div", { className: "@container rounded-xl border bg-card/40 p-4", children: _jsxs(FieldGrid, { className: "gap-x-4 @2xl:grid-cols-3", children: [groups.party && fieldCell(groups.party, false, '@2xl:col-span-2'), groups.essential.map((f) => fieldCell(f))] }) }), party && (_jsx(PartyCard, { name: party.name ?? party.legal_name, rows: summaryRows, credit: credit, fmt: fmt, children: partyContribs.map(({ id, component: C }) => (_jsx(C, { ...contribProps }, id))) }))] }));
|
|
529
|
+
const loadFrom = sources.length > 0 && !isAllocation && (_jsx(EditorSection, { slot: "load-from-source", title: t('documentEditor.load_from', { defaultValue: 'Cargar desde…' }), hint: busy === 'source'
|
|
530
|
+
? t('documentEditor.loading_source', { defaultValue: 'Cargando…' })
|
|
531
|
+
: sourceNote ?? (sourceId ? undefined : t('documentEditor.load_from_hint', { defaultValue: 'Opcional · precarga contraparte y renglones' })), children: _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [sources.length > 1 && (_jsx("div", { role: "radiogroup", "aria-label": t('documentEditor.source_kind', { defaultValue: 'Documento de origen' }), className: "inline-flex h-9 shrink-0 items-center gap-0.5 rounded-md border bg-muted/40 p-0.5", children: sources.map((s) => (_jsx("button", { type: "button", role: "radio", "aria-checked": s.key === sourceKey, "data-source-kind": s.key, onClick: () => {
|
|
530
532
|
setSourceKey(s.key);
|
|
531
533
|
setSourceId('');
|
|
532
|
-
}, className: `rounded px-3
|
|
534
|
+
}, className: `h-full rounded-sm px-3 text-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 ${s.key === sourceKey
|
|
535
|
+
? 'bg-background font-medium text-foreground shadow-xs dark:bg-input/60'
|
|
536
|
+
: 'text-muted-foreground hover:text-foreground'}`, children: tl(s.label) }, s.key))) })), !activeSource && (_jsx("div", { className: "flex h-9 min-w-60 flex-1 items-center rounded-md border border-dashed px-3 text-sm text-muted-foreground", "data-slot": "source-picker-empty", children: t('documentEditor.pick_source_kind', { defaultValue: 'Elige el tipo de documento para buscarlo' }) })), activeSource && (_jsx(SourcePicker, { source: activeSource, value: sourceId, seed: sourceSeed, header: header, label: tl(activeSource.label), onPick: (id) => {
|
|
533
537
|
setSourceKey(activeSource.key);
|
|
534
538
|
setSourceId(id);
|
|
535
539
|
if (id)
|
|
536
540
|
void loadFromSource(activeSource, id);
|
|
537
541
|
} }, activeSource.key))] }) }));
|
|
538
|
-
|
|
542
|
+
// Sin renglones todavía: un estado vacío amable en vez de $0.00 en cada fila.
|
|
543
|
+
const noLines = !isAllocation && !!lineCfg && lines.length === 0;
|
|
544
|
+
const totalsPanel = noLines ? (_jsx(TotalsPanel, { rows: totalsRows.filter((r) => r.emphasis), muted: true })) : (_jsx(TotalsPanel, { rows: totalsRows }));
|
|
545
|
+
const totalsEmpty = noLines && (_jsxs("div", { className: "flex items-start gap-3 text-sm text-muted-foreground", "data-slot": "totals-empty", children: [_jsx(ReceiptText, { className: "mt-0.5 size-4 shrink-0", "aria-hidden": true }), _jsx("p", { children: t('documentEditor.totals_empty', { defaultValue: 'Aún no hay renglones. Busca un producto en la tabla para ver el desglose.' }) })] }));
|
|
539
546
|
const linesSection = lineCfg && (_jsxs(EditorSection, { slot: isAllocation ? 'editor-allocation' : 'editor-lines', title: linesTitle, hint: isAllocation && openBalance > 0 && amountKey ? (_jsx("button", { type: "button", className: "underline-offset-2 hover:underline", onClick: () => updateField(amountKey, openBalance), children: t('documentEditor.pay_all', { defaultValue: 'Cobrar todo ({{amount}})', amount: fmt.money(openBalance) }) })) : undefined, children: [isAllocation ? (allocPartyId ? (_jsx(PaymentAllocator, { documents: openDocs, result: allocation, value: alloc, onChange: setAlloc, currency: currency, loading: docsLoading, today: today })) : (_jsx("p", { className: "text-sm text-muted-foreground", children: t('documentEditor.pick_party_first', { defaultValue: 'Elige el cliente para ver sus documentos con saldo.' }) }))) : (_jsx(DocumentLinesGrid, { value: lines, onChange: (next) => {
|
|
540
547
|
setLines(next);
|
|
541
548
|
setServerIssues([]);
|
|
542
|
-
}, columns: lineCfg.columns ?? ['discount', 'tax'], priceSource: lineCfg.price_source ?? (kind === 'purchase' ? 'cost' : 'sale'), discountMode: lineCfg.discount_mode, mode: linesFromSourceDoc ? 'from_source' : 'free', policy: kind === 'credit' ? { allowZeroQuantity: linesFromSourceDoc } : undefined, search: search, currency: currency })), !fullscreen && totalsPanel] }));
|
|
549
|
+
}, columns: lineCfg.columns ?? ['discount', 'tax'], priceSource: lineCfg.price_source ?? (kind === 'purchase' ? 'cost' : 'sale'), discountMode: lineCfg.discount_mode, mode: linesFromSourceDoc ? 'from_source' : 'free', policy: kind === 'credit' ? { allowZeroQuantity: linesFromSourceDoc } : undefined, search: search, currency: currency, showTotals: false })), !fullscreen && totalsPanel] }));
|
|
543
550
|
const advanced = (groups.advanced.length > 0 || headerContribs.length > 0) && (_jsxs(CollapsibleSection, { slot: "advanced-options", title: t('documentEditor.advanced', { defaultValue: 'Opciones fiscales' }), summary: advancedSummary || t('documentEditor.advanced_defaults', { defaultValue: 'Valores sugeridos' }), open: advancedOpen, onOpenChange: setAdvancedOpen, children: [groups.advanced.length > 0 && _jsx(FieldGrid, { children: groups.advanced.map((f) => fieldCell(f)) }), headerContribs.map(({ id, component: C }) => (_jsx(C, { ...contribProps }, id)))] }));
|
|
544
551
|
const contributedSections = [...bodyContribs, ...sideContribs].map(({ id, component: C }) => (_jsx("section", { "data-contribution": id, children: _jsx(C, { ...contribProps }) }, id)));
|
|
545
552
|
const notes = groups.notes.length > 0 && _jsx(FieldGrid, { children: groups.notes.map((f) => fieldCell(f, true)) });
|
|
@@ -552,8 +559,15 @@ export function DocumentEditor({ model, endpoint, forms, type, record, initialSo
|
|
|
552
559
|
return { label: tl(f.label), value: v == null || v === '' ? '' : opt ? friendlyOptionLabel(tl(String(opt.label ?? v))) : seeded ?? String(v) };
|
|
553
560
|
})
|
|
554
561
|
.filter((r) => r.value), lines: lines, totals: totalsRows, fmt: fmt }) })) : null;
|
|
562
|
+
const isMac = typeof navigator !== 'undefined' && /Mac|iPhone|iPad/.test(navigator.platform || navigator.userAgent || '');
|
|
555
563
|
const checklist = _jsx(ValidationChecklist, { issues: shownIssues, title: t('documentEditor.review', { defaultValue: 'Revisa antes de guardar' }) });
|
|
556
|
-
return (_jsxs("div", { className: "flex min-h-0 flex-1 flex-col", "data-slot": "document-editor", "data-kind": kind, "data-layout": fullscreen ? 'fullscreen' : 'dialog',
|
|
564
|
+
return (_jsxs("div", { className: "flex min-h-0 flex-1 flex-col", "data-slot": "document-editor", "data-kind": kind, "data-layout": fullscreen ? 'fullscreen' : 'dialog', onKeyDown: (e) => {
|
|
565
|
+
// ⌘/Ctrl+Enter guarda desde cualquier campo del editor.
|
|
566
|
+
if (e.key === 'Enter' && (e.metaKey || e.ctrlKey) && busy === null) {
|
|
567
|
+
e.preventDefault();
|
|
568
|
+
void submit();
|
|
569
|
+
}
|
|
570
|
+
}, children: [fullscreen ? (_jsxs("div", { className: "-mx-1 min-h-0 flex-1 overflow-y-auto px-1 py-2 lg:grid lg:grid-cols-[minmax(0,1fr)_320px] lg:gap-6", children: [_jsxs("div", { className: "min-w-0 space-y-6", "data-slot": "editor-main", children: [_jsx(FormErrorBanner, { message: formError }), essentials, loadFrom, linesSection, advanced, contributedSections, notes] }), _jsxs("aside", { className: "mt-6 space-y-4 lg:sticky lg:top-0 lg:mt-0 lg:self-start", "data-slot": "editor-aside", children: [_jsxs("div", { className: "space-y-3 rounded-xl border bg-card/40 p-4", children: [totalsEmpty, totalsPanel] }), checklist, previewSection] })] })) : (_jsxs("div", { className: "-mx-1 min-h-0 flex-1 space-y-6 overflow-y-auto px-1 py-2", children: [_jsx(FormErrorBanner, { message: formError }), essentials, loadFrom, linesSection, advanced, contributedSections, notes, previewSection, checklist] })), _jsxs("footer", { className: "flex shrink-0 flex-wrap items-center justify-end gap-2 border-t pt-4", "data-slot": "editor-actions", children: [_jsxs("span", { className: "mr-auto hidden items-center gap-1 text-xs text-muted-foreground sm:inline-flex", "data-slot": "editor-shortcut", children: [_jsx("kbd", { className: "rounded border bg-muted px-1.5 py-0.5 font-mono text-[0.7rem]", children: isMac ? '⌘' : 'Ctrl' }), _jsx("kbd", { className: "rounded border bg-muted px-1.5 py-0.5 font-mono text-[0.7rem]", children: "Enter" }), _jsx("span", { children: t('documentEditor.shortcut_save', { defaultValue: 'para guardar' }) })] }), footerContribs.map(({ id, component: C }) => (_jsx(C, { ...contribProps }, id))), _jsx(Button, { type: "button", variant: "ghost", onClick: onCancel, disabled: busy === 'save', children: t('common.cancel', { defaultValue: 'Cancelar' }) }), _jsxs(Button, { type: "button", "data-primary": "true", onClick: () => void submit(), disabled: busy !== null, children: [busy === 'save' && _jsx(Loader2, { className: "mr-2 size-4 animate-spin", "aria-hidden": true }), type.submit_label ? tl(type.submit_label) : t('common.save', { defaultValue: 'Guardar' })] })] })] }));
|
|
557
571
|
}
|
|
558
572
|
/** Selector del documento origen (mismo widget de relación que el resto de formularios). */
|
|
559
573
|
function SourcePicker({ source, value, seed, header, label, onPick, }) {
|
|
@@ -564,5 +578,5 @@ function SourcePicker({ source, value, seed, header, label, onPick, }) {
|
|
|
564
578
|
ref: source.model,
|
|
565
579
|
option_filter: source.option_filter,
|
|
566
580
|
}), [source, label]);
|
|
567
|
-
return (_jsx("div", { className: "min-w-
|
|
581
|
+
return (_jsx("div", { className: "min-w-60 flex-1", "data-source": source.key, children: renderField(field, value, (v) => onPick(v == null || v === '' ? '' : String(v)), header, seed ? { id: seed.value, label: seed.label } : undefined) }));
|
|
568
582
|
}
|
package/dist/business/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/business/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAA;AACxB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,YAAY,CAAA;AAC1B,cAAc,cAAc,CAAA;AAC5B,cAAc,kBAAkB,CAAA;AAChC,cAAc,qBAAqB,CAAA;AACnC,cAAc,WAAW,CAAA;AACzB,cAAc,mBAAmB,CAAA;AACjC,cAAc,UAAU,CAAA;AACxB,cAAc,sBAAsB,CAAA;AACpC,cAAc,kBAAkB,CAAA;AAChC,cAAc,kBAAkB,CAAA;AAChC,cAAc,mBAAmB,CAAA;AACjC,cAAc,WAAW,CAAA;AACzB,cAAc,UAAU,CAAA;AACxB,cAAc,iBAAiB,CAAA;AAC/B,cAAc,cAAc,CAAA;AAC5B,cAAc,wBAAwB,CAAA;AACtC,cAAc,kBAAkB,CAAA;AAChC,cAAc,oBAAoB,CAAA;AAClC,cAAc,qBAAqB,CAAA;AACnC,cAAc,0BAA0B,CAAA;AACxC,cAAc,yBAAyB,CAAA;AACvC,cAAc,0BAA0B,CAAA;AACxC,cAAc,qBAAqB,CAAA;AACnC,cAAc,mBAAmB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/business/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAA;AACxB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,YAAY,CAAA;AAC1B,cAAc,cAAc,CAAA;AAC5B,cAAc,kBAAkB,CAAA;AAChC,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA;AACnC,cAAc,WAAW,CAAA;AACzB,cAAc,mBAAmB,CAAA;AACjC,cAAc,UAAU,CAAA;AACxB,cAAc,sBAAsB,CAAA;AACpC,cAAc,kBAAkB,CAAA;AAChC,cAAc,kBAAkB,CAAA;AAChC,cAAc,mBAAmB,CAAA;AACjC,cAAc,WAAW,CAAA;AACzB,cAAc,UAAU,CAAA;AACxB,cAAc,iBAAiB,CAAA;AAC/B,cAAc,cAAc,CAAA;AAC5B,cAAc,wBAAwB,CAAA;AACtC,cAAc,kBAAkB,CAAA;AAChC,cAAc,oBAAoB,CAAA;AAClC,cAAc,qBAAqB,CAAA;AACnC,cAAc,0BAA0B,CAAA;AACxC,cAAc,yBAAyB,CAAA;AACvC,cAAc,0BAA0B,CAAA;AACxC,cAAc,qBAAqB,CAAA;AACnC,cAAc,mBAAmB,CAAA"}
|
package/dist/business/index.js
CHANGED
|
@@ -40,10 +40,15 @@ export interface DocumentLinesGridProps {
|
|
|
40
40
|
editPermission?: string;
|
|
41
41
|
/** Moneda ISO (default: la de la org). */
|
|
42
42
|
currency?: string;
|
|
43
|
+
/**
|
|
44
|
+
* Pinta el bloque de totales bajo la tabla. Default true; un contenedor que
|
|
45
|
+
* ya muestra sus propios totales (DocumentEditor) lo apaga.
|
|
46
|
+
*/
|
|
47
|
+
showTotals?: boolean;
|
|
43
48
|
}
|
|
44
49
|
/** @deprecated Usa `DocumentLinesGridProps`. Es el mismo contrato. */
|
|
45
50
|
export type LineItemsEditorProps = DocumentLinesGridProps;
|
|
46
|
-
export declare function DocumentLinesGrid({ value, onChange, columns, policy, serverErrors, onValidate, onRequestProduct, search, priceSource, mode, discountMode, quantityStepper, mergeSameProduct, warehouseId, allowSections, readOnly, editPermission, currency, }: DocumentLinesGridProps): import("react").JSX.Element;
|
|
51
|
+
export declare function DocumentLinesGrid({ value, onChange, columns, policy, serverErrors, onValidate, onRequestProduct, search, priceSource, mode, discountMode, quantityStepper, mergeSameProduct, warehouseId, allowSections, readOnly, editPermission, currency, showTotals, }: DocumentLinesGridProps): import("react").JSX.Element;
|
|
47
52
|
/** Mismo componente. El nombre anterior sigue exportado. */
|
|
48
53
|
export declare const LineItemsEditor: typeof DocumentLinesGrid;
|
|
49
54
|
//# sourceMappingURL=line-items-editor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"line-items-editor.d.ts","sourceRoot":"","sources":["../../src/business/line-items-editor.tsx"],"names":[],"mappings":"AASA,OAAO,
|
|
1
|
+
{"version":3,"file":"line-items-editor.d.ts","sourceRoot":"","sources":["../../src/business/line-items-editor.tsx"],"names":[],"mappings":"AASA,OAAO,EAIH,KAAK,iBAAiB,EACtB,KAAK,WAAW,EACnB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAKH,KAAK,QAAQ,EACb,KAAK,eAAe,EACpB,KAAK,mBAAmB,EAC3B,MAAM,cAAc,CAAA;AAErB,OAAO,EACH,KAAK,YAAY,EACjB,KAAK,aAAa,EAErB,MAAM,kBAAkB,CAAA;AAEzB,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,KAAK,GAAG,YAAY,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,CAAA;AAExF,MAAM,WAAW,sBAAsB;IACnC,KAAK,EAAE,QAAQ,EAAE,CAAA;IACjB,gEAAgE;IAChE,QAAQ,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,IAAI,CAAA;IACrC,6DAA6D;IAC7D,OAAO,CAAC,EAAE,eAAe,EAAE,CAAA;IAC3B,MAAM,CAAC,EAAE,eAAe,CAAA;IACxB,sFAAsF;IACtF,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACrC,uFAAuF;IACvF,UAAU,CAAC,EAAE,CAAC,CAAC,EAAE,mBAAmB,KAAK,IAAI,CAAA;IAC7C,mFAAmF;IACnF,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAA;IAC7B;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,YAAY,EAAE;QAAE,IAAI,EAAE,OAAO,CAAA;KAAE,CAAC,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,aAAa,EAAE,CAAC,CAAA;IAC3G,4FAA4F;IAC5F,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB,mFAAmF;IACnF,IAAI,CAAC,EAAE,iBAAiB,CAAA;IACxB,oFAAoF;IACpF,YAAY,CAAC,EAAE,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAA;IAC5C,6DAA6D;IAC7D,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,+EAA+E;IAC/E,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,4DAA4D;IAC5D,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,gFAAgF;IAChF,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,0CAA0C;IAC1C,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;CACvB;AAED,sEAAsE;AACtE,MAAM,MAAM,oBAAoB,GAAG,sBAAsB,CAAA;AAIzD,wBAAgB,iBAAiB,CAAC,EAC9B,KAAK,EACL,QAAQ,EACR,OAAsB,EACtB,MAAM,EACN,YAAY,EACZ,UAAU,EACV,gBAAgB,EAChB,MAAM,EACN,WAAoB,EACpB,IAAa,EACb,YAAwB,EACxB,eAAe,EACf,gBAAuB,EACvB,WAAW,EACX,aAAoB,EACpB,QAAgB,EAChB,cAAc,EACd,QAAQ,EACR,UAAiB,GACpB,EAAE,sBAAsB,+BAwbxB;AAED,4DAA4D;AAC5D,eAAO,MAAM,eAAe,0BAAoB,CAAA"}
|