@asteby/metacore-runtime-react 28.3.6 → 28.4.1

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 (39) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/dist/action-modal-dispatcher.js +1 -1
  3. package/dist/column-visibility.d.ts +0 -14
  4. package/dist/column-visibility.d.ts.map +1 -1
  5. package/dist/column-visibility.js +11 -1
  6. package/dist/dashboard-grid.d.ts.map +1 -1
  7. package/dist/dashboard-grid.js +5 -1
  8. package/dist/dialogs/dynamic-record.d.ts +13 -1
  9. package/dist/dialogs/dynamic-record.d.ts.map +1 -1
  10. package/dist/dialogs/dynamic-record.js +18 -6
  11. package/dist/dynamic-form-schema.d.ts +26 -1
  12. package/dist/dynamic-form-schema.d.ts.map +1 -1
  13. package/dist/dynamic-form-schema.js +41 -0
  14. package/dist/dynamic-form.d.ts.map +1 -1
  15. package/dist/dynamic-form.js +12 -5
  16. package/dist/dynamic-relation.d.ts +12 -0
  17. package/dist/dynamic-relation.d.ts.map +1 -1
  18. package/dist/dynamic-relation.js +12 -6
  19. package/dist/dynamic-relations.d.ts +9 -1
  20. package/dist/dynamic-relations.d.ts.map +1 -1
  21. package/dist/dynamic-relations.js +5 -4
  22. package/dist/index.d.ts +1 -1
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +1 -1
  25. package/dist/types.d.ts +35 -0
  26. package/dist/types.d.ts.map +1 -1
  27. package/package.json +1 -1
  28. package/src/__tests__/dynamic-relation-subtable-context.test.tsx +74 -0
  29. package/src/__tests__/visible-when.test.ts +63 -0
  30. package/src/action-modal-dispatcher.tsx +1 -1
  31. package/src/column-visibility.ts +14 -1
  32. package/src/dashboard-grid.tsx +5 -1
  33. package/src/dialogs/dynamic-record.tsx +29 -4
  34. package/src/dynamic-form-schema.ts +47 -1
  35. package/src/dynamic-form.tsx +17 -4
  36. package/src/dynamic-relation.tsx +25 -8
  37. package/src/dynamic-relations.tsx +16 -1
  38. package/src/index.ts +2 -0
  39. package/src/types.ts +36 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,61 @@
1
1
  # @asteby/metacore-runtime-react
2
2
 
3
+ ## 28.4.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 0075ae0: Pulido: (1) el mockup animado del tablero vacío ya no lleva borde punteado ni
8
+ padding — las skeleton cards flotan como las reales; (2) las secciones de
9
+ relaciones apiladas en el modal de vista (ej. "Líneas del pedido" y "Facturas")
10
+ tienen separación (space-y-6) en vez de ir pegadas.
11
+
12
+ ## 28.4.0
13
+
14
+ ### Minor Changes
15
+
16
+ - f1f66cb: feat: `visible_when` conditional field visibility in both form renderers
17
+
18
+ Add support for the kernel's new `visible_when` primitive: a create/edit form
19
+ field is rendered — and validated — only while a sibling field's current value
20
+ matches the declared predicate (`{ field, equals | in }`). A hidden field never
21
+ gates submit (its required-check is dropped along with it).
22
+
23
+ Wired into BOTH form renderers off a shared helper `evaluateVisibleWhen`
24
+ (exported alongside `getVisibleWhen`):
25
+
26
+ - `DynamicForm` (`dynamic-form.tsx`) — drives the zod schema, the balance gate
27
+ and the render off a `visibleFields` list filtered on the live values.
28
+ - `DynamicRecordDialog` (`dialogs/dynamic-record.tsx`) — `filterVisibleFields`
29
+ gains an optional `formValues` arg applying the predicate; the render and the
30
+ required-gate loop both use it.
31
+
32
+ `ActionFieldDef` and `ColumnDefinition` gain `visible_when` / `visibleWhen`
33
+ (snake + camel aliases). Fields without `visible_when` are always visible
34
+ (retro-compatible). Requires a kernel/ops bump that serves the field.
35
+
36
+ ### Patch Changes
37
+
38
+ - 9bc8bd1: runtime-react: scope audit-column hiding in `DynamicRelation` to the view-modal line-subtable context
39
+
40
+ PR #659 started dropping audit/system columns (`created_by`, timestamps,
41
+ `organization_id`) and `visibility: "table"` columns in every one-to-many
42
+ `DynamicRelation` panel — including standalone detail pages
43
+ (`/m/<model>/<id>`, `m/invoices/<id>`), where those columns are useful. Add an
44
+ explicit `lineSubtable` prop (default `false`) that gates
45
+ `isColumnVisibleInLineSubtable`; outside that context the panel keeps the
46
+ previous behaviour and only hides the FK, scope and `hidden` columns. The view
47
+ modal (`DynamicRecordDialog`, action-modal dispatcher) opts in via
48
+ `<DynamicRelations lineSubtable>`.
49
+
50
+ ## 28.3.7
51
+
52
+ ### Patch Changes
53
+
54
+ - ba40a57: Las columnas de auditoría resueltas como relación (created_by servido como
55
+ `created_by.avatar`) ahora también se ocultan en las sub-tablas del modal: el
56
+ filtro matchea la clave base y sus proyecciones `<key>.<subcampo>`, no solo el
57
+ match exacto.
58
+
3
59
  ## 28.3.6
4
60
 
5
61
  ### Patch Changes
@@ -458,7 +458,7 @@ function GenericActionModal({ open, onOpenChange, action, model, record, endpoin
458
458
  resolveWidget(field) === 'textarea' ||
459
459
  resolveWidget(field) === 'richtext';
460
460
  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), fieldErrors[field.key] && (_jsx("p", { className: "text-destructive text-xs mt-1", children: fieldErrors[field.key] }))] }, field.key));
461
- }), relations.length > 0 && (_jsx(FieldCell, { fullWidth: true, children: _jsx(DynamicRelations, { record: record, relations: relations }) }))] }) }), _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)] })] })] }) }));
461
+ }), relations.length > 0 && (_jsx(FieldCell, { fullWidth: true, children: _jsx(DynamicRelations, { record: record, relations: relations, lineSubtable: 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)] })] })] }) }));
462
462
  }
463
463
  // buildFieldDefaults seeds formData for a set of action fields, honoring the
464
464
  // same line-items prefill spec + boolean/empty rules GenericActionModal uses, so
@@ -25,20 +25,6 @@ export declare function isColumnVisibleInModal(col: ColumnDefinition): boolean;
25
25
  * in that context — see {@link isColumnVisibleInLineSubtable}.
26
26
  */
27
27
  export declare const AUDIT_SUBTABLE_COLUMN_KEYS: readonly string[];
