@asteby/metacore-runtime-react 46.4.0 → 47.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.
Files changed (123) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/dist/action-modal-dispatcher.d.ts +10 -1
  3. package/dist/action-modal-dispatcher.d.ts.map +1 -1
  4. package/dist/action-modal-dispatcher.js +107 -19
  5. package/dist/audit-info.d.ts +54 -0
  6. package/dist/audit-info.d.ts.map +1 -0
  7. package/dist/audit-info.js +142 -0
  8. package/dist/business/catalog.d.ts.map +1 -1
  9. package/dist/business/catalog.js +24 -0
  10. package/dist/business/index.d.ts +3 -0
  11. package/dist/business/index.d.ts.map +1 -1
  12. package/dist/business/index.js +3 -0
  13. package/dist/business/print-send-dialog.d.ts +27 -0
  14. package/dist/business/print-send-dialog.d.ts.map +1 -0
  15. package/dist/business/print-send-dialog.js +55 -0
  16. package/dist/business/relate-documents.d.ts +41 -0
  17. package/dist/business/relate-documents.d.ts.map +1 -0
  18. package/dist/business/relate-documents.js +42 -0
  19. package/dist/business/vehicle-picker.d.ts +36 -0
  20. package/dist/business/vehicle-picker.d.ts.map +1 -0
  21. package/dist/business/vehicle-picker.js +61 -0
  22. package/dist/cfdi-stamp-panel.d.ts +29 -0
  23. package/dist/cfdi-stamp-panel.d.ts.map +1 -0
  24. package/dist/cfdi-stamp-panel.js +53 -0
  25. package/dist/dialogs/dynamic-record.d.ts.map +1 -1
  26. package/dist/dialogs/dynamic-record.js +10 -1
  27. package/dist/document-form-dialog.d.ts +23 -0
  28. package/dist/document-form-dialog.d.ts.map +1 -0
  29. package/dist/document-form-dialog.js +196 -0
  30. package/dist/dynamic-crud-page.d.ts +6 -0
  31. package/dist/dynamic-crud-page.d.ts.map +1 -1
  32. package/dist/dynamic-crud-page.js +4 -2
  33. package/dist/dynamic-form.d.ts.map +1 -1
  34. package/dist/dynamic-form.js +3 -0
  35. package/dist/dynamic-kanban.d.ts.map +1 -1
  36. package/dist/dynamic-kanban.js +11 -2
  37. package/dist/dynamic-line-items.d.ts.map +1 -1
  38. package/dist/dynamic-line-items.js +3 -0
  39. package/dist/dynamic-multi-select-field.d.ts.map +1 -1
  40. package/dist/dynamic-multi-select-field.js +2 -0
  41. package/dist/dynamic-select-field.d.ts.map +1 -1
  42. package/dist/dynamic-select-field.js +4 -0
  43. package/dist/dynamic-table.d.ts.map +1 -1
  44. package/dist/dynamic-table.js +6 -2
  45. package/dist/index.d.ts +8 -0
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +9 -0
  48. package/dist/nav-permissions.d.ts +19 -0
  49. package/dist/nav-permissions.d.ts.map +1 -0
  50. package/dist/nav-permissions.js +51 -0
  51. package/dist/navigation-builder.d.ts +6 -0
  52. package/dist/navigation-builder.d.ts.map +1 -1
  53. package/dist/navigation-builder.js +26 -1
  54. package/dist/option-filter.d.ts +31 -0
  55. package/dist/option-filter.d.ts.map +1 -0
  56. package/dist/option-filter.js +49 -0
  57. package/dist/permissions-manager.d.ts +9 -0
  58. package/dist/permissions-manager.d.ts.map +1 -1
  59. package/dist/permissions-manager.js +50 -17
  60. package/dist/print-document-button.d.ts.map +1 -1
  61. package/dist/print-document-button.js +9 -1
  62. package/dist/record-mutation-events.d.ts +14 -0
  63. package/dist/record-mutation-events.d.ts.map +1 -0
  64. package/dist/record-mutation-events.js +37 -0
  65. package/dist/types.d.ts +86 -0
  66. package/dist/types.d.ts.map +1 -1
  67. package/dist/use-options-resolver.d.ts +8 -0
  68. package/dist/use-options-resolver.d.ts.map +1 -1
  69. package/dist/use-options-resolver.js +8 -2
  70. package/dist/use-print-document.d.ts +15 -1
  71. package/dist/use-print-document.d.ts.map +1 -1
  72. package/dist/use-print-document.js +52 -1
  73. package/dist/use-record-mutation-tick.d.ts +6 -0
  74. package/dist/use-record-mutation-tick.d.ts.map +1 -0
  75. package/dist/use-record-mutation-tick.js +11 -0
  76. package/package.json +3 -3
  77. package/src/__tests__/action-modal-feedback.test.tsx +116 -0
  78. package/src/__tests__/action-modal-grid-layout.test.tsx +1 -1
  79. package/src/__tests__/apply-sidebar-layout.test.ts +1 -1
  80. package/src/__tests__/audit-info.test.tsx +103 -0
  81. package/src/__tests__/business-pickers.test.tsx +157 -0
  82. package/src/__tests__/dependent-options.test.tsx +12 -5
  83. package/src/__tests__/document-form-dialog.test.tsx +190 -0
  84. package/src/__tests__/dynamic-table-infinite-scroll.test.tsx +4 -0
  85. package/src/__tests__/dynamic-table-mutation-refresh.test.tsx +116 -0
  86. package/src/__tests__/line-items-lock-rows.test.tsx +2 -1
  87. package/src/__tests__/nav-permissions.test.tsx +76 -0
  88. package/src/__tests__/option-filter.test.tsx +115 -0
  89. package/src/__tests__/permissions-manager.test.tsx +21 -4
  90. package/src/__tests__/structured-view-value.test.tsx +5 -1
  91. package/src/__tests__/use-print-document-feedback.test.tsx +58 -0
  92. package/src/__tests__/wizard-action-modal.test.tsx +1 -1
  93. package/src/action-modal-dispatcher.tsx +124 -6
  94. package/src/audit-info.tsx +219 -0
  95. package/src/business/catalog.ts +24 -0
  96. package/src/business/index.ts +3 -0
  97. package/src/business/print-send-dialog.tsx +175 -0
  98. package/src/business/relate-documents.tsx +182 -0
  99. package/src/business/vehicle-picker.tsx +188 -0
  100. package/src/cfdi-stamp-panel.tsx +146 -0
  101. package/src/dialogs/dynamic-record.tsx +18 -0
  102. package/src/document-form-dialog.tsx +357 -0
  103. package/src/dropdown-menu-entry.test.ts +23 -0
  104. package/src/dynamic-crud-page.tsx +21 -2
  105. package/src/dynamic-form.tsx +3 -0
  106. package/src/dynamic-kanban.tsx +10 -4
  107. package/src/dynamic-line-items.tsx +3 -0
  108. package/src/dynamic-multi-select-field.tsx +2 -0
  109. package/src/dynamic-select-field.tsx +4 -0
  110. package/src/dynamic-table.tsx +6 -2
  111. package/src/index.ts +41 -0
  112. package/src/nav-permissions.ts +58 -0
  113. package/src/navigation-builder.tsx +27 -1
  114. package/src/option-filter.ts +88 -0
  115. package/src/permissions-manager.tsx +39 -26
  116. package/src/print-document-button.tsx +9 -1
  117. package/src/record-mutation-events.ts +50 -0
  118. package/src/types.ts +90 -0
  119. package/src/use-options-resolver.ts +19 -1
  120. package/src/use-print-document.ts +66 -3
  121. package/src/use-record-mutation-tick.ts +12 -0
  122. package/vitest.config.ts +1 -0
  123. package/vitest.setup.ts +7 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,43 @@
