@asteby/metacore-runtime-react 24.0.2 → 25.1.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,24 @@
1
1
  # @asteby/metacore-runtime-react
2
2
 
3
+ ## 25.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - a7da67d: feat(errors): los toasts de mutación/acción muestran la causa real del servidor
8
+
9
+ Nuevo helper compartido `toastServerError` / `extractServerError` (`server-error.ts`): en vez de tragarse el `details` y mostrar solo el `message` genérico ("Error creating record"), el toast muestra el `message` como título y el `details`/`errors` de validación como descripción. Cableado en `action-modal-dispatcher` (create/edit/acciones declarativas). Cualquier consumidor puede reusarlo. Así un 500 del kernel (p. ej. un error de Postgres o un guard declarativo) llega legible al usuario/operador.
10
+
11
+ ## 25.0.0
12
+
13
+ ### Minor Changes
14
+
15
+ - 711bdfc: Nuevo flag declarativo `lock_rows` en campos de line-items (`type: "array"`): cuando está activo, el renderer fija las filas — oculta el botón "Agregar renglón" y los botones de borrar por fila, dejando solo editables las celdas de las filas ya presentes. Primitivo genérico del framework (se lee snake_case `lock_rows` con alias camelCase `lockRows`).
16
+
17
+ ### Patch Changes
18
+
19
+ - Updated dependencies [711bdfc]
20
+ - @asteby/metacore-sdk@3.3.0
21
+
3
22
  ## 24.0.2
4
23
 
5
24
  ### Patch Changes
@@ -1 +1 @@
1
- {"version":3,"file":"action-modal-dispatcher.d.ts","sourceRoot":"","sources":["../src/action-modal-dispatcher.tsx"],"names":[],"mappings":"AAwDA,OAAO,EACH,KAAK,cAAc,EACnB,KAAK,gBAAgB,EAExB,MAAM,sBAAsB,CAAA;AAE7B,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,CAAA;AA0FhD,wBAAgB,qBAAqB,CAAC,EAClC,IAAI,EACJ,YAAY,EACZ,MAAM,EACN,KAAK,EACL,MAAM,EACN,QAAQ,EACR,SAAS,GACZ,EAAE,gBAAgB,sCA+DlB"}
1
+ {"version":3,"file":"action-modal-dispatcher.d.ts","sourceRoot":"","sources":["../src/action-modal-dispatcher.tsx"],"names":[],"mappings":"AAyDA,OAAO,EACH,KAAK,cAAc,EACnB,KAAK,gBAAgB,EAExB,MAAM,sBAAsB,CAAA;AAE7B,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,CAAA;AA0FhD,wBAAgB,qBAAqB,CAAC,EAClC,IAAI,EACJ,YAAY,EACZ,MAAM,EACN,KAAK,EACL,MAAM,EACN,QAAQ,EACR,SAAS,GACZ,EAAE,gBAAgB,sCA+DlB"}
@@ -12,6 +12,7 @@ import { useTranslation } from 'react-i18next';
12
12
  import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, Button, Input, Textarea, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Switch, } from '@asteby/metacore-ui/primitives';
13
13
  import { Loader2 } from 'lucide-react';
14
14
  import { toast } from 'sonner';
15
+ import { toastServerError, toastServerSuccess } from './server-error';
15
16
  import { useApi } from './api-context';
16
17
  import { DynamicIcon } from './dynamic-icon';
17
18
  import { DynamicLineItems } from './dynamic-line-items';