28
- /**
29
- * Column visibility decision for a sub-table of lines/relations rendered inside
30
- * the view modal of a parent record. Two rules on top of the modal criterion:
31
- *
32
- * 1. Respect `visibility`: a column scoped to `'table'`/`'list'` never shows
33
- * here (reuses {@link isColumnVisibleInModal}), so a manifest hides a
34
- * redundant column from the sub-table with `visibility: "table"`.
35
- * 2. Audit columns ({@link AUDIT_SUBTABLE_COLUMN_KEYS}) are hidden BY DEFAULT
36
- * — unless the manifest explicitly opts them back in by declaring a
37
- * `visibility` (`'all'`/`'modal'`) on that column.
38
- *
39
- * Only applies to the embedded child-list context; the main `/m/<model>` table
40
- * uses {@link isColumnVisibleInTable} and is unaffected.
41
- */
42
28
  export declare function isColumnVisibleInLineSubtable(col: ColumnDefinition): boolean;
43
29
  /**
44
30
  * Returns the keys of columns that opt into the model's full-text search,
@@ -1 +1 @@
1
- {"version":3,"file":"column-visibility.d.ts","sourceRoot":"","sources":["../src/column-visibility.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAE9D;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAKrE;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAKrE;AAED;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,EAAE,SAAS,MAAM,EAOvD,CAAA;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,6BAA6B,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAI5E;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACnC,QAAQ,EAAE,IAAI,CAAC,aAAa,EAAE,SAAS,CAAC,GACzC,MAAM,EAAE,GAAG,IAAI,CAKjB"}
1
+ {"version":3,"file":"column-visibility.d.ts","sourceRoot":"","sources":["../src/column-visibility.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAE9D;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAKrE;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAKrE;AAED;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,EAAE,SAAS,MAAM,EAOvD,CAAA;AA6BD,wBAAgB,6BAA6B,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAI5E;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACnC,QAAQ,EAAE,IAAI,CAAC,aAAa,EAAE,SAAS,CAAC,GACzC,MAAM,EAAE,GAAG,IAAI,CAKjB"}
@@ -66,10 +66,20 @@ export const AUDIT_SUBTABLE_COLUMN_KEYS = [
66
66
  * Only applies to the embedded child-list context; the main `/m/<model>` table
67
67
  * uses {@link isColumnVisibleInTable} and is unaffected.
68
68
  */
69
+ /**
70
+ * True when a column key is (or belongs to) an audit column. Matches the plain
71
+ * key AND its resolved-relation projections: a `created_by` user relation is
72
+ * served as `created_by.avatar` / `created_by.name` (avatar+name+email cell),
73
+ * so an exact-match check missed it and the column still showed. We match the
74
+ * base key or any `<key>.<subfield>` projection.
75
+ */
76
+ function isAuditColumnKey(key) {
77
+ return AUDIT_SUBTABLE_COLUMN_KEYS.some((k) => key === k || key.startsWith(k + '.'));
78
+ }
69
79
  export function isColumnVisibleInLineSubtable(col) {
70
80
  if (!isColumnVisibleInModal(col))
71
81
  return false;
72
- if (!col.visibility && AUDIT_SUBTABLE_COLUMN_KEYS.includes(col.key))
82
+ if (!col.visibility && isAuditColumnKey(col.key))
73
83
  return false;
74
84
  return true;
75
85
  }
@@ -1 +1 @@
1
- {"version":3,"file":"dashboard-grid.d.ts","sourceRoot":"","sources":["../src/dashboard-grid.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B,OAAO,KAAK,EACR,kBAAkB,EAElB,oBAAoB,EACpB,mBAAmB,EACtB,MAAM,mBAAmB,CAAA;AAY1B,uEAAuE;AACvE,wBAAgB,eAAe,CAC3B,MAAM,CAAC,EAAE,oBAAoB,EAAE,EAC/B,OAAO,CAAC,EAAE,mBAAmB,EAAE,GAChC,oBAAoB,EAAE,CAgBxB;AASD,wBAAgB,aAAa,CAAC,EAC1B,MAAM,EACN,OAAO,EACP,QAAQ,EACR,OAAO,EACP,MAAM,EACN,QAAQ,EACR,SAAS,EACT,OAAO,GACV,EAAE,kBAAkB,qBAmJpB"}
1
+ {"version":3,"file":"dashboard-grid.d.ts","sourceRoot":"","sources":["../src/dashboard-grid.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B,OAAO,KAAK,EACR,kBAAkB,EAElB,oBAAoB,EACpB,mBAAmB,EACtB,MAAM,mBAAmB,CAAA;AAY1B,uEAAuE;AACvE,wBAAgB,eAAe,CAC3B,MAAM,CAAC,EAAE,oBAAoB,EAAE,EAC/B,OAAO,CAAC,EAAE,mBAAmB,EAAE,GAChC,oBAAoB,EAAE,CAgBxB;AASD,wBAAgB,aAAa,CAAC,EAC1B,MAAM,EACN,OAAO,EACP,QAAQ,EACR,OAAO,EACP,MAAM,EACN,QAAQ,EACR,SAAS,EACT,OAAO,GACV,EAAE,kBAAkB,qBAuJpB"}
@@ -131,7 +131,11 @@ export function DashboardGrid({ groups, widgets, loadData, isAdmin, locale, curr
131
131
  // box auto-height and the tiles collapse into a strip — so we own
132
132
  // the height here instead of hoping the dashboard layout supplies
133
133
  // one.
134
- 'h-[clamp(420px,60vh,720px)] w-full rounded-xl border border-dashed border-border/60 p-4', className), children: _jsx(DashboardEmptyMockup, { className: "h-full" }) }));
134
+ // No dashed border / padding wrapper: the skeleton tiles ARE
135
+ // cards (same anatomy as the real widgets), so the mockup
136
+ // should float exactly like the loaded dashboard, not sit
137
+ // inside a boxed placeholder. Keep only the definite height.
138
+ 'h-[clamp(420px,60vh,720px)] w-full', className), children: _jsx(DashboardEmptyMockup, { className: "h-full" }) }));
135
139
  }