1
1
  # @asteby/metacore-runtime-react
2
2
 
3
+ ## 47.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 1614e02: `TableMetadata.audit` (columnas de auditoría que sirve el kernel >= v0.188) y nuevo `<AuditInfo>`: «Creado por X · fecha», «Modificado por Y · fecha» y «Eliminado por Z · fecha», colapsado al pie del detalle/edición de `DynamicRecordDialog`. El nombre sale del host (`resolveActor`), del hermano expandido del registro (`created_by`) o, si no, un id corto; el actor de sistema se muestra como «Sistema».
8
+ - 0ee83dd: Filtro de opciones en selectores de relación: el campo (columna o campo de acción) acepta `option_filter` / `optionFilter` (una regla o lista con `field` y `equals` / `not_equals` / `in` / `not_in`) para ocultar opciones, p. ej. facturas con status `cancelada` en el selector de abonos (PIT-059). Se aplica en cliente sobre las columnas extra que devuelve `/options`, nunca oculta la selección actual y es retrocompatible (sin la propiedad no cambia nada). Nuevos exports: `getOptionFilter`, `applyOptionFilter`, `optionPassesRule`, tipos `OptionFilter` y `OptionFilterRule`; `useOptionsResolver` acepta `optionFilter` y `keepValue`.
9
+
10
+ ### Patch Changes
11
+
12
+ - ad1e1b0: Kanban: el menú «…» de la tarjeta ya no tira la app con «`MenuItem` must be used within `Menu`». El `DropdownMenu` de la tarjeta se importa ahora desde la entrada raíz de `@asteby/metacore-ui`, la misma de `RowActionMenuItem`. Así no se mezclan dos contextos de Radix cuando el host comparte la raíz por Module Federation y empaqueta `/primitives` aparte. Se agrega un test que lo vigila.
13
+ - Updated dependencies [66d82f5]
14
+ - @asteby/metacore-ui@2.24.1
15
+
16
+ ## 47.0.0
17
+
18
+ ### Minor Changes
19
+
20
+ - 4371f2c: Diálogos de acción: resultado de timbrado y errores del servidor.
21
+
22
+ - #1024 (FAC-3 / PIT-051): nuevo `CfdiStampPanel` / `CfdiStampResultDialog` / `extractStampResult`. Cuando la acción responde con `fiscal_uuid`, `pdf_url` y/o `xml_url`, el diálogo muestra el UUID como texto y enlaces «Descargar PDF» / «Descargar XML» en lugar del JSON. Sin esas claves no se inventa un resultado fiscal.
23
+ - #1023 (PIT-046): los diálogos de acción (genérico, wizard y confirmación) pintan un `FormErrorBanner` dentro del diálogo con el 422 (`errors` por campo) o el mensaje y causa de un 500, y permanecen abiertos.
24
+
25
+ - 4371f2c: FAC-12 (#1022): alta guiada por tipo de documento. Nuevo `DocumentFormDialog` y `resolveDocumentForms`: cuando el metadata de la tabla (o la prop `documentForms` de `DynamicCRUDPage`) trae `document_forms`, «Crear» muestra tarjetas de tipo (Factura / Nota de crédito / REP / Global / Traslado…) y, al elegir, los campos de ese tipo y un paso de renglones con `DocumentLinesGrid`; un solo tipo se salta el selector. Escribe el valor del tipo en `type_field`, serializa los renglones con `serializeLineItems`, muestra los 422 (banner + campos) dentro del diálogo y emite el evento de mutación para refrescar la lista. Sin manifest, el alta genérica no cambia. El SDK no embebe catálogos SAT: campos y valores los sirve el kernel o el addon. Nuevos tipos: `DocumentFormsManifest`, `DocumentFormType`, `DocumentFormLines`, `TableMetadata.document_forms`.
26
+ - 4371f2c: PIT-044: el menú lateral se filtra por los permisos efectivos del rol. `@asteby/metacore-ui` agrega `filterNavGroups`, `NavLinkItem.requires` y la prop `isItemVisible` de `AppSidebar`; `@asteby/metacore-runtime-react` agrega `useNavItemVisible`, `isNavItemAllowed`, `capabilityForNavItem` (una entrada `/m/<modelo>` exige `<modelo>.index`; las demás rutas no se bloquean) y `useNavigation` respeta `NavItem.requires`; el `AppSidebar` de starter-core lo aplica. Sin `PermissionsProvider` (o con `isAdmin`) no se oculta nada.
27
+ - 4371f2c: Las listas se refrescan tras crear, editar o borrar (#1020). `DynamicRecordDialog` y `ActionModalDispatcher` (componente federado, wizard, formulario genérico y confirmación) emiten el evento `metacore:record-mutated` con el modelo; `DynamicTable` y `DynamicKanban` vuelven a pedir los datos cuando el modelo coincide (sin distinguir mayúsculas). Nuevos exports: `emitRecordMutation`, `subscribeRecordMutations`, `useRecordMutationTick`.
28
+ - 4371f2c: Tres componentes de negocio compartidos:
29
+
30
+ - `VehiclePicker` (#1025): selector controlado de vehículo con búsqueda por placa o VIN y alta rápida (`CreateRecordDialog`), mismo estilo que `CustomerPicker`; sin `PermissionsProvider` no bloquea la UI.
31
+ - `RelateDocuments` (#1026): relaciona uno o más documentos origen con un tipo de relación (`related_document_id`, `relation_type`), muestra UUID/folio; los tipos llegan por prop (sin catálogos fiscales embebidos).
32
+ - `PrintSendDialog` (#1027): imprimir (URL de PDF del host) o enviar por `onSend(canal, destino, documento)`; sin integración de mensajería.
33
+
34
+ ### Patch Changes
35
+
36
+ - 4371f2c: Roles y permisos (#1021): «Permisos generales» deja de ser una columna de tarjetas junto al rol y pasa a ser un grupo sintético del combobox de módulos; al elegirlo se editan en la misma grilla de dos columnas que las acciones de un módulo, y el conteo de concedidos los incluye. Nuevo `generalPermissionsModule`.
37
+ - 4371f2c: Auditoría Ronda D: «Descargar Factura CFDI» no daba ninguna señal. `usePrintDocument` ahora muestra un toast de progreso, uno de éxito y, si falla, el motivo que responde el servidor (también cuando el cuerpo llega como Blob); la promesa sigue rechazando. `feedback: false` lo silencia y `PrintDocumentButton` deja el toast de error al llamador cuando pasa `onError`.
38
+ - Updated dependencies [4371f2c]
39
+ - @asteby/metacore-ui@2.24.0
40
+
3
41
  ## 46.4.0
4
42
 
5
43
  ### Minor Changes
@@ -1,3 +1,4 @@
1
+ import type { Translate } from './server-error';
1
2
  import type { ActionFieldDef } from './types';
2
3
  import { type ActionMetadata, type ActionModalProps } from '@asteby/metacore-sdk';
3
4
  export type { ActionMetadata, ActionModalProps };
@@ -66,7 +67,15 @@ export declare function unwrapRecordScalar(value: unknown): unknown;
66
67
  export declare function readRecordPath(record: any, path: string): unknown;
67
68
  /** Scalar seed for one action field from the row being acted on. */
68
69
  export declare function scalarDefaultFromRecord(field: ActionFieldDef, record: any): unknown;
69
- export declare function ActionModalDispatcher({ open, onOpenChange, action, model, record, endpoint, onSuccess, }: ActionModalProps): import("react").JSX.Element | null;
70
+ export declare function ActionModalDispatcher({ open, onOpenChange, action, model, record, endpoint, onSuccess: onSuccessProp, }: ActionModalProps): import("react").JSX.Element | null;
70
71
  /** How long an action with `modal` waits for its federated component. */
71
72
  export declare const FEDERATED_ACTION_MODAL_TIMEOUT_MS = 20000;
73
+ /** Text for the in-dialog error banner of a failed action (PIT-046, #1023): a
74
+ * 422 with per-field `errors` -> "Revisa los campos" + each localized line; any
75
+ * other failure (500, network) -> the server's own message and cause. Unlike
76
+ * the toast it stays on screen until the next attempt. */
77
+ export declare function actionErrorBannerMessage(err: unknown, fields: readonly ActionFieldDef[] | undefined, t: Translate, language?: string): string;
78
+ export declare function buildFieldDefaults(fields: ActionFieldDef[], record: any): Record<string, any>;
79
+ /** Renders one action field (same widgets as the action modals). Shared with DocumentFormDialog. */
80
+ export declare function renderField(field: ActionFieldDef, value: any, onChange: (value: any) => void, formValues?: Record<string, any>, record?: Record<string, any>, fieldErrors?: Record<string, string>): import("react").JSX.Element;
72
81
  //# sourceMappingURL=action-modal-dispatcher.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"action-modal-dispatcher.d.ts","sourceRoot":"","sources":["../src/action-modal-dispatcher.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"action-modal-dispatcher.d.ts","sourceRoot":"","sources":["../src/action-modal-dispatcher.tsx"],"names":[],"mappings":"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"}
@@ -15,13 +15,16 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
15
15
  //
16
16
  // The host injects its axios-like client via <ApiProvider>; we no longer
17
17
  // depend on a bundler alias to `@/lib/api`.
18
- import { useState, useEffect, useMemo, useRef, useSyncExternalStore } from 'react';
18
+ import { emitRecordMutation } from './record-mutation-events';
19
+ import { useState, useCallback, useEffect, useMemo, useRef, useSyncExternalStore } from 'react';
19
20
  import { useTranslation } from 'react-i18next';
20
21
  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';
21
22
  import { Loader2 } from 'lucide-react';
22
23
  import { ProcessStepper } from '@asteby/metacore-ui/wizard';
23
24
  import { toast } from 'sonner';
24
- import { toastServerError, toastServerSuccess, extractFieldErrors, localizeFieldErrorMap } from './server-error';
25
+ import { toastServerError, toastServerSuccess, extractFieldErrors, extractServerError, localizeFieldErrorMap } from './server-error';
26
+ import { FormErrorBanner } from './business/feedback';
27
+ import { CfdiStampResultDialog, extractStampResult } from './cfdi-stamp-panel';
25
28
  import { useBranchCreateGate } from './branch-create-gate';
26
29
  import { validateValues, bagHasErrors } from './validator';
27
30
  import { clearFieldErrorTree, formatFieldErrorsDescription, labelsForValidationFields, labelForValidationPath, lineItemErrorsFor, } from './field-validation-ui';
@@ -233,7 +236,14 @@ export function scalarDefaultFromRecord(field, record) {
233
236
  }
234
237
  return undefined;
235
238
  }
236
- export function ActionModalDispatcher({ open, onOpenChange, action, model, record, endpoint, onSuccess, }) {
239
+ export function ActionModalDispatcher({ open, onOpenChange, action, model, record, endpoint, onSuccess: onSuccessProp, }) {
240
+ // Every successful action may have created/updated/deleted a record of
241
+ // `model` (federated component, wizard, generic form or confirmation), so
242
+ // announce it: mounted lists of the model reload on their own (#1020).
243
+ const onSuccess = useCallback(() => {
244
+ emitRecordMutation(model, 'update');
245
+ onSuccessProp?.();
246
+ }, [model, onSuccessProp]);
237
247
  // Reactive read: a federated remote usually registers AFTER the first
238
248
  // render, and a memoized lookup would pin the fallback forever.
239
249
  const readComponent = () => getActionComponent(model, action.key);
@@ -518,10 +528,28 @@ function toastActionError(err, fields, t, language) {
518
528
  }
519
529
  toastServerError(err, { t, language });
520
530
  }
531
+ /** Text for the in-dialog error banner of a failed action (PIT-046, #1023): a
532
+ * 422 with per-field `errors` -> "Revisa los campos" + each localized line; any
533
+ * other failure (500, network) -> the server's own message and cause. Unlike
534
+ * the toast it stays on screen until the next attempt. */
535
+ export function actionErrorBannerMessage(err, fields, t, language) {
536
+ const localized = localizeActionFieldErrors(err, fields, t, language);
537
+ if (localized) {
538
+ const head = t('validation.failed', { defaultValue: validationCatalog(language).failed });
539
+ const lines = Object.values(localized).filter(Boolean);
540
+ return lines.length ? `${head}: ${lines.join(' · ')}` : head;
541
+ }
542
+ const fallback = t('common.error', { defaultValue: 'Algo salió mal. Intenta de nuevo.' });
543
+ const ex = extractServerError(err, fallback);
544
+ const title = t(ex.title, { defaultValue: ex.title });
545
+ return ex.description && ex.description !== title ? `${title} — ${ex.description}` : title;
546
+ }
521
547
  function ConfirmActionDialog({ open, onOpenChange, action, model, record, endpoint, onSuccess }) {
522
548
  const { t, i18n } = useTranslation();
523
549
  const api = useApi();
524
550
  const [executing, setExecuting] = useState(false);
551
+ const [formError, setFormError] = useState();
552
+ const [stamp, setStamp] = useState();
525
553
  // Actions listed in the model's `reason_required.actions` (cancel, void…)
526
554
  // answer 422 errors.reason without a motive; the prompt asks and retries.
527
555
  const reasonPrompt = useReasonPrompt();
@@ -532,6 +560,7 @@ function ConfirmActionDialog({ open, onOpenChange, action, model, record, endpoi
532
560
  const label = t(action.label, { defaultValue: action.label });
533
561
  const execute = async () => {
534
562
  setExecuting(true);
563
+ setFormError(undefined);
535
564
  try {
536
565
  const url = buildActionUrl(endpoint, model, record.id, action.key);
537
566
  // Supervisor PIN first (action.supervisorPolicy); null = cancelled.
@@ -548,21 +577,37 @@ function ConfirmActionDialog({ open, onOpenChange, action, model, record, endpoi
548
577
  return;
549
578
  if (res.data.success) {
550
579
  toastServerSuccess(res.data, { t });
580
+ const result = extractStampResult(res.data);
581
+ if (result) {
582
+ setStamp(result);
583
+ return;
584
+ }
551
585
  onOpenChange(false);
552
586
  onSuccess();
553
587
  }
554
588
  else {
589
+ setFormError(actionErrorBannerMessage({ response: { data: res.data } }, action.fields, t, i18n.language));
555
590
  toastActionError({ response: { data: res.data } }, action.fields, t, i18n.language);
556
591
  }
557
592
  }
558
593
  catch (err) {
594
+ setFormError(actionErrorBannerMessage(err, action.fields, t, i18n.language));
559
595
  toastActionError(err, action.fields, t, i18n.language);
560
596
  }
561
597
  finally {
562
598
  setExecuting(false);
563
599
  }
564
600
  };
565
- return (_jsxs(_Fragment, { children: [reasonPrompt.dialog, _jsx(AlertDialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsxs(AlertDialogTitle, { className: "flex items-center gap-2", children: [_jsx(DynamicIcon, { name: action.icon, className: "h-5 w-5" }), label] }), _jsx(AlertDialogDescription, { children: action.confirmMessage || `${label}?` })] }), record ? _jsx(RecordPreview, { model: model, record: record }) : null, _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { disabled: executing, children: t('common.cancel') }), _jsxs(AlertDialogAction, { onClick: (e) => { e.preventDefault(); execute(); }, disabled: executing, style: action.color ? { backgroundColor: action.color } : undefined, children: [executing ? _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : _jsx(DynamicIcon, { name: action.icon, className: "mr-2 h-4 w-4" }), label] })] })] }) })] }));
601
+ if (stamp) {
602
+ return (_jsx(CfdiStampResultDialog, { open: open, result: stamp, title: label, onOpenChange: (o) => {
603
+ if (o)
604
+ return;
605
+ setStamp(undefined);
606
+ onOpenChange(false);
607
+ onSuccess();
608
+ } }));
609
+ }
610
+ return (_jsxs(_Fragment, { children: [reasonPrompt.dialog, _jsx(AlertDialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsxs(AlertDialogTitle, { className: "flex items-center gap-2", children: [_jsx(DynamicIcon, { name: action.icon, className: "h-5 w-5" }), label] }), _jsx(AlertDialogDescription, { children: action.confirmMessage || `${label}?` })] }), record ? _jsx(RecordPreview, { model: model, record: record }) : null, _jsx(FormErrorBanner, { message: formError }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { disabled: executing, children: t('common.cancel') }), _jsxs(AlertDialogAction, { onClick: (e) => { e.preventDefault(); execute(); }, disabled: executing, style: action.color ? { backgroundColor: action.color } : undefined, children: [executing ? _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : _jsx(DynamicIcon, { name: action.icon, className: "mr-2 h-4 w-4" }), label] })] })] }) })] }));
566
611
  }