@@ -263,16 +264,16 @@ function ConfirmActionDialog({ open, onOpenChange, action, model, record, endpoi
263
264
  const url = buildActionUrl(endpoint, model, record.id, action.key);
264
265
  const res = await api.post(url, {});
265
266
  if (res.data.success) {
266
- toast.success(res.data.message ? t(res.data.message, { defaultValue: res.data.message }) : t('common.success'));
267
+ toastServerSuccess(res.data, { t });
267
268
  onOpenChange(false);
268
269
  onSuccess();
269
270
  }
270
271
  else {
271
- toast.error(res.data.message ? t(res.data.message, { defaultValue: res.data.message }) : t('common.error'));
272
+ toastServerError({ response: { data: res.data } }, { t });
272
273
  }
273
274
  }
274
275
  catch (err) {
275
- toast.error(err?.response?.data?.message || t('common.error'));
276
+ toastServerError(err, { t });
276
277
  }
277
278
  finally {
278
279
  setExecuting(false);
@@ -360,16 +361,16 @@ function GenericActionModal({ open, onOpenChange, action, model, record, endpoin
360
361
  const url = buildActionUrl(endpoint, model, record.id, action.key);
361
362
  const res = await api.post(url, formData);
362
363
  if (res.data.success) {
363
- toast.success(res.data.message ? t(res.data.message, { defaultValue: res.data.message }) : t('common.success'));
364
+ toastServerSuccess(res.data, { t });
364
365
  onOpenChange(false);
365
366
  onSuccess();
366
367
  }
367
368
  else {
368
- toast.error(res.data.message ? t(res.data.message, { defaultValue: res.data.message }) : t('common.error'));
369
+ toastServerError({ response: { data: res.data } }, { t });
369
370
  }
370
371
  }
371
372
  catch (err) {
372
- toast.error(err?.response?.data?.message || t('common.error'));
373
+ toastServerError(err, { t });
373
374
  }
374
375
  finally {
375
376
  setExecuting(false);
@@ -489,16 +490,16 @@ function WizardActionModal({ open, onOpenChange, action, model, record, endpoint
489
490
  const url = buildActionUrl(endpoint, model, record?.id, action.key);
490
491
  const res = await api.post(url, formData);
491
492
  if (res.data.success) {
492
- toast.success(res.data.message ? t(res.data.message, { defaultValue: res.data.message }) : t('common.success'));
493
+ toastServerSuccess(res.data, { t });
493
494
  onOpenChange(false);
494
495
  onSuccess();
495
496
  }
496
497
  else {
497
- toast.error(res.data.message ? t(res.data.message, { defaultValue: res.data.message }) : t('common.error'));
498
+ toastServerError({ response: { data: res.data } }, { t });
498
499
  }
499
500
  }
500
501
  catch (err) {
501
- toast.error(err?.response?.data?.message || t('common.error'));
502
+ toastServerError(err, { t });
502
503
  }
503
504
  finally {
504
505
  setExecuting(false);
@@ -1 +1 @@
1
- {"version":3,"file":"dynamic-line-items.d.ts","sourceRoot":"","sources":["../src/dynamic-line-items.tsx"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAgB7C,MAAM,WAAW,qBAAqB;IAClC,KAAK,EAAE,cAAc,CAAA;IACrB,KAAK,EAAE,GAAG,EAAE,GAAG,SAAS,CAAA;IACxB,QAAQ,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAA;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CACnC;AAkBD,wBAAgB,gBAAgB,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAgB,EAAE,UAAU,EAAE,EAAE,qBAAqB,+BAmJ/G"}
1
+ {"version":3,"file":"dynamic-line-items.d.ts","sourceRoot":"","sources":["../src/dynamic-line-items.tsx"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAgB7C,MAAM,WAAW,qBAAqB;IAClC,KAAK,EAAE,cAAc,CAAA;IACrB,KAAK,EAAE,GAAG,EAAE,GAAG,SAAS,CAAA;IACxB,QAAQ,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAA;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CACnC;AAkBD,wBAAgB,gBAAgB,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAgB,EAAE,UAAU,EAAE,EAAE,qBAAqB,+BA8J/G"}
@@ -29,6 +29,10 @@ function emptyRow(itemFields) {
29
29
  export function DynamicLineItems({ field, value, onChange, disabled = false, formValues }) {
30
30
  const itemFields = getItemFields(field);
31
31
  const rows = Array.isArray(value) ? value : [];
32
+ // `lock_rows` fixes the row set: no add-row button, no per-row delete. Rows
33
+ // stay editable cell-by-cell. Snake_case is what the kernel serves; tolerate
34
+ // the camelCase alias too. Derived once.
35
+ const lockRows = field.lock_rows ?? field.lockRows ?? false;
32
36
  // Columns flagged `total` get a per-column sum in the footer; the balance
33
37
  // rule (if any) reconciles two of them. Both are declarative & generic.
34
38
  const totals = computeLineItemTotals(field, rows);
@@ -60,7 +64,7 @@ export function DynamicLineItems({ field, value, onChange, disabled = false, for
60
64
  updateCell(idx, key, cellValue);
61
65
  };
62
66
  return (_jsxs("div", { className: "grid gap-2", "data-widget": "line_items", children: [_jsx("div", { className: "overflow-x-auto rounded-md border", children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { className: "bg-muted/50", children: _jsxs("tr", { children: [itemFields.map((col) => (_jsxs("th", { className: 'px-3 py-2 font-medium ' +
63
- (isNumericCol(col) ? 'text-right' : 'text-left'), children: [col.label, col.required && _jsx("span", { className: "text-red-500 ml-1", children: "*" })] }, col.key))), _jsx("th", { className: "w-12 px-3 py-2", "aria-label": "acciones" })] }) }), _jsxs("tbody", { children: [rows.length === 0 && (_jsx("tr", { children: _jsx("td", { colSpan: itemFields.length + 1, className: "px-3 py-4 text-center text-muted-foreground", children: "Sin renglones" }) })), rows.map((row, idx) => (_jsxs("tr", { className: "border-t align-top", children: [itemFields.map((col) => (_jsx("td", { className: "px-2 py-1.5", children: _jsx(CellRenderer, { field: col, value: row?.[col.key], onChange: (v) => handleCell(idx, col.key, v), disabled: disabled, formValues: formValues, rowValues: row }) }, col.key))), _jsx("td", { className: "px-2 py-1.5 text-center", children: _jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => removeRow(idx), disabled: disabled, "aria-label": "Eliminar rengl\u00F3n", children: _jsx(Trash2, { className: "h-4 w-4 text-red-500" }) }) })] }, idx)))] }), hasTotals && rows.length > 0 && (_jsx("tfoot", { className: "border-t bg-muted/30", children: _jsxs("tr", { children: [itemFields.map((col, ci) => {
67
+ (isNumericCol(col) ? 'text-right' : 'text-left'), children: [col.label, col.required && _jsx("span", { className: "text-red-500 ml-1", children: "*" })] }, col.key))), !lockRows && _jsx("th", { className: "w-12 px-3 py-2", "aria-label": "acciones" })] }) }), _jsxs("tbody", { children: [rows.length === 0 && (_jsx("tr", { children: _jsx("td", { colSpan: itemFields.length + (lockRows ? 0 : 1), className: "px-3 py-4 text-center text-muted-foreground", children: "Sin renglones" }) })), rows.map((row, idx) => (_jsxs("tr", { className: "border-t align-top", children: [itemFields.map((col) => (_jsx("td", { className: "px-2 py-1.5", children: _jsx(CellRenderer, { field: col, value: row?.[col.key], onChange: (v) => handleCell(idx, col.key, v), disabled: disabled, formValues: formValues, rowValues: row }) }, col.key))), !lockRows && (_jsx("td", { className: "px-2 py-1.5 text-center", children: _jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => removeRow(idx), disabled: disabled, "aria-label": "Eliminar rengl\u00F3n", children: _jsx(Trash2, { className: "h-4 w-4 text-red-500" }) }) }))] }, idx)))] }), hasTotals && rows.length > 0 && (_jsx("tfoot", { className: "border-t bg-muted/30", children: _jsxs("tr", { children: [itemFields.map((col, ci) => {
64
68
  if (ci === 0) {
65
69
  return (_jsx("td", { className: "px-3 py-2 text-left font-medium text-muted-foreground", children: "Totales" }, col.key));
66
70
  }
@@ -68,7 +72,7 @@ export function DynamicLineItems({ field, value, onChange, disabled = false, for
68
72
  (col.total
69
73
  ? 'text-right font-semibold tabular-nums'
70
74
  : ''), children: col.total ? fmtNumber(totals[col.key] ?? 0) : null }, col.key));
71
- }), _jsx("td", {})] }) }))] }) }), _jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: addRow, disabled: disabled, children: [_jsx(Plus, { className: "mr-1 h-4 w-4" }), "Agregar rengl\u00F3n"] }), balance && _jsx(BalanceBadge, { state: balance })] })] }));
75
+ }), !lockRows && _jsx("td", {})] }) }))] }) }), _jsxs("div", { className: "flex items-center justify-between gap-2", children: [lockRows ? (_jsx("span", {})) : (_jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: addRow, disabled: disabled, children: [_jsx(Plus, { className: "mr-1 h-4 w-4" }), "Agregar rengl\u00F3n"] })), balance && _jsx(BalanceBadge, { state: balance })] })] }));
72
76
  }