136
140
  return (_jsx("div", { "data-testid": "dashboard-grid", className: cn(
137
141
  // Masonry: balanced CSS columns. Cards take their natural height
@@ -1,5 +1,6 @@
1
1
  import type { ModelSchema } from './types';
2
2
  import { type ResolvedOption } from '../use-options-resolver';
3
+ import type { VisibleWhen } from '../types';
3
4
  import { type ItemField } from '../collection-cell';
4
5
  import { type GetImageUrl } from '../image-url-context';
5
6
  export type { GetImageUrl };
@@ -71,6 +72,17 @@ export interface FieldDef {
71
72
  itemFields?: ItemField[];
72
73
  /** snake_case alias served by the kernel for `itemFields`. */
73
74
  item_fields?: ItemField[];
75
+ /**
76
+ * Conditional visibility: render — and required-check — this field only
77
+ * while a sibling field's current value matches the predicate. Mirrors the
78
+ * kernel v3 `visible_when` (projected onto the served modal FieldDef).
79
+ * Tolerates the camelCase alias. Absent = always visible; a hidden field is
80
+ * dropped from the required-gate so it never blocks submit. Evaluated by
81
+ * `evaluateVisibleWhen` against the live form values.
82
+ */
83
+ visible_when?: VisibleWhen;
84
+ /** camelCase alias for `visible_when`. */
85
+ visibleWhen?: VisibleWhen;
74
86
  }
75
87
  export interface DynamicRecordDialogProps {
76
88
  open: boolean;
@@ -165,7 +177,7 @@ export declare function fieldItemFields(field: FieldDef): ItemField[] | undefine
165
177
  export declare function isLineItemsField(field: FieldDef, value: any): boolean;
166
178
  export declare function fkSeedOption(field: FieldDef, value: any, record: any): ResolvedOption | null;
167
179
  export declare function isMoneyField(field: FieldDef, value: any): boolean;
168
- export declare function filterVisibleFields(fields: FieldDef[] | undefined, mode: 'view' | 'edit' | 'create'): FieldDef[];
180
+ export declare function filterVisibleFields(fields: FieldDef[] | undefined, mode: 'view' | 'edit' | 'create', formValues?: Record<string, any>): FieldDef[];
169
181
  export declare function DynamicRecordDialog({ open, onOpenChange, mode, model, recordId, endpoint, onSaved, onCreate, onUpdate, defaults, schema, onDelete, onEdit, onOpenFullPage, initialRecord, getImageUrl, timeZone, currency, onChange, }: DynamicRecordDialogProps): import("react").JSX.Element;
170
182
  export declare function ReadonlyEditField({ field, value }: {
171
183
  field: FieldDef;
@@ -1 +1 @@
1
- {"version":3,"file":"dynamic-record.d.ts","sourceRoot":"","sources":["../../src/dialogs/dynamic-record.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dynamic-record.d.ts","sourceRoot":"","sources":["../../src/dialogs/dynamic-record.tsx"],"names":[],"mappings":"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"}
@@ -23,7 +23,7 @@ import { toastServerError, extractFieldErrors, localizeFieldIssue } from '../ser
23
23
  import { DynamicSelectField, OptionLead, OptionThumb } from '../dynamic-select-field';
24
24
  import { DynamicRelations } from '../dynamic-relations';
25
25
  import { useOptionsResolver } from '../use-options-resolver';
26
- import { getFieldRef } from '../dynamic-form-schema';
26
+ import { getFieldRef, getVisibleWhen, evaluateVisibleWhen } from '../dynamic-form-schema';
27
27
  import { FieldCell } from '../field-grid';
28
28
  import { isNilUuid, normalizeNilUuid } from '../nil-uuid';
29
29
  import { normalizeRefFieldsForSubmit } from './normalize-submit';
@@ -239,12 +239,21 @@ export function isMoneyField(field, value) {
239
239
  // given mode. `hidden` fields never render. A `readonly` (server/system-
240
240
  // generated) field is EXCLUDED on create — the user can't set a value the
241
241
  // server will overwrite — but stays visible on edit/view (rendered disabled).
242
- export function filterVisibleFields(fields, mode) {
242
+ //
243
+ // `formValues`, when provided, additionally applies each field's `visible_when`
244
+ // predicate against the live form values (conditional visibility): a field is
245
+ // dropped while the referenced sibling's value does not match. Driving both the
246
+ // render and the required-gate off this same list means a hidden field never
247
+ // blocks submit. Omitting `formValues` keeps the legacy (always-visible)
248
+ // behaviour for callers that only gate on mode.
249
+ export function filterVisibleFields(fields, mode, formValues) {
243
250
  return (fields ?? []).filter(f => {
244
251
  if (f.hidden)
245
252
  return false;
246
253
  if (mode === 'create' && f.readonly)
247
254
  return false;
255
+ if (formValues && !evaluateVisibleWhen(getVisibleWhen(f), formValues))
256
+ return false;
248
257
  return true;
249
258
  });
250
259
  }
@@ -462,9 +471,12 @@ export function DynamicRecordDialog({ open, onOpenChange, mode, model, recordId,
462
471
  return;
463
472
  if (isEditable) {
464
473
  // Collect ALL missing required fields (not just the first) and mark
465
- // each inline instead of a single toast.
474
+ // each inline instead of a single toast. Only CURRENTLY-VISIBLE
475
+ // fields are gated: a field hidden by its `visible_when` predicate
476
+ // must not block submit even when it is declared required (matching
477
+ // the render, which drops it via the same filter).
466
478
  const missing = {};
467
- for (const field of modalMeta.fields ?? []) {
479
+ for (const field of filterVisibleFields(modalMeta.fields, mode, formValues)) {
468
480
  if (field.required && !formValues[field.key] && formValues[field.key] !== 0 && formValues[field.key] !== false) {
469
481
  missing[field.key] = localizeFieldIssue({ code: 'required' }, field.label, t);
470
482
  }
@@ -550,7 +562,7 @@ export function DynamicRecordDialog({ open, onOpenChange, mode, model, recordId,
550
562
  }
551
563
  };
552
564
  const title = modalMeta ? config.getTitle(modalMeta, t) : '';
553
- const visibleFields = filterVisibleFields(modalMeta?.fields, mode);
565
+ const visibleFields = filterVisibleFields(modalMeta?.fields, mode, formValues);
554
566
  return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: "sm:max-w-2xl max-h-[90vh] flex flex-col p-0 gap-0 overflow-hidden", children: [_jsxs(DialogHeader, { className: "p-6 pb-4 border-b shrink-0", children: [_jsx(DialogTitle, { children: title }), _jsx(DialogDescription, { children: config.description })] }), _jsx("div", { className: "flex-1 overflow-y-auto p-6", children: loading ? (_jsx(LoadingSkeleton, {})) : modalMeta ? (_jsx(ModelContext.Provider, { value: model, children: _jsx(ImageUrlContext.Provider, { value: getImageUrl, children: _jsx(TimeZoneContext.Provider, { value: timeZone, children: _jsxs(CurrencyContext.Provider, { value: currency, children: [_jsxs("form", { id: "dynamic-record-form", onSubmit: handleSubmit, className: "grid grid-cols-1 gap-x-6 gap-y-4 sm:grid-cols-2", children: [visibleFields.map(field => {
555
567
  const isFullWidth = field.type === 'textarea' ||
556
568
  field.widget === 'textarea' ||
@@ -566,7 +578,7 @@ export function DynamicRecordDialog({ open, onOpenChange, mode, model, recordId,
566
578
  return next;
567
579
  });
568
580
  } }) }, field.key));
569
- }), record?.external_url && (_jsx("div", { className: "sm:col-span-2 min-w-0", children: _jsxs("a", { href: record.external_url, target: "_blank", rel: "noreferrer", className: "inline-flex items-center gap-1.5 text-sm text-primary hover:underline mt-1", children: [_jsx(ExternalLink, { className: "h-3.5 w-3.5" }), "Ver en ", record.external_provider ?? 'proveedor externo'] }) }))] }), !isCreate && record && relations.length > 0 && (_jsx("div", { className: "mt-6", children: _jsx(DynamicRelations, { record: record, relations: relations, canCreate: mode === 'edit', canEdit: mode === 'edit', canDelete: mode === 'edit', onChange: handleChildChange }) }))] }) }) }) })) : null }), _jsxs(DialogFooter, { className: "p-4 border-t shrink-0 sm:justify-between", children: [isView && onOpenFullPage ? (_jsxs(Button, { variant: "ghost", size: "sm", className: "text-muted-foreground", onClick: () => { onOpenChange(false); onOpenFullPage(); }, children: [_jsx(ExternalLink, { className: "mr-1.5 h-3.5 w-3.5" }), "Ver p\u00E1gina completa"] })) : _jsx("span", {}), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), disabled: saving || deleting, children: config.cancelLabel }), isView && onDelete && (_jsxs(Button, { variant: "destructive", onClick: handleDelete, disabled: deleting || loading, children: [deleting && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), deleting ? 'Eliminando...' : 'Eliminar'] })), isView && onEdit && (_jsx(Button, { onClick: onEdit, disabled: deleting || loading, children: "Editar" })), isEditable && (_jsxs(Button, { type: "submit", form: "dynamic-record-form", disabled: saving || loading, children: [saving && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), saving ? config.submittingLabel : config.submitLabel] }))] })] })] }) }));
581
+ }), record?.external_url && (_jsx("div", { className: "sm:col-span-2 min-w-0", children: _jsxs("a", { href: record.external_url, target: "_blank", rel: "noreferrer", className: "inline-flex items-center gap-1.5 text-sm text-primary hover:underline mt-1", children: [_jsx(ExternalLink, { className: "h-3.5 w-3.5" }), "Ver en ", record.external_provider ?? 'proveedor externo'] }) }))] }), !isCreate && record && relations.length > 0 && (_jsx("div", { className: "mt-6", children: _jsx(DynamicRelations, { record: record, relations: relations, lineSubtable: true, canCreate: mode === 'edit', canEdit: mode === 'edit', canDelete: mode === 'edit', onChange: handleChildChange }) }))] }) }) }) })) : null }), _jsxs(DialogFooter, { className: "p-4 border-t shrink-0 sm:justify-between", children: [isView && onOpenFullPage ? (_jsxs(Button, { variant: "ghost", size: "sm", className: "text-muted-foreground", onClick: () => { onOpenChange(false); onOpenFullPage(); }, children: [_jsx(ExternalLink, { className: "mr-1.5 h-3.5 w-3.5" }), "Ver p\u00E1gina completa"] })) : _jsx("span", {}), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), disabled: saving || deleting, children: config.cancelLabel }), isView && onDelete && (_jsxs(Button, { variant: "destructive", onClick: handleDelete, disabled: deleting || loading, children: [deleting && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), deleting ? 'Eliminando...' : 'Eliminar'] })), isView && onEdit && (_jsx(Button, { onClick: onEdit, disabled: deleting || loading, children: "Editar" })), isEditable && (_jsxs(Button, { type: "submit", form: "dynamic-record-form", disabled: saving || loading, children: [saving && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), saving ? config.submittingLabel : config.submitLabel] }))] })] })] }) }));
570
582
  }