567
612
  function GenericActionModal({ open, onOpenChange, action, model, record, endpoint, onSuccess }) {
568
613
  const { t, i18n } = useTranslation();
@@ -578,6 +623,10 @@ function GenericActionModal({ open, onOpenChange, action, model, record, endpoin
578
623
  const [executing, setExecuting] = useState(false);
579
624
  // Per-field validation errors (localized), shown inline under each input.
580
625
  const [fieldErrors, setFieldErrors] = useState({});
626
+ // Persistent in-dialog banner for a failed submit (422 / 500) — the toast
627
+ // alone vanished and, for some failures, only the console had the cause.
628
+ const [formError, setFormError] = useState();
629
+ const [stamp, setStamp] = useState();
581
630
  // Related records to surface BELOW the form, as read-only context for the
582
631
  // record being acted on — e.g. the reception history of a transfer while
583
632
  // receiving against it. Sourced from the model's metadata.relations (the
@@ -610,6 +659,7 @@ function GenericActionModal({ open, onOpenChange, action, model, record, endpoin
610
659
  if (open && action.fields) {
611
660
  setFormData(buildFieldDefaults(action.fields, record));
612
661
  setFieldErrors({});
662
+ setFormError(undefined);
613
663
  }
614
664
  }, [open, action.fields, record]);
615
665
  // Lines the list row does not carry: load them from the declared relation
@@ -696,6 +746,7 @@ function GenericActionModal({ open, onOpenChange, action, model, record, endpoin
696
746
  };
697
747
  const lang = i18n.language;
698
748
  const handleActionError = (err) => {
749
+ setFormError(actionErrorBannerMessage(err, action.fields, t, lang));
699
750
  const labels = labelsForValidationFields(action.fields, t);
700
751
  const localized = localizeActionFieldErrors(err, action.fields, t, lang);
701
752
  if (localized) {
@@ -733,6 +784,7 @@ function GenericActionModal({ open, onOpenChange, action, model, record, endpoin
733
784
  }
734
785
  }
735
786
  setFieldErrors({});
787
+ setFormError(undefined);
736
788
  setExecuting(true);
737
789
  try {
738
790
  let payload = { ...formData };
@@ -765,6 +817,11 @@ function GenericActionModal({ open, onOpenChange, action, model, record, endpoin
765
817
  return;
766
818
  if (res.data.success) {
767
819
  toastServerSuccess(res.data, { t });
820
+ const result = extractStampResult(res.data);
821
+ if (result) {
822
+ setStamp(result);
823
+ return;
824
+ }
768
825
  onOpenChange(false);
769
826
  onSuccess();
770
827
  }
@@ -794,17 +851,26 @@ function GenericActionModal({ open, onOpenChange, action, model, record, endpoin
794
851
  : hasLineItems
795
852
  ? '820px'
796
853
  : undefined;
797
- return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: 'flex max-h-[90dvh] flex-col overflow-hidden ' + (widthPx ? '' : 'sm:max-w-xl'), style: { maxHeight: '90dvh', ...(widthPx ? { maxWidth: widthPx, width: '95vw' } : {}) }, children: [_jsxs(DialogHeader, { className: "shrink-0", children: [_jsxs(DialogTitle, { className: "flex items-center gap-2", children: [_jsx(DynamicIcon, { name: action.icon, className: "h-5 w-5" }), tl(action.label)] }), action.confirmMessage && _jsx(DialogDescription, { children: tl(action.confirmMessage) })] }), _jsx("div", { className: "-mx-1 min-h-0 flex-1 overflow-y-auto px-1 py-4", children: _jsxs(FieldGrid, { children: [action.fields?.map((field) => {
798
- const fullWidth = isLineItemsField(field) ||
799
- resolveWidget(field) === 'textarea' ||
800
- resolveWidget(field) === 'richtext';
801
- return (_jsxs(FieldCell, { fullWidth: fullWidth, children: [_jsx(FieldLabel, { htmlFor: field.key, required: field.required, children: tl(field.label) }), renderField(field, formData[field.key], (v) => updateField(field.key, v), formData, record, fieldErrors), fieldErrors[field.key] && (_jsx("p", { className: "text-destructive text-xs mt-1", children: fieldErrors[field.key] }))] }, field.key));
802
- }), embedRelations && relations.length > 0 && (_jsx(FieldCell, { fullWidth: true, children: _jsx(DynamicRelations, { record: record, relations: relations, lineSubtable: true, embedOnly: true }) }))] }) }), _jsxs(DialogFooter, { className: "shrink-0", children: [_jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), disabled: executing, children: t('common.cancel') }), _jsxs(Button, { onClick: execute, disabled: executing, style: action.color ? { backgroundColor: action.color, color: 'white' } : undefined, children: [executing ? _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : _jsx(DynamicIcon, { name: action.icon, className: "mr-2 h-4 w-4" }), tl(action.label)] })] }), reasonPrompt.dialog] }) }));
854
+ if (stamp) {
855
+ return (_jsx(CfdiStampResultDialog, { open: open, result: stamp, title: tl(action.label), onOpenChange: (o) => {
856
+ if (o)
857
+ return;
858
+ setStamp(undefined);
859
+ onOpenChange(false);
860
+ onSuccess();
861
+ } }));
862
+ }
863
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: 'flex max-h-[90dvh] flex-col overflow-hidden ' + (widthPx ? '' : 'sm:max-w-xl'), style: { maxHeight: '90dvh', ...(widthPx ? { maxWidth: widthPx, width: '95vw' } : {}) }, children: [_jsxs(DialogHeader, { className: "shrink-0", children: [_jsxs(DialogTitle, { className: "flex items-center gap-2", children: [_jsx(DynamicIcon, { name: action.icon, className: "h-5 w-5" }), tl(action.label)] }), action.confirmMessage && _jsx(DialogDescription, { children: tl(action.confirmMessage) })] }), _jsxs("div", { className: "-mx-1 min-h-0 flex-1 overflow-y-auto px-1 py-4", children: [_jsx(FormErrorBanner, { message: formError, className: "mb-3" }), _jsxs(FieldGrid, { children: [action.fields?.map((field) => {
864
+ const fullWidth = isLineItemsField(field) ||
865
+ resolveWidget(field) === 'textarea' ||
866
+ resolveWidget(field) === 'richtext';
867
+ return (_jsxs(FieldCell, { fullWidth: fullWidth, children: [_jsx(FieldLabel, { htmlFor: field.key, required: field.required, children: tl(field.label) }), renderField(field, formData[field.key], (v) => updateField(field.key, v), formData, record, fieldErrors), fieldErrors[field.key] && (_jsx("p", { className: "text-destructive text-xs mt-1", children: fieldErrors[field.key] }))] }, field.key));
868
+ }), embedRelations && relations.length > 0 && (_jsx(FieldCell, { fullWidth: true, children: _jsx(DynamicRelations, { record: record, relations: relations, lineSubtable: true, embedOnly: true }) }))] })] }), _jsxs(DialogFooter, { className: "shrink-0", children: [_jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), disabled: executing, children: t('common.cancel') }), _jsxs(Button, { onClick: execute, disabled: executing, style: action.color ? { backgroundColor: action.color, color: 'white' } : undefined, children: [executing ? _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : _jsx(DynamicIcon, { name: action.icon, className: "mr-2 h-4 w-4" }), tl(action.label)] })] }), reasonPrompt.dialog] }) }));
803
869
  }