73
77
  function BalanceBadge({ state, }) {
74
78
  if (state.balanced) {
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from './types';
2
2
  export { LicenseGate, LicenseExpiryBanner, LicenseStatusBadge, isLicenseOperable, isLicenseBlocking, isPresetEntitled, isTrialExpired, type LicenseGateProps, type LicenseExpiryBannerProps, type LicenseStatusBadgeProps, type LicenseState, type LicenseStatus, type LicenseBranding, } from './license';
3
3
  export * from './options-context';
4
+ export { extractServerError, toastServerError, toastServerSuccess, type ExtractedError, type Translate, } from './server-error';
4
5
  export * from './dynamic-table';
5
6
  export { DynamicKanban, type DynamicKanbanProps, deriveStages, groupByStage, isTransitionAllowed, applyOptimisticMove, selectCardColumns, UNASSIGNED_LANE, } from './dynamic-kanban';
6
7
  export { useStageAutomations, StageAutomationsButton, isTagColumn, automationFieldOptions, groupAutomationsByStage, activeAutomationCount, type StageAutomation, type StageAutomationAction, type StageAutomationActionType, type NewStageAutomation, type UseStageAutomationsResult, type StageAutomationsButtonProps, } from './stage-automations';
@@ -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,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,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,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,YAAY,EAAE,wBAAwB,EAAE,QAAQ,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AAC5G,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAA;AACnE,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,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,GACvB,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,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
@@ -6,6 +6,7 @@
6
6
  export * from './types';
7
7
  export { LicenseGate, LicenseExpiryBanner, LicenseStatusBadge, isLicenseOperable, isLicenseBlocking, isPresetEntitled, isTrialExpired, } from './license';
8
8
  export * from './options-context';
9
+ export { extractServerError, toastServerError, toastServerSuccess, } from './server-error';
9
10
  export * from './dynamic-table';
10
11
  export { DynamicKanban, deriveStages, groupByStage, isTransitionAllowed, applyOptimisticMove, selectCardColumns, UNASSIGNED_LANE, } from './dynamic-kanban';
11
12
  export { useStageAutomations, StageAutomationsButton, isTagColumn, automationFieldOptions, groupAutomationsByStage, activeAutomationCount, } from './stage-automations';
@@ -0,0 +1,47 @@
1
+ /** Structured, display-ready view of an error: a headline + an optional cause. */
2
+ export interface ExtractedError {
3
+ /** Primary line — the server's `message` (may be an i18n key) or a fallback. */
4
+ title: string;
5
+ /** Secondary line — the specific cause: `details`, `error`, joined validation
6
+ * `errors`, or a raw network/thrown message. Undefined when nothing more
7
+ * specific than the title is available. */
8
+ description?: string;
9
+ }
10
+ /**
11
+ * Pull the best available headline + cause out of an axios error, a raw
12
+ * `{ success:false, ... }` response body, or a thrown Error/string. Pure — no
13
+ * i18n, no toast — so it is unit-testable and reusable (dialogs, inline errors).
14
+ *
15
+ * Resolution:
16
+ * title ← data.message (else the specific cause, so the toast is never
17
+ * empty; else `fallbackTitle`)
18
+ * description ← data.details → data.error → joined data.errors → raw err.message
19
+ */
20
+ export declare function extractServerError(err: unknown, fallbackTitle: string): ExtractedError;
21
+ /** i18n translator, tolerant of non-keys (returns `defaultValue`/the input). */
22
+ export type Translate = (key: string, opts?: {
23
+ defaultValue?: string;
24
+ }) => string;
25
+ /**
26
+ * Toast a successful mutation/action response, LOCALIZED. The kernel/host often
27
+ * returns a hardcoded English `message` ("Record created successfully"); echoing
28
+ * it verbatim leaks English into a Spanish UI. So we only translate the server
29
+ * message when it is an i18n KEY (dotted, e.g. "pos.rate.created"); a prose
30
+ * message is dropped in favour of the localized `fallbackKey` (default
31
+ * "common.success"). Pass the app's `t`.
32
+ */
33
+ export declare function toastServerSuccess(data: unknown, opts?: {
34
+ t?: Translate;
35
+ fallbackKey?: string;
36
+ }): void;
37
+ /**
38
+ * Toast a server/network error, surfacing the REAL cause as the description
39
+ * instead of a bare generic line. Pass the app's `t` so an i18n-key `message`
40
+ * (e.g. "pos.error.no_rate") is localized; a plain-text message passes through
41
+ * unchanged. Use everywhere a mutation/action can fail.
42
+ */
43
+ export declare function toastServerError(err: unknown, opts?: {
44
+ t?: Translate;
45
+ fallback?: string;
46
+ }): void;
47
+ //# sourceMappingURL=server-error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-error.d.ts","sourceRoot":"","sources":["../src/server-error.ts"],"names":[],"mappings":"AAaA,kFAAkF;AAClF,MAAM,WAAW,cAAc;IAC3B,gFAAgF;IAChF,KAAK,EAAE,MAAM,CAAA;IACb;;gDAE4C;IAC5C,WAAW,CAAC,EAAE,MAAM,CAAA;CACvB;AAiBD;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,GAAG,cAAc,CAuBtF;AAED,gFAAgF;AAChF,MAAM,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;IAAE,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,KAAK,MAAM,CAAA;AAUjF;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAC9B,IAAI,EAAE,OAAO,EACb,IAAI,CAAC,EAAE;IAAE,CAAC,CAAC,EAAE,SAAS,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/C,IAAI,CAUN;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE;IAAE,CAAC,CAAC,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAOhG"}
@@ -0,0 +1,100 @@
1
+ // Shared server/network error surfacing for declarative mutations & actions.
2
+ //
3
+ // Kernel/host handlers answer a failed write with a consistent envelope:
4
+ // { success: false, message: "<headline / i18n key>", details: "<real cause>" }
5
+ // where `details` carries what actually went wrong (a Postgres error, a
6
+ // declarative guard failure, a validation reason). The historical toast pattern
7
+ // toast.error(err?.response?.data?.message || t('common.error'))
8
+ // threw `details` away and showed only the generic headline ("Error creating
9
+ // record") — so a user/operator saw "something failed" with no way to know WHY
10
+ // or report it. This module keeps the headline but ALSO surfaces the cause as
11
+ // the toast description, in ONE place so every call site behaves identically.
12
+ import { toast } from 'sonner';
13
+ /** Flattens a validation `errors` payload (string | string[] | field→msgs map)
14
+ * into a single newline-joined string, or undefined when empty. */
15
+ function joinErrors(errors) {
16
+ if (!errors)
17
+ return undefined;
18
+ if (typeof errors === 'string')
19
+ return errors || undefined;
20
+ if (Array.isArray(errors))
21
+ return errors.filter(Boolean).map(String).join('\n') || undefined;
22
+ if (typeof errors === 'object') {
23
+ const parts = Object.entries(errors).map(([k, v]) => `${k}: ${Array.isArray(v) ? v.join(', ') : String(v)}`);
24
+ return parts.join('\n') || undefined;
25
+ }
26
+ return undefined;
27
+ }
28
+ /**
29
+ * Pull the best available headline + cause out of an axios error, a raw
30
+ * `{ success:false, ... }` response body, or a thrown Error/string. Pure — no
31
+ * i18n, no toast — so it is unit-testable and reusable (dialogs, inline errors).
32
+ *
33
+ * Resolution:
34
+ * title ← data.message (else the specific cause, so the toast is never
35
+ * empty; else `fallbackTitle`)
36
+ * description ← data.details → data.error → joined data.errors → raw err.message
37
+ */
38
+ export function extractServerError(err, fallbackTitle) {
39
+ // Accept either an axios error (`err.response.data`) or a bare response body
40
+ // (`{ success, message, details }`) passed straight in.
41
+ const maybeAxios = err?.response?.data;
42
+ const data = maybeAxios ?? err;
43
+ if (data && typeof data === 'object' && ('message' in data || 'details' in data || 'errors' in data || 'error' in data)) {
44
+ const d = data;
45
+ const message = typeof d.message === 'string' ? d.message.trim() : '';
46
+ const details = (typeof d.details === 'string' && d.details.trim()) ||
47
+ (typeof d.error === 'string' && d.error.trim()) ||
48
+ joinErrors(d.errors) ||
49
+ '';
50
+ if (message && details && message !== details)
51
+ return { title: message, description: details };
52
+ if (message || details)
53
+ return { title: message || details };
54
+ }
55
+ // Non-HTTP failure (network down, CORS, a thrown string/Error): show it as
56
+ // the cause under the generic fallback headline.
57
+ const raw = typeof err === 'string' ? err : err?.message;
58
+ if (typeof raw === 'string' && raw.trim())
59
+ return { title: fallbackTitle, description: raw.trim() };
60
+ return { title: fallbackTitle };
61
+ }
62
+ /** A dotted, space-free token (e.g. "pos.rate.created") — the shape of an i18n
63
+ * key, as opposed to human prose ("Record created successfully"). Used to
64
+ * decide whether a server-sent message is safe to translate or should be
65
+ * replaced by a localized fallback. */
66
+ function looksLikeI18nKey(s) {
67
+ return /^[a-z0-9_-]+(\.[a-z0-9_-]+)+$/i.test(s);
68
+ }
69
+ /**
70
+ * Toast a successful mutation/action response, LOCALIZED. The kernel/host often
71
+ * returns a hardcoded English `message` ("Record created successfully"); echoing
72
+ * it verbatim leaks English into a Spanish UI. So we only translate the server
73
+ * message when it is an i18n KEY (dotted, e.g. "pos.rate.created"); a prose
74
+ * message is dropped in favour of the localized `fallbackKey` (default
75
+ * "common.success"). Pass the app's `t`.
76
+ */
77
+ export function toastServerSuccess(data, opts) {
78
+ const t = opts?.t;
79
+ const fallbackKey = opts?.fallbackKey ?? 'common.success';
80
+ const fallback = t ? t(fallbackKey, { defaultValue: 'Success' }) : 'Success';
81
+ const msg = data?.message;
82
+ if (t && typeof msg === 'string' && msg && looksLikeI18nKey(msg)) {
83
+ toast.success(t(msg, { defaultValue: fallback }));
84
+ return;
85
+ }
86
+ toast.success(fallback);
87
+ }
88
+ /**
89
+ * Toast a server/network error, surfacing the REAL cause as the description
90
+ * instead of a bare generic line. Pass the app's `t` so an i18n-key `message`
91
+ * (e.g. "pos.error.no_rate") is localized; a plain-text message passes through
92
+ * unchanged. Use everywhere a mutation/action can fail.
93
+ */
94
+ export function toastServerError(err, opts) {
95
+ const t = opts?.t;
96
+ const fallback = opts?.fallback ?? (t ? t('common.error', { defaultValue: 'Something went wrong' }) : 'Something went wrong');
97
+ const { title, description } = extractServerError(err, fallback);
98
+ const shownTitle = t ? t(title, { defaultValue: title }) : title;
99
+ toast.error(shownTitle, description ? { description } : undefined);
100
+ }
package/dist/types.d.ts CHANGED
@@ -393,6 +393,16 @@ export interface ActionFieldDef {
393
393
  * reconcile. Mirrors kernel v3 `ActionField.balance`.
394
394
  */
395
395
  balance?: FieldBalanceRule;
396
+ /**
397
+ * On a line-items (`type: "array"`) field: fixes the set of rows. When true
398
+ * the SDK hides the "Agregar renglón" button and the per-row delete control,
399
+ * so the rows served (e.g. a prefilled set of allocations) can only have
400
+ * their cells edited — never added to or removed. Generic & domain-agnostic.
401
+ * Mirrors kernel v3 `ActionField.lock_rows`. Tolerates the camelCase alias.
402
+ */
403
+ lock_rows?: boolean;
404
+ /** camelCase alias for `lock_rows`. */
405
+ lockRows?: boolean;
396
406
  /**
397
407
  * `upload` widget: comma-separated accept list forwarded to the file input
398
408
  * `accept` attribute (e.g. `"image/*,.pdf"`). Tolerates the snake_case the
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@asteby/metacore-runtime-react",
3
- "version": "24.0.2",
3
+ "version": "25.1.0",
4
4
  "description": "React runtime for metacore hosts — renders addon contributions dynamically",
5
5
  "repository": {
6
6
  "type": "git",
@@ -37,7 +37,7 @@
37
37
  "react-i18next": ">=13",
38
38
  "sonner": ">=1.7",
39
39
  "zustand": ">=5",
40
- "@asteby/metacore-sdk": "^3.2.1",
40
+ "@asteby/metacore-sdk": "^3.3.0",
41
41
  "@asteby/metacore-ui": "^2.10.1"
42
42
  },
43
43
  "peerDependenciesMeta": {
@@ -67,7 +67,7 @@
67
67
  "typescript": "^6.0.0",
68
68
  "vitest": "^4.0.0",
69
69
  "zustand": "^5.0.0",
70
- "@asteby/metacore-sdk": "3.2.1",
70
+ "@asteby/metacore-sdk": "3.3.0",
71
71
  "@asteby/metacore-ui": "2.10.1"
72
72
  },
73
73
  "scripts": {
@@ -0,0 +1,70 @@
1
+ // @vitest-environment happy-dom
2
+ //
3
+ // `lock_rows` on a line-items (type "array") field fixes the row set: the SDK
4
+ // hides the "Agregar renglón" button and every per-row delete control, so the
5
+ // served rows can only have their cells edited — never added to or removed.
6
+ // Generic framework primitive (mirrors kernel v3 `ActionField.lock_rows`).
7
+ //
8
+ // Cells here are plain text/number inputs on purpose: no Radix Select, so the
9
+ // happy-dom BubbleSelect value-reset gotcha does not apply.
10
+ import { afterEach, describe, expect, it, vi } from 'vitest'
11
+ import { cleanup, fireEvent, render, screen } from '@testing-library/react'
12
+ import { DynamicLineItems } from '../dynamic-line-items'
13
+ import type { ActionFieldDef } from '../types'
14
+
15
+ afterEach(cleanup)
16
+
17
+ const lineField = (overrides: Partial<ActionFieldDef> = {}): ActionFieldDef => ({
18
+ key: 'allocations',
19
+ label: 'Renglones',
20
+ type: 'array',
21
+ itemFields: [
22
+ { key: 'sku', label: 'SKU', type: 'text' },
23
+ { key: 'qty', label: 'Cantidad', type: 'number' },
24
+ ],
25
+ ...overrides,
26
+ })
27
+
28
+ const rows = [
29
+ { sku: 'A-1', qty: 2 },
30
+ { sku: 'B-2', qty: 5 },
31
+ ]
32
+
33
+ describe('DynamicLineItems — lock_rows', () => {
34
+ it('con lock_rows=true oculta "Agregar renglón" y los botones de borrar, pero deja editar celdas', () => {
35
+ const onChange = vi.fn()
36
+ render(
37
+ <DynamicLineItems field={lineField({ lock_rows: true })} value={rows} onChange={onChange} />,
38
+ )
39
+
40
+ // No add-row control, no per-row delete controls.
41
+ expect(screen.queryByText('Agregar renglón')).toBeNull()
42
+ expect(screen.queryByLabelText('Eliminar renglón')).toBeNull()
43
+
44
+ // Cells stay editable: typing into a cell fires onChange with the row updated.
45
+ const skuInputs = screen.getAllByDisplayValue('A-1')
46
+ fireEvent.change(skuInputs[0], { target: { value: 'A-9' } })
47
+ expect(onChange).toHaveBeenCalledWith([
48
+ { sku: 'A-9', qty: 2 },
49
+ { sku: 'B-2', qty: 5 },
50
+ ])
51
+ })
52
+
53
+ it('acepta el alias camelCase lockRows', () => {
54
+ render(
55
+ <DynamicLineItems
56
+ field={lineField({ lockRows: true })}
57
+ value={rows}
58
+ onChange={vi.fn()}
59
+ />,
60
+ )
61
+ expect(screen.queryByText('Agregar renglón')).toBeNull()
62
+ expect(screen.queryByLabelText('Eliminar renglón')).toBeNull()
63
+ })
64
+
65
+ it('control: sin lock_rows muestra "Agregar renglón" y un botón de borrar por fila', () => {
66
+ render(<DynamicLineItems field={lineField()} value={rows} onChange={vi.fn()} />)
67
+ expect(screen.getByText('Agregar renglón')).toBeTruthy()
68
+ expect(screen.getAllByLabelText('Eliminar renglón')).toHaveLength(2)
69
+ })
70
+ })
@@ -0,0 +1,43 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { extractServerError } from '../server-error'
3
+
4
+ describe('extractServerError', () => {
5
+ it('surfaces details as description under message headline', () => {
6
+ const err = { response: { data: { success: false, message: 'Error creating record', details: 'ERROR: column "x" does not exist (SQLSTATE 42703)' } } }
7
+ expect(extractServerError(err, 'fallback')).toEqual({ title: 'Error creating record', description: 'ERROR: column "x" does not exist (SQLSTATE 42703)' })
8
+ })
9
+ it('accepts a bare response body', () => {
10
+ expect(extractServerError({ success: false, message: 'm', details: 'd' }, 'fb')).toEqual({ title: 'm', description: 'd' })
11
+ })
12
+ it('uses details as title when no message', () => {
13
+ expect(extractServerError({ response: { data: { details: 'boom' } } }, 'fb')).toEqual({ title: 'boom' })
14
+ })
15
+ it('joins validation errors object', () => {
16
+ const r = extractServerError({ response: { data: { message: 'Validation error', errors: { name: ['required'], sku: 'dup' } } } }, 'fb')
17
+ expect(r.title).toBe('Validation error'); expect(r.description).toContain('name: required'); expect(r.description).toContain('sku: dup')
18
+ })
19
+ it('falls back for a thrown string / network error', () => {
20
+ expect(extractServerError('Network Error', 'fb')).toEqual({ title: 'fb', description: 'Network Error' })
21
+ expect(extractServerError({}, 'fb')).toEqual({ title: 'fb' })
22
+ })
23
+ it('does not duplicate when message equals details', () => {
24
+ expect(extractServerError({ response: { data: { message: 'same', details: 'same' } } }, 'fb')).toEqual({ title: 'same' })
25
+ })
26
+ })
27
+
28
+ import { vi } from 'vitest'
29
+ import { toastServerSuccess } from '../server-error'
30
+ vi.mock('sonner', () => ({ toast: { success: vi.fn(), error: vi.fn() } }))
31
+ import { toast } from 'sonner'
32
+ describe('toastServerSuccess', () => {
33
+ const t = (k: string, o?: { defaultValue?: string }) => (k === 'common.success' ? 'Registro creado' : (o?.defaultValue ?? k))
34
+ it('drops English prose in favour of localized fallback', () => {
35
+ toastServerSuccess({ message: 'Record created successfully' }, { t })
36
+ expect((toast.success as any).mock.calls.at(-1)[0]).toBe('Registro creado')
37
+ })
38
+ it('translates a dotted i18n key', () => {
39
+ const t2 = (k: string, o?: { defaultValue?: string }) => (k === 'pos.rate.created' ? 'Tasa creada' : (k === 'common.success' ? 'Registro creado' : (o?.defaultValue ?? k)))
40
+ toastServerSuccess({ message: 'pos.rate.created' }, { t: t2 })
41
+ expect((toast.success as any).mock.calls.at(-1)[0]).toBe('Tasa creada')
42
+ })
43
+ })
@@ -35,6 +35,7 @@ import {
35
35
  } from '@asteby/metacore-ui/primitives'
36
36
  import { Loader2 } from 'lucide-react'
37
37
  import { toast } from 'sonner'
38
+ import { toastServerError, toastServerSuccess } from './server-error'
38
39
  import { useApi } from './api-context'
39
40
  import { DynamicIcon } from './dynamic-icon'
40
41
  import { DynamicLineItems } from './dynamic-line-items'
@@ -417,14 +418,14 @@ function ConfirmActionDialog({ open, onOpenChange, action, model, record, endpoi
417
418
  const url = buildActionUrl(endpoint, model, record.id, action.key)
418
419
  const res = await api.post(url, {})
419
420
  if (res.data.success) {
420
- toast.success(res.data.message ? t(res.data.message, { defaultValue: res.data.message }) : t('common.success'))
421
+ toastServerSuccess(res.data, { t })
421
422
  onOpenChange(false)
422
423
  onSuccess()
423
424
  } else {
424
- toast.error(res.data.message ? t(res.data.message, { defaultValue: res.data.message }) : t('common.error'))
425
+ toastServerError({ response: { data: res.data } }, { t })
425
426
  }
426
427
  } catch (err: any) {
427
- toast.error(err?.response?.data?.message || t('common.error'))
428
+ toastServerError(err, { t })
428
429
  } finally {
429
430
  setExecuting(false)
430
431
  }
@@ -539,14 +540,14 @@ function GenericActionModal({ open, onOpenChange, action, model, record, endpoin
539
540
  const url = buildActionUrl(endpoint, model, record.id, action.key)
540
541
  const res = await api.post(url, formData)
541
542
  if (res.data.success) {
542
- toast.success(res.data.message ? t(res.data.message, { defaultValue: res.data.message }) : t('common.success'))
543
+ toastServerSuccess(res.data, { t })
543
544
  onOpenChange(false)
544
545
  onSuccess()
545
546
  } else {
546
- toast.error(res.data.message ? t(res.data.message, { defaultValue: res.data.message }) : t('common.error'))
547
+ toastServerError({ response: { data: res.data } }, { t })
547
548
  }
548
549
  } catch (err: any) {
549
- toast.error(err?.response?.data?.message || t('common.error'))
550
+ toastServerError(err, { t })
550
551
  } finally {
551
552
  setExecuting(false)
552
553
  }
@@ -744,14 +745,14 @@ function WizardActionModal({ open, onOpenChange, action, model, record, endpoint
744
745
  const url = buildActionUrl(endpoint, model, record?.id, action.key)
745
746
  const res = await api.post(url, formData)
746
747
  if (res.data.success) {
747
- toast.success(res.data.message ? t(res.data.message, { defaultValue: res.data.message }) : t('common.success'))
748
+ toastServerSuccess(res.data, { t })
748
749
  onOpenChange(false)
749
750
  onSuccess()
750
751
  } else {
751
- toast.error(res.data.message ? t(res.data.message, { defaultValue: res.data.message }) : t('common.error'))
752
+ toastServerError({ response: { data: res.data } }, { t })
752
753
  }
753
754
  } catch (err: any) {
754
- toast.error(err?.response?.data?.message || t('common.error'))
755
+ toastServerError(err, { t })
755
756
  } finally {
756
757
  setExecuting(false)
757
758
  }
@@ -69,6 +69,11 @@ export function DynamicLineItems({ field, value, onChange, disabled = false, for
69
69
  const itemFields = getItemFields(field)
70
70
  const rows: any[] = Array.isArray(value) ? value : []
71
71
 
72
+ // `lock_rows` fixes the row set: no add-row button, no per-row delete. Rows
73
+ // stay editable cell-by-cell. Snake_case is what the kernel serves; tolerate
74
+ // the camelCase alias too. Derived once.
75
+ const lockRows = field.lock_rows ?? (field as any).lockRows ?? false
76
+
72
77
  // Columns flagged `total` get a per-column sum in the footer; the balance
73
78
  // rule (if any) reconciles two of them. Both are declarative & generic.
74
79
  const totals = computeLineItemTotals(field, rows)
@@ -126,14 +131,14 @@ export function DynamicLineItems({ field, value, onChange, disabled = false, for
126
131
  {col.required && <span className="text-red-500 ml-1">*</span>}
127
132
  </th>
128
133
  ))}
129
- <th className="w-12 px-3 py-2" aria-label="acciones" />
134
+ {!lockRows && <th className="w-12 px-3 py-2" aria-label="acciones" />}
130
135
  </tr>
131
136
  </thead>
132
137
  <tbody>
133
138
  {rows.length === 0 && (
134
139
  <tr>
135
140
  <td
136
- colSpan={itemFields.length + 1}
141
+ colSpan={itemFields.length + (lockRows ? 0 : 1)}
137
142
  className="px-3 py-4 text-center text-muted-foreground"
138
143
  >
139
144
  Sin renglones
@@ -154,18 +159,20 @@ export function DynamicLineItems({ field, value, onChange, disabled = false, for
154
159
  />
155
160
  </td>
156
161
  ))}
157
- <td className="px-2 py-1.5 text-center">
158
- <Button
159
- type="button"
160
- variant="ghost"
161
- size="icon"
162
- onClick={() => removeRow(idx)}
163
- disabled={disabled}
164
- aria-label="Eliminar renglón"
165
- >
166
- <Trash2 className="h-4 w-4 text-red-500" />
167
- </Button>
168
- </td>
162
+ {!lockRows && (
163
+ <td className="px-2 py-1.5 text-center">
164
+ <Button
165
+ type="button"
166
+ variant="ghost"
167
+ size="icon"
168
+ onClick={() => removeRow(idx)}
169
+ disabled={disabled}
170
+ aria-label="Eliminar renglón"
171
+ >
172
+ <Trash2 className="h-4 w-4 text-red-500" />
173
+ </Button>
174
+ </td>
175
+ )}
169
176
  </tr>
170
177
  ))}
171
178
  </tbody>
@@ -197,17 +204,21 @@ export function DynamicLineItems({ field, value, onChange, disabled = false, for
197
204
  </td>
198
205
  )
199
206
  })}
200
- <td />
207
+ {!lockRows && <td />}
201
208
  </tr>
202
209
  </tfoot>
203
210
  )}
204
211
  </table>
205
212
  </div>
206
213
  <div className="flex items-center justify-between gap-2">
207
- <Button type="button" variant="outline" size="sm" onClick={addRow} disabled={disabled}>
208
- <Plus className="mr-1 h-4 w-4" />
209
- Agregar renglón
210
- </Button>
214
+ {lockRows ? (
215
+ <span />
216
+ ) : (
217
+ <Button type="button" variant="outline" size="sm" onClick={addRow} disabled={disabled}>
218
+ <Plus className="mr-1 h-4 w-4" />
219
+ Agregar renglón
220
+ </Button>
221
+ )}
211
222
  {balance && <BalanceBadge state={balance} />}
212
223
  </div>
213
224
  </div>
package/src/index.ts CHANGED
@@ -20,6 +20,13 @@ export {
20
20
  type LicenseBranding,
21
21
  } from './license'
22
22
  export * from './options-context'
23
+ export {
24
+ extractServerError,
25
+ toastServerError,
26
+ toastServerSuccess,
27
+ type ExtractedError,
28
+ type Translate,
29
+ } from './server-error'
23
30
  export * from './dynamic-table'
24
31
  export {
25
32
  DynamicKanban,
@@ -0,0 +1,121 @@
1
+ // Shared server/network error surfacing for declarative mutations & actions.
2
+ //
3
+ // Kernel/host handlers answer a failed write with a consistent envelope:
4
+ // { success: false, message: "<headline / i18n key>", details: "<real cause>" }
5
+ // where `details` carries what actually went wrong (a Postgres error, a
6
+ // declarative guard failure, a validation reason). The historical toast pattern
7
+ // toast.error(err?.response?.data?.message || t('common.error'))
8
+ // threw `details` away and showed only the generic headline ("Error creating
9
+ // record") — so a user/operator saw "something failed" with no way to know WHY
10
+ // or report it. This module keeps the headline but ALSO surfaces the cause as
11
+ // the toast description, in ONE place so every call site behaves identically.
12
+ import { toast } from 'sonner'
13
+
14
+ /** Structured, display-ready view of an error: a headline + an optional cause. */
15
+ export interface ExtractedError {
16
+ /** Primary line — the server's `message` (may be an i18n key) or a fallback. */
17
+ title: string
18
+ /** Secondary line — the specific cause: `details`, `error`, joined validation
19
+ * `errors`, or a raw network/thrown message. Undefined when nothing more
20
+ * specific than the title is available. */
21
+ description?: string
22
+ }
23
+
24
+ /** Flattens a validation `errors` payload (string | string[] | field→msgs map)
25
+ * into a single newline-joined string, or undefined when empty. */
26
+ function joinErrors(errors: unknown): string | undefined {
27
+ if (!errors) return undefined
28
+ if (typeof errors === 'string') return errors || undefined
29
+ if (Array.isArray(errors)) return errors.filter(Boolean).map(String).join('\n') || undefined
30
+ if (typeof errors === 'object') {
31
+ const parts = Object.entries(errors as Record<string, unknown>).map(
32
+ ([k, v]) => `${k}: ${Array.isArray(v) ? v.join(', ') : String(v)}`,
33
+ )
34
+ return parts.join('\n') || undefined
35
+ }
36
+ return undefined
37
+ }
38
+
39
+ /**
40
+ * Pull the best available headline + cause out of an axios error, a raw
41
+ * `{ success:false, ... }` response body, or a thrown Error/string. Pure — no
42
+ * i18n, no toast — so it is unit-testable and reusable (dialogs, inline errors).
43
+ *
44
+ * Resolution:
45
+ * title ← data.message (else the specific cause, so the toast is never
46
+ * empty; else `fallbackTitle`)
47
+ * description ← data.details → data.error → joined data.errors → raw err.message
48
+ */
49
+ export function extractServerError(err: unknown, fallbackTitle: string): ExtractedError {
50
+ // Accept either an axios error (`err.response.data`) or a bare response body
51
+ // (`{ success, message, details }`) passed straight in.
52
+ const maybeAxios = (err as { response?: { data?: unknown } } | undefined)?.response?.data
53
+ const data = maybeAxios ?? err
54
+
55
+ if (data && typeof data === 'object' && ('message' in data || 'details' in data || 'errors' in data || 'error' in data)) {
56
+ const d = data as { message?: unknown; details?: unknown; error?: unknown; errors?: unknown }
57
+ const message = typeof d.message === 'string' ? d.message.trim() : ''
58
+ const details =
59
+ (typeof d.details === 'string' && d.details.trim()) ||
60
+ (typeof d.error === 'string' && d.error.trim()) ||
61
+ joinErrors(d.errors) ||
62
+ ''
63
+ if (message && details && message !== details) return { title: message, description: details }
64
+ if (message || details) return { title: message || details }
65
+ }
66
+
67
+ // Non-HTTP failure (network down, CORS, a thrown string/Error): show it as
68
+ // the cause under the generic fallback headline.
69
+ const raw = typeof err === 'string' ? err : (err as { message?: unknown } | undefined)?.message
70
+ if (typeof raw === 'string' && raw.trim()) return { title: fallbackTitle, description: raw.trim() }
71
+ return { title: fallbackTitle }
72
+ }
73
+
74
+ /** i18n translator, tolerant of non-keys (returns `defaultValue`/the input). */
75
+ export type Translate = (key: string, opts?: { defaultValue?: string }) => string
76
+
77
+ /** A dotted, space-free token (e.g. "pos.rate.created") — the shape of an i18n
78
+ * key, as opposed to human prose ("Record created successfully"). Used to
79
+ * decide whether a server-sent message is safe to translate or should be
80
+ * replaced by a localized fallback. */
81
+ function looksLikeI18nKey(s: string): boolean {
82
+ return /^[a-z0-9_-]+(\.[a-z0-9_-]+)+$/i.test(s)
83
+ }
84
+
85
+ /**
86
+ * Toast a successful mutation/action response, LOCALIZED. The kernel/host often
87
+ * returns a hardcoded English `message` ("Record created successfully"); echoing
88
+ * it verbatim leaks English into a Spanish UI. So we only translate the server
89
+ * message when it is an i18n KEY (dotted, e.g. "pos.rate.created"); a prose
90
+ * message is dropped in favour of the localized `fallbackKey` (default
91
+ * "common.success"). Pass the app's `t`.
92
+ */
93
+ export function toastServerSuccess(
94
+ data: unknown,
95
+ opts?: { t?: Translate; fallbackKey?: string },
96
+ ): void {
97
+ const t = opts?.t
98
+ const fallbackKey = opts?.fallbackKey ?? 'common.success'
99
+ const fallback = t ? t(fallbackKey, { defaultValue: 'Success' }) : 'Success'
100
+ const msg = (data as { message?: unknown } | undefined)?.message
101
+ if (t && typeof msg === 'string' && msg && looksLikeI18nKey(msg)) {
102
+ toast.success(t(msg, { defaultValue: fallback }))
103
+ return
104
+ }
105
+ toast.success(fallback)
106
+ }
107
+
108
+ /**
109
+ * Toast a server/network error, surfacing the REAL cause as the description
110
+ * instead of a bare generic line. Pass the app's `t` so an i18n-key `message`
111
+ * (e.g. "pos.error.no_rate") is localized; a plain-text message passes through
112
+ * unchanged. Use everywhere a mutation/action can fail.
113
+ */
114
+ export function toastServerError(err: unknown, opts?: { t?: Translate; fallback?: string }): void {
115
+ const t = opts?.t
116
+ const fallback =
117
+ opts?.fallback ?? (t ? t('common.error', { defaultValue: 'Something went wrong' }) : 'Something went wrong')
118
+ const { title, description } = extractServerError(err, fallback)
119
+ const shownTitle = t ? t(title, { defaultValue: title }) : title
120
+ toast.error(shownTitle, description ? { description } : undefined)
121
+ }
package/src/types.ts CHANGED
@@ -443,6 +443,16 @@ export interface ActionFieldDef {
443
443
  * reconcile. Mirrors kernel v3 `ActionField.balance`.
444
444
  */
445
445
  balance?: FieldBalanceRule
446
+ /**
447
+ * On a line-items (`type: "array"`) field: fixes the set of rows. When true
448
+ * the SDK hides the "Agregar renglón" button and the per-row delete control,
449
+ * so the rows served (e.g. a prefilled set of allocations) can only have
450
+ * their cells edited — never added to or removed. Generic & domain-agnostic.
451
+ * Mirrors kernel v3 `ActionField.lock_rows`. Tolerates the camelCase alias.
452
+ */
453
+ lock_rows?: boolean
454
+ /** camelCase alias for `lock_rows`. */
455
+ lockRows?: boolean
446
456
  /**
447
457
  * `upload` widget: comma-separated accept list forwarded to the file input
448
458
  * `accept` attribute (e.g. `"image/*,.pdf"`). Tolerates the snake_case the