571
583
  function LoadingSkeleton() {
572
584
  return (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-x-6 gap-y-4", children: Array.from({ length: 6 }).map((_, i) => (_jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx(Skeleton, { className: "h-3.5 w-24" }), _jsx(Skeleton, { className: "h-9 w-full" })] }, i))) }));
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod';
2
- import type { ActionFieldDef, FieldOptionsConfig, OptionDef } from './types';
2
+ import type { ActionFieldDef, FieldOptionsConfig, OptionDef, VisibleWhen } from './types';
3
3
  /**
4
4
  * Apps register validator implementations by slug. The slug is the value
5
5
  * `OrgConfig.validators[<key>]` returns for a $org.<key> reference.
@@ -97,6 +97,31 @@ export declare function resolveDependsValue(field: ActionFieldDef, formValues?:
97
97
  * used as the default gating field.
98
98
  */
99
99
  export declare function applyOptionWhen(options: OptionDef[] | undefined, formValues: Record<string, any> | null | undefined, dependsOn?: string): OptionDef[];
100
+ /**
101
+ * Reads a field's `visible_when` predicate, tolerating the camelCase alias
102
+ * (`visibleWhen`) an app may author and the snake_case (`visible_when`) the
103
+ * kernel serves. Returns `undefined` when the field declares neither.
104
+ */
105
+ export declare function getVisibleWhen(field: {
106
+ visible_when?: VisibleWhen;
107
+ visibleWhen?: VisibleWhen;
108
+ } | null | undefined): VisibleWhen | undefined;
109
+ /**
110
+ * Evaluates a `visible_when` predicate against the current flat form values.
111
+ * Pure — no React, no side effects.
112
+ *
113
+ * - No predicate → `true` (the field is always visible; retrocompat).
114
+ * - With a predicate: read the value of the sibling `cond.field` from
115
+ * `formValues` (as string, null/undefined → ''). Visible when it is a member
116
+ * of `cond.in` (any-of, wins when present) OR equals `cond.equals`. A
117
+ * predicate with an empty `field` is a no-op (visible). A predicate that
118
+ * names a field but declares neither `in` nor `equals` hides nothing
119
+ * (visible) — nothing to gate on.
120
+ *
121
+ * `cond` may be the raw block off either the snake_case or camelCase slot; use
122
+ * `getVisibleWhen` to normalize first.
123
+ */
124
+ export declare function evaluateVisibleWhen(cond: VisibleWhen | null | undefined, formValues: Record<string, any> | null | undefined): boolean;
100
125
  /**
101
126
  * Reads a field's enriched options-resolution config, tolerating the camelCase
102
127
  * `optionsConfig` (authored SDK shape) and the snake_case `options_config` the
@@ -1 +1 @@
1
- {"version":3,"file":"dynamic-form-schema.d.ts","sourceRoot":"","sources":["../src/dynamic-form-schema.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dynamic-form-schema.d.ts","sourceRoot":"","sources":["../src/dynamic-form-schema.ts"],"names":[],"mappings":"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"}
@@ -313,6 +313,47 @@ export function applyOptionWhen(options, formValues, dependsOn) {
313
313
  return true;
314
314
  });
315
315
  }
316
+ /**
317
+ * Reads a field's `visible_when` predicate, tolerating the camelCase alias
318
+ * (`visibleWhen`) an app may author and the snake_case (`visible_when`) the
319
+ * kernel serves. Returns `undefined` when the field declares neither.
320
+ */
321
+ export function getVisibleWhen(field) {
322
+ if (!field)
323
+ return undefined;
324
+ const vw = field.visible_when ?? field.visibleWhen;
325
+ return vw && typeof vw === 'object' && typeof vw.field === 'string' ? vw : undefined;
326
+ }
327
+ /**
328
+ * Evaluates a `visible_when` predicate against the current flat form values.
329
+ * Pure — no React, no side effects.
330
+ *
331
+ * - No predicate → `true` (the field is always visible; retrocompat).
332
+ * - With a predicate: read the value of the sibling `cond.field` from
333
+ * `formValues` (as string, null/undefined → ''). Visible when it is a member
334
+ * of `cond.in` (any-of, wins when present) OR equals `cond.equals`. A
335
+ * predicate with an empty `field` is a no-op (visible). A predicate that
336
+ * names a field but declares neither `in` nor `equals` hides nothing
337
+ * (visible) — nothing to gate on.
338
+ *
339
+ * `cond` may be the raw block off either the snake_case or camelCase slot; use
340
+ * `getVisibleWhen` to normalize first.
341
+ */
342
+ export function evaluateVisibleWhen(cond, formValues) {
343
+ if (!cond || typeof cond.field !== 'string' || cond.field.trim() === '')
344
+ return true;
345
+ const raw = formValues ? formValues[cond.field.trim()] : undefined;
346
+ const current = raw == null ? '' : String(raw);
347
+ const inList = cond.in;
348
+ if (Array.isArray(inList) && inList.length > 0) {
349
+ return inList.some((v) => String(v) === current);
350
+ }
351
+ if (typeof cond.equals === 'string') {
352
+ return cond.equals === current;
353
+ }
354
+ // Named a field but declared no comparison → nothing to gate on.
355
+ return true;
356
+ }
316
357
  /**
317
358
  * Reads a field's enriched options-resolution config, tolerating the camelCase
318
359
  * `optionsConfig` (authored SDK shape) and the snake_case `options_config` the
@@ -1 +1 @@
1
- {"version":3,"file":"dynamic-form.d.ts","sourceRoot":"","sources":["../src/dynamic-form.tsx"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAC7C,OAAO,EACH,cAAc,EACd,aAAa,EAKhB,MAAM,uBAAuB,CAAA;AAQ9B,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,CAAA;AACxC,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAErD,MAAM,WAAW,gBAAgB;IAC7B,MAAM,EAAE,cAAc,EAAE,CAAA;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACnC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/D,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACrB;AAED,wBAAgB,WAAW,CAAC,EACxB,MAAM,EACN,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,WAAuB,EACvB,WAAwB,EACxB,QAAgB,GACnB,EAAE,gBAAgB,+BA8FlB"}
1
+ {"version":3,"file":"dynamic-form.d.ts","sourceRoot":"","sources":["../src/dynamic-form.tsx"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAC7C,OAAO,EACH,cAAc,EACd,aAAa,EAOhB,MAAM,uBAAuB,CAAA;AAQ9B,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,CAAA;AACxC,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAErD,MAAM,WAAW,gBAAgB;IAC7B,MAAM,EAAE,cAAc,EAAE,CAAA;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACnC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/D,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACrB;AAED,wBAAgB,WAAW,CAAC,EACxB,MAAM,EACN,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,WAAuB,EACvB,WAAwB,EACxB,QAAgB,GACnB,EAAE,gBAAgB,+BAyGlB"}
@@ -4,7 +4,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
4
4
  // outside the full record-edit modal.
5
5
  import { useEffect, useMemo, useState } from 'react';
6
6
  import { Input, Textarea, Label, Switch, Button, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@asteby/metacore-ui/primitives';
7
- import { buildZodSchema, resolveWidget, isLineItemsField, evaluateBalance, applyOptionWhen, getDependsOn, } from './dynamic-form-schema';
7
+ import { buildZodSchema, resolveWidget, isLineItemsField, evaluateBalance, applyOptionWhen, getDependsOn, getVisibleWhen, evaluateVisibleWhen, } from './dynamic-form-schema';
8
8
  import { useOptionsResolver } from './use-options-resolver';
9
9
  import { DynamicLineItems } from './dynamic-line-items';
10
10
  import { DynamicSelectField } from './dynamic-select-field';
@@ -27,19 +27,26 @@ export function DynamicForm({ fields, initialValues, onSubmit, onCancel, submitL
27
27
  // (ColumnDef.Readonly). They are also excluded from defaults/submit so a
28
28
  // create never posts e.g. `status: ""` over the column default.
29
29
  const editableFields = useMemo(() => fields.filter((f) => !f.readonly), [fields]);
30
- const schema = useMemo(() => buildZodSchema(editableFields), [editableFields]);
30
+ // Conditional visibility: a field carrying `visible_when` is rendered — and
31
+ // validated — only while the referenced sibling field's current value
32
+ // matches the predicate. Driving BOTH the schema and the render off the same
33
+ // filtered list means a hidden field never blocks submit (its required-gate
34
+ // is dropped with it), matching the primitive's contract. Fields with no
35
+ // `visible_when` are always kept (retrocompat).
36
+ const visibleFields = useMemo(() => editableFields.filter((f) => evaluateVisibleWhen(getVisibleWhen(f), values)), [editableFields, values]);
37
+ const schema = useMemo(() => buildZodSchema(visibleFields), [visibleFields]);
31
38
  // Line-items fields carrying a balance rule gate submit: an unbalanced entry
32
39
  // (Σdebit ≠ Σcredit, or all-zero when require_nonzero) can't be saved. This
33
40
  // is fully declarative — `evaluateBalance` returns undefined for fields with
34
41
  // no rule, so non-balanced forms are unaffected.
35
42
  const balanceBlocked = useMemo(() => {
36
- for (const f of editableFields) {
43
+ for (const f of visibleFields) {
37
44
  const state = evaluateBalance(f, values[f.key]);
38
45
  if (state && !state.balanced)
39
46
  return true;
40
47
  }
41
48
  return false;
42
- }, [editableFields, values]);
49
+ }, [visibleFields, values]);
43
50
  useEffect(() => {
44
51
  const defaults = {};
45
52
  for (const f of editableFields) {
@@ -81,7 +88,7 @@ export function DynamicForm({ fields, initialValues, onSubmit, onCancel, submitL
81
88
  // line-items grids (and textareas) span the full width so the row table /
82
89
  // memo gets room. Mirrors the pro look of the federated journal modal but
83
90
  // stays fully declarative — driven only by field shape.
84
- return (_jsxs("form", { onSubmit: handleSubmit, className: "grid gap-4", children: [_jsx("div", { className: "grid gap-4 sm:grid-cols-2", children: editableFields.map((field) => (_jsx(FieldRow, { field: field, value: values[field.key], onChange: (v) => update(field.key, v), values: values, error: errors[field.key], initialValues: initialValues }, field.key))) }), _jsxs("div", { className: "flex justify-end gap-2 pt-2", children: [onCancel && (_jsx(Button, { type: "button", variant: "outline", onClick: onCancel, disabled: submitting || disabled, children: cancelLabel })), _jsx(Button, { type: "submit", disabled: submitting || disabled || balanceBlocked, children: submitLabel })] })] }));
91
+ return (_jsxs("form", { onSubmit: handleSubmit, className: "grid gap-4", children: [_jsx("div", { className: "grid gap-4 sm:grid-cols-2", children: visibleFields.map((field) => (_jsx(FieldRow, { field: field, value: values[field.key], onChange: (v) => update(field.key, v), values: values, error: errors[field.key], initialValues: initialValues }, field.key))) }), _jsxs("div", { className: "flex justify-end gap-2 pt-2", children: [onCancel && (_jsx(Button, { type: "button", variant: "outline", onClick: onCancel, disabled: submitting || disabled, children: cancelLabel })), _jsx(Button, { type: "submit", disabled: submitting || disabled || balanceBlocked, children: submitLabel })] })] }));
85
92
  }
86
93
  // One form field row: label + renderer + inline error. Encapsulated as its own
87
94
  // component so a STATIC enum select whose options are all gated out by a sibling
@@ -27,6 +27,18 @@ interface CommonProps {
27
27
  filters?: Record<string, string>;
28
28
  /** Hidden columns; el FK siempre se oculta automáticamente. */
29
29
  hiddenColumns?: string[];
30
+ /**
31
+ * Contexto de sub-tabla de líneas dentro del MODAL de vista de un registro
32
+ * padre. Cuando es true se aplican las reglas de {@link isColumnVisibleInLineSubtable}
33
+ * — se ocultan por defecto las columnas de auditoría/sistema (created_by,
34
+ * timestamps, organization_id) y las scopeadas a `visibility: "table"`, que
35
+ * son ruido redundante bajo el registro padre.
36
+ *
37
+ * Default false: en una página de detalle autónoma (`/m/<model>/<id>`) el
38
+ * panel de relación conserva el comportamiento previo (solo oculta FK, scope
39
+ * y columnas `hidden`), donde esas columnas SÍ son útiles.
40
+ */
41
+ lineSubtable?: boolean;
30
42
  /** Permisos visibles. Default true. */
31
43
  canCreate?: boolean;
32
44
  canDelete?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"dynamic-relation.d.ts","sourceRoot":"","sources":["../src/dynamic-relation.tsx"],"names":[],"mappings":"AAgEA,YAAY,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AACrE,OAAO,EACH,kBAAkB,EAClB,uBAAuB,EACvB,kBAAkB,EAClB,yBAAyB,EACzB,wBAAwB,EACxB,aAAa,EACb,wBAAwB,EACxB,kBAAkB,EAClB,WAAW,EACX,eAAe,EACf,cAAc,GACjB,MAAM,4BAA4B,CAAA;AAEnC,MAAM,WAAW,sBAAsB;IACnC,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,wBAAwB,EAAE,MAAM,CAAA;IAChC,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,iBAAiB,EAAE,MAAM,CAAA;IACzB,uBAAuB,EAAE,MAAM,CAAA;IAC/B,WAAW,EAAE,MAAM,CAAA;CACtB;AAiBD,UAAU,WAAW;IACjB,6BAA6B;IAC7B,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;IACzB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,+DAA+D;IAC/D,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB,uCAAuC;IACvC,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,qCAAqC;IACrC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,2BAA2B;IAC3B,OAAO,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAA;IACzC,yBAAyB;IACzB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;CACxB;AAED,MAAM,WAAW,6BAA8B,SAAQ,WAAW;IAC9D,IAAI,EAAE,aAAa,CAAA;IACnB,yFAAyF;IACzF,KAAK,EAAE,MAAM,CAAA;IACb,kDAAkD;IAClD,UAAU,EAAE,MAAM,CAAA;IAClB,mDAAmD;IACnD,QAAQ,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,8BAA+B,SAAQ,WAAW;IAC/D,IAAI,EAAE,cAAc,CAAA;IACpB,wEAAwE;IACxE,OAAO,EAAE,MAAM,CAAA;IACf,sEAAsE;IACtE,UAAU,EAAE,MAAM,CAAA;IAClB,6BAA6B;IAC7B,UAAU,EAAE,MAAM,CAAA;IAClB,oEAAoE;IACpE,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,mEAAmE;IACnE,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,uEAAuE;IACvE,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,MAAM,oBAAoB,GAC1B,6BAA6B,GAC7B,8BAA8B,CAAA;AAEpC,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,+BAK1D"}
1
+ {"version":3,"file":"dynamic-relation.d.ts","sourceRoot":"","sources":["../src/dynamic-relation.tsx"],"names":[],"mappings":"AAgEA,YAAY,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AACrE,OAAO,EACH,kBAAkB,EAClB,uBAAuB,EACvB,kBAAkB,EAClB,yBAAyB,EACzB,wBAAwB,EACxB,aAAa,EACb,wBAAwB,EACxB,kBAAkB,EAClB,WAAW,EACX,eAAe,EACf,cAAc,GACjB,MAAM,4BAA4B,CAAA;AAEnC,MAAM,WAAW,sBAAsB;IACnC,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,wBAAwB,EAAE,MAAM,CAAA;IAChC,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,iBAAiB,EAAE,MAAM,CAAA;IACzB,uBAAuB,EAAE,MAAM,CAAA;IAC/B,WAAW,EAAE,MAAM,CAAA;CACtB;AAiBD,UAAU,WAAW;IACjB,6BAA6B;IAC7B,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;IACzB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,+DAA+D;IAC/D,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB;;;;;;;;;;OAUG;IACH,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,uCAAuC;IACvC,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,qCAAqC;IACrC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,2BAA2B;IAC3B,OAAO,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAA;IACzC,yBAAyB;IACzB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;CACxB;AAED,MAAM,WAAW,6BAA8B,SAAQ,WAAW;IAC9D,IAAI,EAAE,aAAa,CAAA;IACnB,yFAAyF;IACzF,KAAK,EAAE,MAAM,CAAA;IACb,kDAAkD;IAClD,UAAU,EAAE,MAAM,CAAA;IAClB,mDAAmD;IACnD,QAAQ,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,8BAA+B,SAAQ,WAAW;IAC/D,IAAI,EAAE,cAAc,CAAA;IACpB,wEAAwE;IACxE,OAAO,EAAE,MAAM,CAAA;IACf,sEAAsE;IACtE,UAAU,EAAE,MAAM,CAAA;IAClB,6BAA6B;IAC7B,UAAU,EAAE,MAAM,CAAA;IAClB,oEAAoE;IACpE,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,mEAAmE;IACnE,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,uEAAuE;IACvE,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,MAAM,oBAAoB,GAC1B,6BAA6B,GAC7B,8BAA8B,CAAA;AAEpC,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,+BAK1D"}
@@ -40,7 +40,7 @@ export function DynamicRelation(props) {
40
40
  }
41
41
  return _jsx(OneToManyRelation, { ...props });
42
42
  }