804
870
  // buildFieldDefaults seeds formData for a set of action fields, honoring the
805
871
  // same line-items prefill spec + boolean/empty rules GenericActionModal uses, so
806
872
  // wizard steps and single-page forms initialize identically.
807
- function buildFieldDefaults(fields, record) {
873
+ export function buildFieldDefaults(fields, record) {
808
874
  const defaults = {};
809
875
  for (const field of fields) {
810
876
  if (isLineItemsField(field)) {
@@ -851,6 +917,8 @@ function WizardActionModal({ open, onOpenChange, action, model, record, endpoint
851
917
  const [stepIndex, setStepIndex] = useState(0);
852
918
  const [formData, setFormData] = useState({});
853
919
  const [executing, setExecuting] = useState(false);
920
+ const [formError, setFormError] = useState();
921
+ const [stamp, setStamp] = useState();
854
922
  // Reset to the first step and seed defaults for EVERY step's fields whenever
855
923
  // the modal (re)opens, so accumulated values from a prior run don't leak.
856
924
  useEffect(() => {
@@ -859,6 +927,7 @@ function WizardActionModal({ open, onOpenChange, action, model, record, endpoint
859
927
  const allFields = steps.flatMap((s) => s.fields ?? []);
860
928
  setFormData(buildFieldDefaults(allFields, record));
861
929
  setStepIndex(0);
930
+ setFormError(undefined);
862
931
  }, [open, action.steps, record]);
863
932
  const updateField = (key, value) => setFormData((prev) => ({ ...prev, [key]: value }));
864
933
  const step = steps[stepIndex];
@@ -890,6 +959,7 @@ function WizardActionModal({ open, onOpenChange, action, model, record, endpoint
890
959
  }
891
960
  }
892
961
  setExecuting(true);
962
+ setFormError(undefined);
893
963
  try {
894
964
  const url = buildActionUrl(endpoint, model, record?.id, action.key);
895
965
  const auth = await supervisor.authorize({ label: tl(action.label), supervisorPolicy: action.supervisorPolicy }, { model, recordId: record?.id != null ? String(record.id) : undefined });
@@ -902,16 +972,24 @@ function WizardActionModal({ open, onOpenChange, action, model, record, endpoint
902
972
  });
903
973
  if (!res)
904
974
  return;
975
+ const allFields = steps.flatMap(s => s.fields ?? []);
905
976
  if (res.data.success) {
906
977
  toastServerSuccess(res.data, { t });
978
+ const result = extractStampResult(res.data);
979
+ if (result) {
980
+ setStamp(result);
981
+ return;
982
+ }
907
983
  onOpenChange(false);
908
984
  onSuccess();
909
985
  }
910
986
  else {
911
- toastActionError({ response: { data: res.data } }, steps.flatMap(s => s.fields ?? []), t, i18n.language);
987
+ setFormError(actionErrorBannerMessage({ response: { data: res.data } }, allFields, t, i18n.language));
988
+ toastActionError({ response: { data: res.data } }, allFields, t, i18n.language);
912
989
  }
913
990
  }
914
991
  catch (err) {
992
+ setFormError(actionErrorBannerMessage(err, steps.flatMap(s => s.fields ?? []), t, i18n.language));
915
993
  toastActionError(err, steps.flatMap(s => s.fields ?? []), t, i18n.language);
916
994
  }
917
995
  finally {
@@ -920,12 +998,21 @@ function WizardActionModal({ open, onOpenChange, action, model, record, endpoint
920
998
  };
921
999
  if (steps.length === 0)
922
1000
  return null;
923
- return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: 'flex max-h-[90dvh] flex-col overflow-hidden ' + (widthPx ? '' : 'sm:max-w-xl'), style: { maxHeight: '90dvh', ...(widthPx ? { maxWidth: widthPx, width: '95vw' } : {}) }, children: [_jsxs(DialogHeader, { className: "shrink-0", children: [_jsxs(DialogTitle, { className: "flex items-center gap-2", children: [_jsx(DynamicIcon, { name: action.icon, className: "h-5 w-5" }), tl(action.label)] }), _jsxs("div", { className: "pt-1", children: [_jsx(ProcessStepper, { steps: steps.map((s, i) => ({ key: String(i), label: s.title ? tl(s.title) : `${t('common.step', { defaultValue: 'Paso' })} ${i + 1}` })), activeIndex: stepIndex, onStepClick: i => setStepIndex(i) }), _jsxs(DialogDescription, { className: "pt-3", children: [t('common.step', { defaultValue: 'Paso' }), " ", stepIndex + 1, "/", steps.length, step?.title ? ` · ${tl(step.title)}` : ''] }), step?.description && (_jsx("p", { className: "pt-1 text-sm text-muted-foreground", children: tl(step.description) }))] })] }), _jsx("div", { className: "-mx-1 min-h-0 flex-1 overflow-y-auto px-1 py-4", children: _jsx(FieldGrid, { children: stepFields.map((field) => {
924
- const fullWidth = isLineItemsField(field) ||
925
- resolveWidget(field) === 'textarea' ||
926
- resolveWidget(field) === 'richtext';
927
- return (_jsxs(FieldCell, { fullWidth: fullWidth, children: [_jsx(FieldLabel, { htmlFor: field.key, required: field.required, children: tl(field.label) }), renderField(field, formData[field.key], (v) => updateField(field.key, v), formData, record)] }, field.key));
928
- }) }) }), _jsxs(DialogFooter, { className: "shrink-0", children: [stepIndex > 0 ? (_jsx(Button, { variant: "outline", onClick: goBack, disabled: executing, children: t('common.back', { defaultValue: 'Atrás' }) })) : (_jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), disabled: executing, children: t('common.cancel') })), isLast ? (_jsxs(Button, { onClick: submit, disabled: executing, style: action.color ? { backgroundColor: action.color, color: 'white' } : undefined, children: [executing ? _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : _jsx(DynamicIcon, { name: action.icon, className: "mr-2 h-4 w-4" }), tl(action.label)] })) : (_jsx(Button, { onClick: goNext, disabled: executing, style: action.color ? { backgroundColor: action.color, color: 'white' } : undefined, children: t('common.next', { defaultValue: 'Siguiente' }) }))] }), reasonPrompt.dialog] }) }));
1001
+ if (stamp) {
1002
+ return (_jsx(CfdiStampResultDialog, { open: open, result: stamp, title: tl(action.label), onOpenChange: (o) => {
1003
+ if (o)
1004
+ return;
1005
+ setStamp(undefined);
1006
+ onOpenChange(false);
1007
+ onSuccess();
1008
+ } }));
1009
+ }
1010
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: 'flex max-h-[90dvh] flex-col overflow-hidden ' + (widthPx ? '' : 'sm:max-w-xl'), style: { maxHeight: '90dvh', ...(widthPx ? { maxWidth: widthPx, width: '95vw' } : {}) }, children: [_jsxs(DialogHeader, { className: "shrink-0", children: [_jsxs(DialogTitle, { className: "flex items-center gap-2", children: [_jsx(DynamicIcon, { name: action.icon, className: "h-5 w-5" }), tl(action.label)] }), _jsxs("div", { className: "pt-1", children: [_jsx(ProcessStepper, { steps: steps.map((s, i) => ({ key: String(i), label: s.title ? tl(s.title) : `${t('common.step', { defaultValue: 'Paso' })} ${i + 1}` })), activeIndex: stepIndex, onStepClick: i => setStepIndex(i) }), _jsxs(DialogDescription, { className: "pt-3", children: [t('common.step', { defaultValue: 'Paso' }), " ", stepIndex + 1, "/", steps.length, step?.title ? ` · ${tl(step.title)}` : ''] }), step?.description && (_jsx("p", { className: "pt-1 text-sm text-muted-foreground", children: tl(step.description) }))] })] }), _jsxs("div", { className: "-mx-1 min-h-0 flex-1 overflow-y-auto px-1 py-4", children: [_jsx(FormErrorBanner, { message: formError, className: "mb-3" }), _jsx(FieldGrid, { children: stepFields.map((field) => {
1011
+ const fullWidth = isLineItemsField(field) ||
1012
+ resolveWidget(field) === 'textarea' ||
1013
+ resolveWidget(field) === 'richtext';
1014
+ return (_jsxs(FieldCell, { fullWidth: fullWidth, children: [_jsx(FieldLabel, { htmlFor: field.key, required: field.required, children: tl(field.label) }), renderField(field, formData[field.key], (v) => updateField(field.key, v), formData, record)] }, field.key));
1015
+ }) })] }), _jsxs(DialogFooter, { className: "shrink-0", children: [stepIndex > 0 ? (_jsx(Button, { variant: "outline", onClick: goBack, disabled: executing, children: t('common.back', { defaultValue: 'Atrás' }) })) : (_jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), disabled: executing, children: t('common.cancel') })), isLast ? (_jsxs(Button, { onClick: submit, disabled: executing, style: action.color ? { backgroundColor: action.color, color: 'white' } : undefined, children: [executing ? _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : _jsx(DynamicIcon, { name: action.icon, className: "mr-2 h-4 w-4" }), tl(action.label)] })) : (_jsx(Button, { onClick: goNext, disabled: executing, style: action.color ? { backgroundColor: action.color, color: 'white' } : undefined, children: t('common.next', { defaultValue: 'Siguiente' }) }))] }), reasonPrompt.dialog] }) }));
929
1016
  }
930
1017
  function seedOptionFromRecord(field, value, record) {
931
1018
  if (!record || !field.key.endsWith('_id'))
@@ -948,7 +1035,8 @@ function seedOptionFromRecord(field, value, record) {
948
1035
  icon: sib.icon,
949
1036
  };
950
1037
  }
951
- function renderField(field, value, onChange,
1038
+ /** Renders one action field (same widgets as the action modals). Shared with DocumentFormDialog. */
1039
+ export function renderField(field, value, onChange,
952
1040
  // Full current form values — lets a line-items grid (and any cascading
953
1041
  // header picker) resolve a `dependsOn` reference against sibling header
954
1042
  // fields. Omitted by callers that have no surrounding form (the field is
@@ -0,0 +1,54 @@
1
+ /**
2
+ * audit-info.tsx
3
+ *
4
+ * <AuditInfo> — discreet "when and who" footer for a record: created, modified
5
+ * and (if tombstoned) deleted, each with the actor and a relative + absolute
6
+ * date. Driven only by the kernel's `TableMetadata.audit` (column names) and
7
+ * the record row — manifests declare nothing.
8
+ *
9
+ * Actor resolution (no extra requests), in order:
10
+ * 1. the host's `resolveActor(id)` when it returns a name;
11
+ * 2. the record's expanded sibling (`created_by_id` → `row.created_by`,
12
+ * `{ label | name | title }` or a plain string);
13
+ * 3. the system actor id → "Sistema" / "System";
14
+ * 4. a short id (first 8 chars) — never a long uuid.
15
+ *
16
+ * Renders nothing when `audit` is absent or the record has none of its values.
17
+ */
18
+ import * as React from 'react';
19
+ import type { AuditMeta } from './types';
20
+ /** The kernel's actor for unattended work (schedules, webhooks, connectors). */
21
+ export declare const SYSTEM_ACTOR_ID = "00000000-0000-0000-0000-000000000001";
22
+ /**
23
+ * Defensive reader for the served `audit` block: keeps only known keys whose
24
+ * value is a non-empty string; returns `undefined` when nothing usable remains.
25
+ */
26
+ export declare function readAuditMeta(raw: unknown): AuditMeta | undefined;
27
+ export interface AuditActor {
28
+ /** Display text. */
29
+ name: string;
30
+ /** True when the name is a real resolved label (not a short id). */
31
+ resolved: boolean;
32
+ }
33
+ /** Resolves the actor stored in `row[idColumn]` to something human. */
34
+ export declare function resolveAuditActor(row: Record<string, unknown>, idColumn: string | undefined, opts: {
35
+ resolveActor?: (id: string) => string | undefined;
36
+ systemLabel: string;
37
+ }): AuditActor | null;
38
+ export interface AuditInfoProps {
39
+ /** The record row (keys are the column names `audit` points at). */
40
+ record: Record<string, unknown> | null | undefined;
41
+ /** `TableMetadata.audit`. Absent → renders nothing. */
42
+ audit: AuditMeta | null | undefined;
43
+ /** Optional host resolver uuid → display name (e.g. from a users cache). */
44
+ resolveActor?: (id: string) => string | undefined;
45
+ /** IANA timezone; defaults to the org runtime timezone. */
46
+ timeZone?: string;
47
+ /** BCP-47 locale; defaults to the i18n language. */
48
+ locale?: string;
49
+ /** Start expanded. Default: collapsed. */
50
+ defaultOpen?: boolean;
51
+ className?: string;
52
+ }
53
+ export declare function AuditInfo({ record, audit, resolveActor, timeZone, locale, defaultOpen, className, }: AuditInfoProps): React.JSX.Element | null;
54
+ //# sourceMappingURL=audit-info.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit-info.d.ts","sourceRoot":"","sources":["../src/audit-info.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAU9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAExC,gFAAgF;AAChF,eAAO,MAAM,eAAe,yCAAyC,CAAA;AAWrE;;;GAGG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,CASjE;AAED,MAAM,WAAW,UAAU;IACvB,oBAAoB;IACpB,IAAI,EAAE,MAAM,CAAA;IACZ,oEAAoE;IACpE,QAAQ,EAAE,OAAO,CAAA;CACpB;AAED,uEAAuE;AACvE,wBAAgB,iBAAiB,CAC7B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5B,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,IAAI,EAAE;IAAE,YAAY,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GACjF,UAAU,GAAG,IAAI,CAmBnB;AAyBD,MAAM,WAAW,cAAc;IAC3B,oEAAoE;IACpE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,CAAA;IAClD,uDAAuD;IACvD,KAAK,EAAE,SAAS,GAAG,IAAI,GAAG,SAAS,CAAA;IACnC,4EAA4E;IAC5E,YAAY,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAA;IACjD,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,oDAAoD;IACpD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,0CAA0C;IAC1C,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,CAAA;CACrB;AAUD,wBAAgB,SAAS,CAAC,EACtB,MAAM,EACN,KAAK,EACL,YAAY,EACZ,QAAQ,EACR,MAAM,EACN,WAAmB,EACnB,SAAS,GACZ,EAAE,cAAc,4BA0EhB"}
@@ -0,0 +1,142 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * audit-info.tsx
4
+ *
5
+ * <AuditInfo> — discreet "when and who" footer for a record: created, modified
6
+ * and (if tombstoned) deleted, each with the actor and a relative + absolute
7
+ * date. Driven only by the kernel's `TableMetadata.audit` (column names) and
8
+ * the record row — manifests declare nothing.
9
+ *
10
+ * Actor resolution (no extra requests), in order:
11
+ * 1. the host's `resolveActor(id)` when it returns a name;
12
+ * 2. the record's expanded sibling (`created_by_id` → `row.created_by`,
13
+ * `{ label | name | title }` or a plain string);
14
+ * 3. the system actor id → "Sistema" / "System";
15
+ * 4. a short id (first 8 chars) — never a long uuid.
16
+ *
17
+ * Renders nothing when `audit` is absent or the record has none of its values.
18
+ */
19
+ import * as React from 'react';
20
+ import { useTranslation } from 'react-i18next';
21
+ import { formatDistanceToNow } from 'date-fns';
22
+ import { es, enUS } from 'date-fns/locale';
23
+ import { ChevronDown, ChevronRight, History } from 'lucide-react';
24
+ import { cn } from '@asteby/metacore-ui/lib';
25
+ import { formatDateCell } from './dynamic-columns';
26
+ import { objectLabel } from './dynamic-relation-helpers';
27
+ import { isNilUuid } from './nil-uuid';
28
+ import { useTimeZone } from './org-runtime-context';
29
+ /** The kernel's actor for unattended work (schedules, webhooks, connectors). */
30
+ export const SYSTEM_ACTOR_ID = '00000000-0000-0000-0000-000000000001';
31
+ const AUDIT_KEYS = [
32
+ 'created_at',
33
+ 'created_by',
34
+ 'updated_at',
35
+ 'updated_by',
36
+ 'deleted_at',
37
+ 'deleted_by',
38
+ ];
39
+ /**
40
+ * Defensive reader for the served `audit` block: keeps only known keys whose
41
+ * value is a non-empty string; returns `undefined` when nothing usable remains.
42
+ */
43
+ export function readAuditMeta(raw) {
44
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw))
45
+ return undefined;
46
+ const src = raw;
47
+ const out = {};
48
+ for (const k of AUDIT_KEYS) {
49
+ const v = src[k];
50
+ if (typeof v === 'string' && v.trim() !== '')
51
+ out[k] = v;
52
+ }
53
+ return Object.keys(out).length ? out : undefined;
54
+ }
55
+ /** Resolves the actor stored in `row[idColumn]` to something human. */
56
+ export function resolveAuditActor(row, idColumn, opts) {
57
+ if (!idColumn)
58
+ return null;
59
+ const raw = row[idColumn];
60
+ // The column itself may already be an expanded object.
61
+ const own = objectLabel(raw);
62
+ const id = typeof raw === 'string' ? raw.trim() : '';
63
+ if (!own && (!id || isNilUuid(id)))
64
+ return null;
65
+ if (id.toLowerCase() === SYSTEM_ACTOR_ID)
66
+ return { name: opts.systemLabel, resolved: true };
67
+ if (id) {
68
+ const fromHost = opts.resolveActor?.(id);
69
+ if (fromHost)
70
+ return { name: fromHost, resolved: true };
71
+ }
72
+ if (own)
73
+ return { name: own, resolved: true };
74
+ const base = idColumn.endsWith('_id') ? idColumn.slice(0, -3) : idColumn;
75
+ const sibling = base === idColumn ? undefined : row[base];
76
+ const siblingName = objectLabel(sibling) ?? (typeof sibling === 'string' && sibling.trim() !== '' ? sibling : undefined);
77
+ if (siblingName)
78
+ return { name: siblingName, resolved: true };
79
+ return { name: id.length > 8 ? id.slice(0, 8) : id, resolved: false };
80
+ }
81
+ const COPY = {
82
+ es: {
83
+ title: 'Historial del registro',
84
+ created: 'Creado por {{who}}',
85
+ updated: 'Modificado por {{who}}',
86
+ deleted: 'Eliminado por {{who}}',
87
+ createdAt: 'Creado',
88
+ updatedAt: 'Modificado',
89
+ deletedAt: 'Eliminado',
90
+ system: 'Sistema',
91
+ },
92
+ en: {
93
+ title: 'Record history',
94
+ created: 'Created by {{who}}',
95
+ updated: 'Modified by {{who}}',
96
+ deleted: 'Deleted by {{who}}',
97
+ createdAt: 'Created',
98
+ updatedAt: 'Modified',
99
+ deletedAt: 'Deleted',
100
+ system: 'System',
101
+ },
102
+ };
103
+ export function AuditInfo({ record, audit, resolveActor, timeZone, locale, defaultOpen = false, className, }) {
104
+ const { t, i18n } = useTranslation();
105
+ const ctxTz = useTimeZone();
106
+ const [open, setOpen] = React.useState(defaultOpen);
107
+ const meta = readAuditMeta(audit);
108
+ if (!meta || !record)
109
+ return null;
110
+ const lang = (locale || i18n?.language || 'es').toLowerCase().startsWith('en') ? 'en' : 'es';
111
+ const copy = COPY[lang];
112
+ const dfLocale = lang === 'en' ? enUS : es;
113
+ const tz = timeZone ?? ctxTz;
114
+ const tt = (key, vars) => t(`audit.${key}`, { defaultValue: copy[key], ...vars });
115
+ const build = (key, atCol, byCol, whoKey, whenKey) => {
116
+ const f = atCol ? formatDateCell(record[atCol], undefined, dfLocale, tz) : null;
117
+ if (!f)
118
+ return null;
119
+ const actor = resolveAuditActor(record, byCol, { resolveActor, systemLabel: tt('system') });
120
+ const date = new Date(record[atCol]);
121
+ const relative = formatDistanceToNow(date, { addSuffix: true, locale: dfLocale });
122
+ return {
123
+ key,
124
+ text: actor ? tt(whoKey, { who: actor.name }) : tt(whenKey),
125
+ absolute: f.display,
126
+ title: f.title,
127
+ relative,
128
+ };
129
+ };
130
+ const lines = [
131
+ build('created', meta.created_at, meta.created_by, 'created', 'createdAt'),
132
+ // Hide "modified" when it is the creation instant itself (never edited).
133
+ meta.updated_at && record[meta.updated_at] !== record[meta.created_at ?? '']
134
+ ? build('updated', meta.updated_at, meta.updated_by, 'updated', 'updatedAt')
135
+ : null,
136
+ build('deleted', meta.deleted_at, meta.deleted_by, 'deleted', 'deletedAt'),
137
+ ].filter((l) => l !== null);
138
+ if (lines.length === 0)
139
+ return null;
140
+ const Chevron = open ? ChevronDown : ChevronRight;
141
+ return (_jsxs("div", { className: cn('text-xs text-muted-foreground', className), "data-testid": "audit-info", children: [_jsxs("button", { type: "button", className: "flex items-center gap-1.5 hover:text-foreground", "aria-expanded": open, onClick: () => setOpen(o => !o), children: [_jsx(Chevron, { className: "h-3.5 w-3.5" }), _jsx(History, { className: "h-3.5 w-3.5" }), _jsx("span", { children: tt('title') })] }), open && (_jsx("ul", { className: "mt-2 space-y-1 pl-6", children: lines.map(l => (_jsxs("li", { "data-audit-line": l.key, children: [_jsx("span", { children: l.text }), ' · ', _jsx("time", { title: l.title, children: l.absolute }), _jsxs("span", { className: "opacity-70", children: [" (", l.relative, ")"] })] }, l.key))) }))] }));
142
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../src/business/catalog.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,qBAAqB;IAClC,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAA;IACZ,yCAAyC;IACzC,KAAK,EAAE,MAAM,CAAA;IACb,uCAAuC;IACvC,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,4EAA4E;IAC5E,WAAW,EAAE,MAAM,EAAE,CAAA;IACrB,kDAAkD;IAClD,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,uDAAuD;IACvD,OAAO,EAAE,MAAM,EAAE,CAAA;CACpB;AAED,eAAO,MAAM,mBAAmB,EAAE,SAAS,qBAAqB,EA8EtD,CAAA"}
1
+ {"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../src/business/catalog.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,qBAAqB;IAClC,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAA;IACZ,yCAAyC;IACzC,KAAK,EAAE,MAAM,CAAA;IACb,uCAAuC;IACvC,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,4EAA4E;IAC5E,WAAW,EAAE,MAAM,EAAE,CAAA;IACrB,kDAAkD;IAClD,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,uDAAuD;IACvD,OAAO,EAAE,MAAM,EAAE,CAAA;CACpB;AAED,eAAO,MAAM,mBAAmB,EAAE,SAAS,qBAAqB,EAsGtD,CAAA"}
@@ -76,4 +76,28 @@ export const BUSINESS_COMPONENTS = [
76
76
  'recommendationsToLines',
77
77
  ],
78
78
  },