43
- function OneToManyRelation({ kind, model, foreignKey, parentId, filters, endpoint, hiddenColumns = [], canCreate = true, canDelete = true, canEdit = true, readonly, readOnly, strings, className, onChange, }) {
43
+ function OneToManyRelation({ kind, model, foreignKey, parentId, filters, endpoint, hiddenColumns = [], lineSubtable = false, canCreate = true, canDelete = true, canEdit = true, readonly, readOnly, strings, className, onChange, }) {
44
44
  // Read-only relation always wins over host-passed perms (AND). Tolerates the
45
45
  // camelCase alias the same way lock_rows/lockRows does.
46
46
  const isReadonly = readonly === true || readOnly === true;
@@ -102,11 +102,17 @@ function OneToManyRelation({ kind, model, foreignKey, parentId, filters, endpoin
102
102
  // Hide the FK and every scope column — they're fixed for this parent and
103
103
  // would just render the same value on every row.
104
104
  const hidden = new Set([foreignKey, ...Object.keys(filters || {}), ...hiddenColumns]);
105
- // isColumnVisibleInLineSubtable additionally drops `hidden`/table-scoped
106
- // columns AND the audit/system noise (created_by, timestamps, org_id)
107
- // that's redundant under a parent record — see column-visibility.ts.
108
- return metadata.columns.filter(c => !hidden.has(c.key) && isColumnVisibleInLineSubtable(c));
109
- }, [metadata, foreignKey, filtersKey, hiddenColumns]);
105
+ // In the view-modal line-subtable context, isColumnVisibleInLineSubtable
106
+ // additionally drops `hidden`/table-scoped columns AND the audit/system
107
+ // noise (created_by, timestamps, org_id) that's redundant under a parent
108
+ // record see column-visibility.ts. Outside that context (standalone
109
+ // detail page) we keep the previous behaviour and only drop `hidden`
110
+ // columns, so those columns still show where they're useful.
111
+ const keep = lineSubtable
112
+ ? (c) => !hidden.has(c.key) && isColumnVisibleInLineSubtable(c)
113
+ : (c) => !hidden.has(c.key) && !c.hidden;
114
+ return metadata.columns.filter(keep);
115
+ }, [metadata, foreignKey, filtersKey, hiddenColumns, lineSubtable]);
110
116
  // Reuse the EXACT column factory the main `<DynamicTable>` uses so each cell