79
+ {
80
+ name: 'VehiclePicker',
81
+ value: 'value: VehicleResult | null',
82
+ events: ['onChange'],
83
+ permissions: ['<model>.create (alta rápida; default Vehicle → vehicle.create)'],
84
+ usedBy: ['POS', 'Recepción de vehículo', 'OT', 'Cotización', 'Pedido'],
85
+ helpers: ['describeVehicle'],
86
+ },
87
+ {
88
+ name: 'RelateDocuments',
89
+ value: 'value: RelatedDocument[] (related_document_id + relation_type)',
90
+ events: ['onChange'],
91
+ permissions: [],
92
+ usedBy: ['Nota de crédito', 'Devolución', 'Sustitución de CFDI', 'REP'],
93
+ helpers: ['serializeRelatedDocuments'],
94
+ },
95
+ {
96
+ name: 'PrintSendDialog',
97
+ value: 'document: PrintableDocument (+ open)',
98
+ events: ['onSend', 'onPrint', 'onOpenChange'],
99
+ permissions: [],
100
+ usedBy: ['Cotización', 'Factura', 'Nota de crédito', 'Pedido', 'OC'],
101
+ helpers: [],
102
+ },
79
103
  ];
@@ -16,4 +16,7 @@ export * from './return';
16
16
  export * from './return-wizard';
17
17
  export * from './inspection';
18
18
  export * from './inspection-checklist';
19
+ export * from './vehicle-picker';
20
+ export * from './relate-documents';
21
+ export * from './print-send-dialog';
19
22
  //# sourceMappingURL=index.d.ts.map
@@ -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"}
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"}