111
117
  // renders identically — money in the org currency right-aligned, FK chips
112
118
  // with thumbnails, dates in the org timezone, status/option badges, creator
@@ -29,6 +29,14 @@ export interface DynamicRelationsProps {
29
29
  canEdit?: boolean;
30
30
  /** Translatable strings forwarded to each DynamicRelation. */
31
31
  strings?: Partial<DynamicRelationStrings>;
32
+ /**
33
+ * True cuando estos paneles se renderizan como sub-tablas de líneas dentro
34
+ * del MODAL de vista de un registro. Propaga `lineSubtable` a cada
35
+ * `<DynamicRelation>` para ocultar por defecto las columnas de auditoría/
36
+ * sistema redundantes bajo el padre. Default false — una página de detalle
37
+ * autónoma conserva todas las columnas.
38
+ */
39
+ lineSubtable?: boolean;
32
40
  /** Bubble up when any panel's data changes (create/delete/attach/detach). */
33
41
  onChange?: (relation: RelationMeta) => void;
34
42
  }
@@ -47,5 +55,5 @@ export declare function resolveParentId(record: {
47
55
  * both places is safe.
48
56
  */
49
57
  export declare function buildRelationFilters(relation: Pick<RelationMeta, 'foreign_key' | 'scope'>, parentId: string | number): Record<string, string>;
50
- export declare function DynamicRelations({ record, relations, parentIdKey, className, panelClassName, canCreate, canDelete, canEdit, strings, onChange, }: DynamicRelationsProps): import("react").JSX.Element | null;
58
+ export declare function DynamicRelations({ record, relations, parentIdKey, className, panelClassName, canCreate, canDelete, canEdit, strings, lineSubtable, onChange, }: DynamicRelationsProps): import("react").JSX.Element | null;
51
59
  //# sourceMappingURL=dynamic-relations.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dynamic-relations.d.ts","sourceRoot":"","sources":["../src/dynamic-relations.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAmB,KAAK,sBAAsB,EAAE,MAAM,oBAAoB,CAAA;AACjF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAE3C,MAAM,WAAW,qBAAqB;IAClC;;;OAGG;IACH,MAAM,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;QAAC,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,GAAG,IAAI,GAAG,SAAS,CAAA;IACzE,gEAAgE;IAChE,SAAS,EAAE,YAAY,EAAE,GAAG,IAAI,GAAG,SAAS,CAAA;IAC5C;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,6CAA6C;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,mCAAmC;IACnC,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,8DAA8D;IAC9D,OAAO,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAA;IACzC,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,YAAY,KAAK,IAAI,CAAA;CAC9C;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC3B,MAAM,EAAE;IAAE,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,EACnD,WAAW,SAAO,GACnB,MAAM,GAAG,MAAM,GAAG,SAAS,CAM7B;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAChC,QAAQ,EAAE,IAAI,CAAC,YAAY,EAAE,aAAa,GAAG,OAAO,CAAC,EACrD,QAAQ,EAAE,MAAM,GAAG,MAAM,GAC1B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAUxB;AAOD,wBAAgB,gBAAgB,CAAC,EAC7B,MAAM,EACN,SAAS,EACT,WAAkB,EAClB,SAAS,EACT,cAAc,EACd,SAAgB,EAChB,SAAgB,EAChB,OAAc,EACd,OAAO,EACP,QAAQ,GACX,EAAE,qBAAqB,sCAkEvB"}
1
+ {"version":3,"file":"dynamic-relations.d.ts","sourceRoot":"","sources":["../src/dynamic-relations.tsx"],"names":[],"mappings":"AAcA,OAAO,EAAmB,KAAK,sBAAsB,EAAE,MAAM,oBAAoB,CAAA;AACjF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAE3C,MAAM,WAAW,qBAAqB;IAClC;;;OAGG;IACH,MAAM,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;QAAC,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,GAAG,IAAI,GAAG,SAAS,CAAA;IACzE,gEAAgE;IAChE,SAAS,EAAE,YAAY,EAAE,GAAG,IAAI,GAAG,SAAS,CAAA;IAC5C;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,6CAA6C;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,mCAAmC;IACnC,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,8DAA8D;IAC9D,OAAO,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAA;IACzC;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,YAAY,KAAK,IAAI,CAAA;CAC9C;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC3B,MAAM,EAAE;IAAE,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,EACnD,WAAW,SAAO,GACnB,MAAM,GAAG,MAAM,GAAG,SAAS,CAM7B;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAChC,QAAQ,EAAE,IAAI,CAAC,YAAY,EAAE,aAAa,GAAG,OAAO,CAAC,EACrD,QAAQ,EAAE,MAAM,GAAG,MAAM,GAC1B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAUxB;AAOD,wBAAgB,gBAAgB,CAAC,EAC7B,MAAM,EACN,SAAS,EACT,WAAkB,EAClB,SAAS,EACT,cAAc,EACd,SAAgB,EAChB,SAAgB,EAChB,OAAc,EACd,OAAO,EACP,YAAoB,EACpB,QAAQ,GACX,EAAE,qBAAqB,sCAuEvB"}
@@ -12,6 +12,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
12
12
  // { owner_model: "Customer" }) into the panel's `filters` so the child list
13
13
  // is scoped by the FK AND every scope column.
14
14
  import { useMemo } from 'react';
15
+ import { cn } from '@asteby/metacore-ui/lib';
15
16
  import { DynamicRelation } from './dynamic-relation';
16
17
  /**
17
18
  * Normalizes the parent id off the record, tolerating a custom `parentIdKey`.
@@ -51,12 +52,12 @@ export function buildRelationFilters(relation, parentId) {
51
52
  function relationKey(rel, idx) {
52
53
  return rel.name || `${rel.through}-${rel.foreign_key}-${idx}`;
53
54
  }
54
- export function DynamicRelations({ record, relations, parentIdKey = 'id', className, panelClassName, canCreate = true, canDelete = true, canEdit = true, strings, onChange, }) {
55
+ export function DynamicRelations({ record, relations, parentIdKey = 'id', className, panelClassName, canCreate = true, canDelete = true, canEdit = true, strings, lineSubtable = false, onChange, }) {
55
56
  const parentId = useMemo(() => resolveParentId(record, parentIdKey), [record, parentIdKey]);
56
57
  if (parentId === undefined || !relations || relations.length === 0) {
57
58
  return null;
58
59
  }
59
- return (_jsx("div", { className: className, "data-dynamic-relations": "", children: relations.map((rel, idx) => {
60
+ return (_jsx("div", { className: cn('space-y-6', className), "data-dynamic-relations": "", children: relations.map((rel, idx) => {
60
61
  const filters = buildRelationFilters(rel, parentId);
61
62
  const panelStrings = {
62
63
  ...(strings || {}),
@@ -73,8 +74,8 @@ export function DynamicRelations({ record, relations, parentIdKey = 'id', classN
73
74
  // the panel degrades to the pivot list. Hosts with a
74
75
  // declared `references` should render DynamicRelation
75
76
  // directly. (one_to_many is the common detail-page case.)
76
- references: rel.through, foreignKey: rel.foreign_key, parentId: parentId, filters: filters, className: panelClassName, canCreate: canCreate, canDelete: canDelete, readonly: relReadonly, strings: panelStrings, onChange: onChange ? () => onChange(rel) : undefined }, relationKey(rel, idx)));
77
+ references: rel.through, foreignKey: rel.foreign_key, parentId: parentId, filters: filters, className: panelClassName, lineSubtable: lineSubtable, canCreate: canCreate, canDelete: canDelete, readonly: relReadonly, strings: panelStrings, onChange: onChange ? () => onChange(rel) : undefined }, relationKey(rel, idx)));
77
78
  }
78
- return (_jsx(DynamicRelation, { kind: "one_to_many", model: rel.through, foreignKey: rel.foreign_key, parentId: parentId, filters: filters, className: panelClassName, canCreate: canCreate, canDelete: canDelete, canEdit: canEdit, readonly: relReadonly, strings: panelStrings, onChange: onChange ? () => onChange(rel) : undefined }, relationKey(rel, idx)));
79
+ return (_jsx(DynamicRelation, { kind: "one_to_many", model: rel.through, foreignKey: rel.foreign_key, parentId: parentId, filters: filters, className: panelClassName, lineSubtable: lineSubtable, canCreate: canCreate, canDelete: canDelete, canEdit: canEdit, readonly: relReadonly, strings: panelStrings, onChange: onChange ? () => onChange(rel) : undefined }, relationKey(rel, idx)));
79
80
  }) }));
80
81
  }
package/dist/index.d.ts CHANGED
@@ -52,7 +52,7 @@ export { registerModelExtension, getModelExtension, clearModelExtensions, type M
52
52
  export { isColumnVisibleInTable, isColumnVisibleInModal, isColumnVisibleInLineSubtable, AUDIT_SUBTABLE_COLUMN_KEYS, getSearchableColumnKeys, } from './column-visibility';
53
53
  export { useOptionsResolver, projectOption, type ResolvedOption, type OptionsMeta, type UseOptionsResolverArgs, type UseOptionsResolverResult, } from './use-options-resolver';
54
54
  export { setOrgConfigBridge, getOrgConfigBridge, resolveValidatorToken, type OrgConfigBridge, } from './use-org-config-bridge';
55
- export { registerValidator, getDependsOn, resolveDependsValue, getOptionsConfig, resolveOptionsSource, } from './dynamic-form-schema';
55
+ export { registerValidator, getDependsOn, resolveDependsValue, getOptionsConfig, resolveOptionsSource, getVisibleWhen, evaluateVisibleWhen, } from './dynamic-form-schema';
56
56
  export { ActivityValueRenderer, type ActivityValueRendererProps, } from './activity-value-renderer';
57
57
  export { ActivityDiff, type ActivityEvent, type ActivityDiffProps, } from './activity-diff';
58
58
  export { RecordHistory, type RecordHistoryProps, } from './record-history';