@asteby/metacore-runtime-react 28.3.7 → 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.
package/dist/types.d.ts CHANGED
@@ -271,6 +271,17 @@ export interface ColumnDefinition {
271
271
  itemFields?: ColumnItemField[];
272
272
  /** snake_case alias served by the kernel for `itemFields`. */
273
273
  item_fields?: ColumnItemField[];
274
+ /**
275
+ * Conditional visibility in the create/edit modal: render this field only
276
+ * when a sibling field's current value matches the predicate. Mirrors the
277
+ * kernel v3 `Column.visible_when` (projected onto the served ColumnDef).
278
+ * Tolerates the camelCase alias. Absent = always visible; a hidden field is
279
+ * skipped by the required-gate so it never blocks submit. See
280
+ * `evaluateVisibleWhen`.
281
+ */
282
+ visible_when?: VisibleWhen;
283
+ /** camelCase alias for `visible_when`. */
284
+ visibleWhen?: VisibleWhen;
274
285
  }
275
286
  /**
276
287
  * One declared sub-field of a jsonb line-items column (see
@@ -290,6 +301,20 @@ export interface ActionCondition {
290
301
  operator: 'eq' | 'neq' | 'in' | 'not_in';
291
302
  value: string | string[];
292
303
  }
304
+ /**
305
+ * Conditional-visibility predicate for a create/edit form field. Mirrors the
306
+ * kernel v3 `visible_when` block (json `visible_when`): the owning field is
307
+ * rendered only when the SIBLING field named by `field` holds a value that
308
+ * matches — either equal to `equals` (exact string) OR a member of `in`
309
+ * (any-of). When both are set `in` wins. Absent = the field is always visible
310
+ * (retrocompat). Evaluated by `evaluateVisibleWhen` against the live form
311
+ * values; a hidden field never gates submit (its required-check is skipped).
312
+ */
313
+ export interface VisibleWhen {
314
+ field: string;
315
+ equals?: string;
316
+ in?: string[];
317
+ }
293
318
  export interface FieldValidation {
294
319
  regex?: string;
295
320
  min?: number;
@@ -442,6 +467,16 @@ export interface ActionFieldDef {
442
467
  storagePath?: string;
443
468
  /** snake_case alias served by the kernel manifest for `storagePath`. */
444
469
  storage_path?: string;
470
+ /**
471
+ * Conditional visibility: render this field only when a sibling field's
472
+ * current value matches the predicate. Mirrors the kernel v3
473
+ * `ActionField.visible_when`. Tolerates the camelCase alias. Absent = always
474
+ * visible; a hidden field is skipped by the required-gate so it never blocks
475
+ * submit. See `evaluateVisibleWhen`.
476
+ */
477
+ visible_when?: VisibleWhen;
478
+ /** camelCase alias for `visible_when`. */
479
+ visibleWhen?: VisibleWhen;
445
480
  }
446
481
  /**
447
482
  * Declarative reconciliation constraint on a line-items field: the summed value
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@asteby/metacore-runtime-react",
3
- "version": "28.3.7",
3
+ "version": "28.4.1",
4
4
  "description": "React runtime for metacore hosts — renders addon contributions dynamically",
5
5
  "repository": {
6
6
  "type": "git",
@@ -0,0 +1,74 @@
1
+ // @vitest-environment happy-dom
2
+ //
3
+ // `lineSubtable` scopes the audit/system-column hiding to the view-modal
4
+ // line-subtable context. On a standalone detail page (default, lineSubtable
5
+ // omitted) the relation panel keeps the previous behaviour and still renders
6
+ // audit columns (created_at, created_by, …). Inside the view modal the host
7
+ // passes `lineSubtable` and those redundant columns are dropped.
8
+ //
9
+ // Guards against the regression CodeRabbit flagged on SDK #659: the audit
10
+ // filter was applied unconditionally to every DynamicRelation, hiding those
11
+ // columns on full detail pages too.
12
+ import { afterEach, describe, expect, it, vi } from 'vitest'
13
+ import { cleanup, render, screen, waitFor } from '@testing-library/react'
14
+ import { DynamicRelation } from '../dynamic-relation'
15
+ import { ApiProvider, type ApiClient } from '../api-context'
16
+
17
+ afterEach(cleanup)
18
+
19
+ const META = {
20
+ name: 'line_item',
21
+ columns: [
22
+ { key: 'id', label: 'ID', type: 'text', sortable: true, filterable: false, hidden: true },
23
+ { key: 'invoice_id', label: 'Factura', type: 'text', sortable: false, filterable: false },
24
+ { key: 'sku', label: 'SKU', type: 'text', sortable: true, filterable: true },
25
+ { key: 'created_at', label: 'Creado', type: 'datetime', sortable: true, filterable: false },
26
+ ],
27
+ actions: [],
28
+ hasActions: false,
29
+ enableCRUDActions: false,
30
+ }
31
+
32
+ const DATA = [{ id: 'li_1', invoice_id: 'inv_42', sku: 'A-1', created_at: '2026-01-01T00:00:00Z' }]
33
+
34
+ function mockApi(): ApiClient {
35
+ return {
36
+ get: vi.fn((url: string) => {
37
+ if (url.startsWith('/metadata/table/')) {
38
+ return Promise.resolve({ data: { success: true, data: META } })
39
+ }
40
+ return Promise.resolve({ data: { success: true, data: DATA } })
41
+ }),
42
+ post: vi.fn(() => Promise.resolve({ data: { success: true, data: {} } })),
43
+ put: vi.fn(() => Promise.resolve({ data: { success: true, data: {} } })),
44
+ delete: vi.fn(() => Promise.resolve({ data: { success: true, data: {} } })),
45
+ }
46
+ }
47
+
48
+ function renderPanel(extra: Record<string, unknown>) {
49
+ return render(
50
+ <ApiProvider client={mockApi()}>
51
+ <DynamicRelation
52
+ kind="one_to_many"
53
+ model="line_item"
54
+ foreignKey="invoice_id"
55
+ parentId="inv_42"
56
+ {...extra}
57
+ />
58
+ </ApiProvider>,
59
+ )
60
+ }
61
+
62
+ describe('DynamicRelation — lineSubtable context', () => {
63
+ it('página de detalle (default): conserva la columna de auditoría created_at', async () => {
64
+ renderPanel({})
65
+ await waitFor(() => expect(screen.getByText('A-1')).toBeTruthy())
66
+ expect(screen.getByText('Creado')).toBeTruthy()
67
+ })
68
+
69
+ it('sub-tabla del modal de vista (lineSubtable): oculta created_at', async () => {
70
+ renderPanel({ lineSubtable: true })
71
+ await waitFor(() => expect(screen.getByText('A-1')).toBeTruthy())
72
+ expect(screen.queryByText('Creado')).toBeNull()
73
+ })
74
+ })
@@ -0,0 +1,63 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { evaluateVisibleWhen, getVisibleWhen } from '../dynamic-form-schema'
3
+ import type { VisibleWhen } from '../types'
4
+
5
+ // The discount_rules use case: a `rule_scope` field decides which picker shows.
6
+ const equalsProduct: VisibleWhen = { field: 'rule_scope', equals: 'product' }
7
+ const inCategory: VisibleWhen = { field: 'rule_scope', in: ['category'] }
8
+ const inMulti: VisibleWhen = { field: 'scope', in: ['a', 'b'] }
9
+
10
+ describe('evaluateVisibleWhen', () => {
11
+ it('no predicate → always visible', () => {
12
+ expect(evaluateVisibleWhen(undefined, { rule_scope: 'all' })).toBe(true)
13
+ expect(evaluateVisibleWhen(null, {})).toBe(true)
14
+ })
15
+
16
+ it('empty field → always visible (no-op)', () => {
17
+ expect(evaluateVisibleWhen({ field: '' } as VisibleWhen, { rule_scope: 'x' })).toBe(true)
18
+ })
19
+
20
+ it('`equals` matches the sibling value exactly', () => {
21
+ expect(evaluateVisibleWhen(equalsProduct, { rule_scope: 'product' })).toBe(true)
22
+ expect(evaluateVisibleWhen(equalsProduct, { rule_scope: 'category' })).toBe(false)
23
+ expect(evaluateVisibleWhen(equalsProduct, { rule_scope: 'all' })).toBe(false)
24
+ })
25
+
26
+ it('`in` matches membership; wins over `equals` when both present', () => {
27
+ expect(evaluateVisibleWhen(inCategory, { rule_scope: 'category' })).toBe(true)
28
+ expect(evaluateVisibleWhen(inCategory, { rule_scope: 'product' })).toBe(false)
29
+ expect(evaluateVisibleWhen(inMulti, { scope: 'b' })).toBe(true)
30
+ const both: VisibleWhen = { field: 'rule_scope', equals: 'product', in: ['category'] }
31
+ // `in` wins: category matches, product does not.
32
+ expect(evaluateVisibleWhen(both, { rule_scope: 'category' })).toBe(true)
33
+ expect(evaluateVisibleWhen(both, { rule_scope: 'product' })).toBe(false)
34
+ })
35
+
36
+ it('missing / null sibling value compares as empty string', () => {
37
+ expect(evaluateVisibleWhen(equalsProduct, {})).toBe(false)
38
+ expect(evaluateVisibleWhen(equalsProduct, { rule_scope: null })).toBe(false)
39
+ expect(evaluateVisibleWhen({ field: 'x', equals: '' } as VisibleWhen, {})).toBe(true)
40
+ })
41
+
42
+ it('coerces non-string sibling values before comparison', () => {
43
+ expect(evaluateVisibleWhen({ field: 'n', equals: '5' } as VisibleWhen, { n: 5 })).toBe(true)
44
+ expect(evaluateVisibleWhen({ field: 'b', in: ['true'] } as VisibleWhen, { b: true })).toBe(true)
45
+ })
46
+
47
+ it('predicate naming a field but no comparison → visible', () => {
48
+ expect(evaluateVisibleWhen({ field: 'rule_scope' } as VisibleWhen, { rule_scope: 'all' })).toBe(true)
49
+ })
50
+ })
51
+
52
+ describe('getVisibleWhen', () => {
53
+ it('reads snake_case and camelCase aliases', () => {
54
+ expect(getVisibleWhen({ visible_when: equalsProduct })).toEqual(equalsProduct)
55
+ expect(getVisibleWhen({ visibleWhen: inCategory })).toEqual(inCategory)
56
+ })
57
+
58
+ it('returns undefined for none / malformed', () => {
59
+ expect(getVisibleWhen(undefined)).toBeUndefined()
60
+ expect(getVisibleWhen({})).toBeUndefined()
61
+ expect(getVisibleWhen({ visible_when: { equals: 'x' } as unknown as VisibleWhen })).toBeUndefined()
62
+ })
63
+ })
@@ -686,7 +686,7 @@ function GenericActionModal({ open, onOpenChange, action, model, record, endpoin
686
686
  })}
687
687
  {relations.length > 0 && (
688
688
  <FieldCell fullWidth>
689
- <DynamicRelations record={record} relations={relations} />
689
+ <DynamicRelations record={record} relations={relations} lineSubtable />
690
690
  </FieldCell>
691
691
  )}
692
692
  </FieldGrid>
@@ -164,7 +164,11 @@ export function DashboardGrid({
164
164
  // box auto-height and the tiles collapse into a strip — so we own
165
165
  // the height here instead of hoping the dashboard layout supplies
166
166
  // one.
167
- 'h-[clamp(420px,60vh,720px)] w-full rounded-xl border border-dashed border-border/60 p-4',
167
+ // No dashed border / padding wrapper: the skeleton tiles ARE
168
+ // cards (same anatomy as the real widgets), so the mockup
169
+ // should float exactly like the loaded dashboard, not sit
170
+ // inside a boxed placeholder. Keep only the definite height.
171
+ 'h-[clamp(420px,60vh,720px)] w-full',
168
172
  className,
169
173
  )}
170
174
  >
@@ -52,7 +52,8 @@ import { toastServerError, extractFieldErrors, localizeFieldIssue } from '../ser
52
52
  import { DynamicSelectField, OptionLead, OptionThumb } from '../dynamic-select-field'
53
53
  import { DynamicRelations } from '../dynamic-relations'
54
54
  import { useOptionsResolver, type ResolvedOption } from '../use-options-resolver'
55
- import { getFieldRef } from '../dynamic-form-schema'
55
+ import { getFieldRef, getVisibleWhen, evaluateVisibleWhen } from '../dynamic-form-schema'
56
+ import type { VisibleWhen } from '../types'
56
57
  import { FieldCell } from '../field-grid'
57
58
  import { isNilUuid, normalizeNilUuid } from '../nil-uuid'
58
59
  import { normalizeRefFieldsForSubmit } from './normalize-submit'
@@ -146,6 +147,17 @@ export interface FieldDef {
146
147
  itemFields?: ItemField[]
147
148
  /** snake_case alias served by the kernel for `itemFields`. */
148
149
  item_fields?: ItemField[]
150
+ /**
151
+ * Conditional visibility: render — and required-check — this field only
152
+ * while a sibling field's current value matches the predicate. Mirrors the
153
+ * kernel v3 `visible_when` (projected onto the served modal FieldDef).
154
+ * Tolerates the camelCase alias. Absent = always visible; a hidden field is
155
+ * dropped from the required-gate so it never blocks submit. Evaluated by
156
+ * `evaluateVisibleWhen` against the live form values.
157
+ */
158
+ visible_when?: VisibleWhen
159
+ /** camelCase alias for `visible_when`. */
160
+ visibleWhen?: VisibleWhen
149
161
  }
150
162
 
151
163
  // Permissive shape: the wire payload may omit some fields (e.g. `title` is
@@ -458,13 +470,22 @@ export function isMoneyField(field: FieldDef, value: any): boolean {
458
470
  // given mode. `hidden` fields never render. A `readonly` (server/system-
459
471
  // generated) field is EXCLUDED on create — the user can't set a value the
460
472
  // server will overwrite — but stays visible on edit/view (rendered disabled).
473
+ //
474
+ // `formValues`, when provided, additionally applies each field's `visible_when`
475
+ // predicate against the live form values (conditional visibility): a field is
476
+ // dropped while the referenced sibling's value does not match. Driving both the
477
+ // render and the required-gate off this same list means a hidden field never
478
+ // blocks submit. Omitting `formValues` keeps the legacy (always-visible)
479
+ // behaviour for callers that only gate on mode.
461
480
  export function filterVisibleFields(
462
481
  fields: FieldDef[] | undefined,
463
482
  mode: 'view' | 'edit' | 'create',
483
+ formValues?: Record<string, any>,
464
484
  ): FieldDef[] {
465
485
  return (fields ?? []).filter(f => {
466
486
  if (f.hidden) return false
467
487
  if (mode === 'create' && f.readonly) return false
488
+ if (formValues && !evaluateVisibleWhen(getVisibleWhen(f), formValues)) return false
468
489
  return true
469
490
  })
470
491
  }
@@ -711,9 +732,12 @@ export function DynamicRecordDialog({
711
732
 
712
733
  if (isEditable) {
713
734
  // Collect ALL missing required fields (not just the first) and mark
714
- // each inline instead of a single toast.
735
+ // each inline instead of a single toast. Only CURRENTLY-VISIBLE
736
+ // fields are gated: a field hidden by its `visible_when` predicate
737
+ // must not block submit even when it is declared required (matching
738
+ // the render, which drops it via the same filter).
715
739
  const missing: Record<string, string> = {}
716
- for (const field of modalMeta.fields ?? []) {
740
+ for (const field of filterVisibleFields(modalMeta.fields, mode, formValues)) {
717
741
  if (field.required && !formValues[field.key] && formValues[field.key] !== 0 && formValues[field.key] !== false) {
718
742
  missing[field.key] = localizeFieldIssue({ code: 'required' }, field.label, t)
719
743
  }
@@ -803,7 +827,7 @@ export function DynamicRecordDialog({
803
827
 
804
828
  const title = modalMeta ? config.getTitle(modalMeta, t) : ''
805
829
 
806
- const visibleFields = filterVisibleFields(modalMeta?.fields, mode)
830
+ const visibleFields = filterVisibleFields(modalMeta?.fields, mode, formValues)
807
831
 
808
832
  return (
809
833
  <Dialog open={open} onOpenChange={onOpenChange}>
@@ -881,6 +905,7 @@ export function DynamicRecordDialog({
881
905
  <DynamicRelations
882
906
  record={record}
883
907
  relations={relations}
908
+ lineSubtable
884
909
  canCreate={mode === 'edit'}
885
910
  canEdit={mode === 'edit'}
886
911
  canDelete={mode === 'edit'}
@@ -2,7 +2,7 @@
2
2
  // callers (and unit tests) can use the zod schema without pulling in React or
3
3
  // metacore-ui primitives.
4
4
  import { z, type ZodTypeAny } from 'zod'
5
- import type { ActionFieldDef, FieldValidation, FieldOptionsConfig, OptionDef } from './types'
5
+ import type { ActionFieldDef, FieldValidation, FieldOptionsConfig, OptionDef, VisibleWhen } from './types'
6
6
  import { resolveValidatorToken } from './use-org-config-bridge'
7
7
 
8
8
  /**
@@ -335,6 +335,52 @@ export function applyOptionWhen(
335
335
  })
336
336
  }
337
337
 
338
+ /**
339
+ * Reads a field's `visible_when` predicate, tolerating the camelCase alias
340
+ * (`visibleWhen`) an app may author and the snake_case (`visible_when`) the
341
+ * kernel serves. Returns `undefined` when the field declares neither.
342
+ */
343
+ export function getVisibleWhen(
344
+ field: { visible_when?: VisibleWhen; visibleWhen?: VisibleWhen } | null | undefined,
345
+ ): VisibleWhen | undefined {
346
+ if (!field) return undefined
347
+ const vw = field.visible_when ?? field.visibleWhen
348
+ return vw && typeof vw === 'object' && typeof vw.field === 'string' ? vw : undefined
349
+ }
350
+
351
+ /**
352
+ * Evaluates a `visible_when` predicate against the current flat form values.
353
+ * Pure — no React, no side effects.
354
+ *
355
+ * - No predicate → `true` (the field is always visible; retrocompat).
356
+ * - With a predicate: read the value of the sibling `cond.field` from
357
+ * `formValues` (as string, null/undefined → ''). Visible when it is a member
358
+ * of `cond.in` (any-of, wins when present) OR equals `cond.equals`. A
359
+ * predicate with an empty `field` is a no-op (visible). A predicate that
360
+ * names a field but declares neither `in` nor `equals` hides nothing
361
+ * (visible) — nothing to gate on.
362
+ *
363
+ * `cond` may be the raw block off either the snake_case or camelCase slot; use
364
+ * `getVisibleWhen` to normalize first.
365
+ */
366
+ export function evaluateVisibleWhen(
367
+ cond: VisibleWhen | null | undefined,
368
+ formValues: Record<string, any> | null | undefined,
369
+ ): boolean {
370
+ if (!cond || typeof cond.field !== 'string' || cond.field.trim() === '') return true
371
+ const raw = formValues ? formValues[cond.field.trim()] : undefined
372
+ const current = raw == null ? '' : String(raw)
373
+ const inList = cond.in
374
+ if (Array.isArray(inList) && inList.length > 0) {
375
+ return inList.some((v) => String(v) === current)
376
+ }
377
+ if (typeof cond.equals === 'string') {
378
+ return cond.equals === current
379
+ }
380
+ // Named a field but declared no comparison → nothing to gate on.
381
+ return true
382
+ }
383
+
338
384
  /**
339
385
  * Reads a field's enriched options-resolution config, tolerating the camelCase
340
386
  * `optionsConfig` (authored SDK shape) and the snake_case `options_config` the
@@ -22,6 +22,8 @@ import {
22
22
  evaluateBalance,
23
23
  applyOptionWhen,
24
24
  getDependsOn,
25
+ getVisibleWhen,
26
+ evaluateVisibleWhen,
25
27
  } from './dynamic-form-schema'
26
28
  import { useOptionsResolver, type ResolvedOption } from './use-options-resolver'
27
29
  import { DynamicLineItems } from './dynamic-line-items'
@@ -70,19 +72,30 @@ export function DynamicForm({
70
72
  [fields],
71
73
  )
72
74
 
73
- const schema = useMemo(() => buildZodSchema(editableFields), [editableFields])
75
+ // Conditional visibility: a field carrying `visible_when` is rendered — and
76
+ // validated — only while the referenced sibling field's current value
77
+ // matches the predicate. Driving BOTH the schema and the render off the same
78
+ // filtered list means a hidden field never blocks submit (its required-gate
79
+ // is dropped with it), matching the primitive's contract. Fields with no
80
+ // `visible_when` are always kept (retrocompat).
81
+ const visibleFields = useMemo(
82
+ () => editableFields.filter((f) => evaluateVisibleWhen(getVisibleWhen(f), values)),
83
+ [editableFields, values],
84
+ )
85
+
86
+ const schema = useMemo(() => buildZodSchema(visibleFields), [visibleFields])
74
87
 
75
88
  // Line-items fields carrying a balance rule gate submit: an unbalanced entry
76
89
  // (Σdebit ≠ Σcredit, or all-zero when require_nonzero) can't be saved. This
77
90
  // is fully declarative — `evaluateBalance` returns undefined for fields with
78
91
  // no rule, so non-balanced forms are unaffected.
79
92
  const balanceBlocked = useMemo(() => {
80
- for (const f of editableFields) {
93
+ for (const f of visibleFields) {
81
94
  const state = evaluateBalance(f, values[f.key])
82
95
  if (state && !state.balanced) return true
83
96
  }
84
97
  return false
85
- }, [editableFields, values])
98
+ }, [visibleFields, values])
86
99
 
87
100
  useEffect(() => {
88
101
  const defaults: Record<string, any> = {}
@@ -125,7 +138,7 @@ export function DynamicForm({
125
138
  return (
126
139
  <form onSubmit={handleSubmit} className="grid gap-4">
127
140
  <div className="grid gap-4 sm:grid-cols-2">
128
- {editableFields.map((field) => (
141
+ {visibleFields.map((field) => (
129
142
  <FieldRow
130
143
  key={field.key}
131
144
  field={field}
@@ -48,7 +48,7 @@ import { useTimeZone, useCurrency } from './org-runtime-context'
48
48
  import { makeDefaultGetDynamicColumns } from './dynamic-columns'
49
49
  import { isColumnVisibleInLineSubtable } from './column-visibility'
50
50
  import { useOptionsResolver } from './use-options-resolver'
51
- import type { ApiResponse, TableMetadata } from './types'
51
+ import type { ApiResponse, ColumnDefinition, TableMetadata } from './types'
52
52
  import {
53
53
  buildCreatePayload,
54
54
  buildPivotAttachPayload,
@@ -120,6 +120,18 @@ interface CommonProps {
120
120
  filters?: Record<string, string>
121
121
  /** Hidden columns; el FK siempre se oculta automáticamente. */
122
122
  hiddenColumns?: string[]
123
+ /**
124
+ * Contexto de sub-tabla de líneas dentro del MODAL de vista de un registro
125
+ * padre. Cuando es true se aplican las reglas de {@link isColumnVisibleInLineSubtable}
126
+ * — se ocultan por defecto las columnas de auditoría/sistema (created_by,
127
+ * timestamps, organization_id) y las scopeadas a `visibility: "table"`, que
128
+ * son ruido redundante bajo el registro padre.
129
+ *
130
+ * Default false: en una página de detalle autónoma (`/m/<model>/<id>`) el
131
+ * panel de relación conserva el comportamiento previo (solo oculta FK, scope
132
+ * y columnas `hidden`), donde esas columnas SÍ son útiles.
133
+ */
134
+ lineSubtable?: boolean
123
135
  /** Permisos visibles. Default true. */
124
136
  canCreate?: boolean
125
137
  canDelete?: boolean
@@ -191,6 +203,7 @@ function OneToManyRelation({
191
203
  filters,
192
204
  endpoint,
193
205
  hiddenColumns = [],
206
+ lineSubtable = false,
194
207
  canCreate = true,
195
208
  canDelete = true,
196
209
  canEdit = true,
@@ -266,13 +279,17 @@ function OneToManyRelation({
266
279
  // Hide the FK and every scope column — they're fixed for this parent and
267
280
  // would just render the same value on every row.
268
281
  const hidden = new Set([foreignKey, ...Object.keys(filters || {}), ...hiddenColumns])
269
- // isColumnVisibleInLineSubtable additionally drops `hidden`/table-scoped
270
- // columns AND the audit/system noise (created_by, timestamps, org_id)
271
- // that's redundant under a parent record — see column-visibility.ts.
272
- return metadata.columns.filter(
273
- c => !hidden.has(c.key) && isColumnVisibleInLineSubtable(c),
274
- )
275
- }, [metadata, foreignKey, filtersKey, hiddenColumns])
282
+ // In the view-modal line-subtable context, isColumnVisibleInLineSubtable
283
+ // additionally drops `hidden`/table-scoped columns AND the audit/system
284
+ // noise (created_by, timestamps, org_id) that's redundant under a parent
285
+ // record — see column-visibility.ts. Outside that context (standalone
286
+ // detail page) we keep the previous behaviour and only drop `hidden`
287
+ // columns, so those columns still show where they're useful.
288
+ const keep = lineSubtable
289
+ ? (c: ColumnDefinition) => !hidden.has(c.key) && isColumnVisibleInLineSubtable(c)
290
+ : (c: ColumnDefinition) => !hidden.has(c.key) && !c.hidden
291
+ return metadata.columns.filter(keep)
292
+ }, [metadata, foreignKey, filtersKey, hiddenColumns, lineSubtable])
276
293
 
277
294
  // Reuse the EXACT column factory the main `<DynamicTable>` uses so each cell
278
295
  // renders identically — money in the org currency right-aligned, FK chips
@@ -11,6 +11,7 @@
11
11
  // { owner_model: "Customer" }) into the panel's `filters` so the child list
12
12
  // is scoped by the FK AND every scope column.
13
13
  import { useMemo } from 'react'
14
+ import { cn } from '@asteby/metacore-ui/lib'
14
15
  import { DynamicRelation, type DynamicRelationStrings } from './dynamic-relation'
15
16
  import type { RelationMeta } from './types'
16
17
 
@@ -40,6 +41,14 @@ export interface DynamicRelationsProps {
40
41
  canEdit?: boolean
41
42
  /** Translatable strings forwarded to each DynamicRelation. */
42
43
  strings?: Partial<DynamicRelationStrings>
44
+ /**
45
+ * True cuando estos paneles se renderizan como sub-tablas de líneas dentro
46
+ * del MODAL de vista de un registro. Propaga `lineSubtable` a cada
47
+ * `<DynamicRelation>` para ocultar por defecto las columnas de auditoría/
48
+ * sistema redundantes bajo el padre. Default false — una página de detalle
49
+ * autónoma conserva todas las columnas.
50
+ */
51
+ lineSubtable?: boolean
43
52
  /** Bubble up when any panel's data changes (create/delete/attach/detach). */
44
53
  onChange?: (relation: RelationMeta) => void
45
54
  }
@@ -96,6 +105,7 @@ export function DynamicRelations({
96
105
  canDelete = true,
97
106
  canEdit = true,
98
107
  strings,
108
+ lineSubtable = false,
99
109
  onChange,
100
110
  }: DynamicRelationsProps) {
101
111
  const parentId = useMemo(
@@ -108,7 +118,10 @@ export function DynamicRelations({
108
118
  }
109
119
 
110
120
  return (
111
- <div className={className} data-dynamic-relations="">
121
+ // `space-y-6` separates stacked relation panels (e.g. "Líneas del
122
+ // pedido" and "Facturas" in the view modal) — without it consecutive
123
+ // panels sat flush against each other with no breathing room.
124
+ <div className={cn('space-y-6', className)} data-dynamic-relations="">
112
125
  {relations.map((rel, idx) => {
113
126
  const filters = buildRelationFilters(rel, parentId)
114
127
  const panelStrings: Partial<DynamicRelationStrings> = {
@@ -135,6 +148,7 @@ export function DynamicRelations({
135
148
  parentId={parentId}
136
149
  filters={filters}
137
150
  className={panelClassName}
151
+ lineSubtable={lineSubtable}
138
152
  canCreate={canCreate}
139
153
  canDelete={canDelete}
140
154
  readonly={relReadonly}
@@ -152,6 +166,7 @@ export function DynamicRelations({
152
166
  parentId={parentId}
153
167
  filters={filters}
154
168
  className={panelClassName}
169
+ lineSubtable={lineSubtable}
155
170
  canCreate={canCreate}
156
171
  canDelete={canDelete}
157
172
  canEdit={canEdit}
package/src/index.ts CHANGED
@@ -307,6 +307,8 @@ export {
307
307
  resolveDependsValue,
308
308
  getOptionsConfig,
309
309
  resolveOptionsSource,
310
+ getVisibleWhen,
311
+ evaluateVisibleWhen,
310
312
  } from './dynamic-form-schema'
311
313
  export {
312
314
  ActivityValueRenderer,
package/src/types.ts CHANGED
@@ -295,6 +295,17 @@ export interface ColumnDefinition {
295
295
  itemFields?: ColumnItemField[]
296
296
  /** snake_case alias served by the kernel for `itemFields`. */
297
297
  item_fields?: ColumnItemField[]
298
+ /**
299
+ * Conditional visibility in the create/edit modal: render this field only
300
+ * when a sibling field's current value matches the predicate. Mirrors the
301
+ * kernel v3 `Column.visible_when` (projected onto the served ColumnDef).
302
+ * Tolerates the camelCase alias. Absent = always visible; a hidden field is
303
+ * skipped by the required-gate so it never blocks submit. See
304
+ * `evaluateVisibleWhen`.
305
+ */
306
+ visible_when?: VisibleWhen
307
+ /** camelCase alias for `visible_when`. */
308
+ visibleWhen?: VisibleWhen
298
309
  }
299
310
 
300
311
  /**
@@ -317,6 +328,21 @@ export interface ActionCondition {
317
328
  value: string | string[]
318
329
  }
319
330
 
331
+ /**
332
+ * Conditional-visibility predicate for a create/edit form field. Mirrors the
333
+ * kernel v3 `visible_when` block (json `visible_when`): the owning field is
334
+ * rendered only when the SIBLING field named by `field` holds a value that
335
+ * matches — either equal to `equals` (exact string) OR a member of `in`
336
+ * (any-of). When both are set `in` wins. Absent = the field is always visible
337
+ * (retrocompat). Evaluated by `evaluateVisibleWhen` against the live form
338
+ * values; a hidden field never gates submit (its required-check is skipped).
339
+ */
340
+ export interface VisibleWhen {
341
+ field: string
342
+ equals?: string
343
+ in?: string[]
344
+ }
345
+
320
346
  // Mirrors `ValidationRule` from packages/sdk/src/generated/manifest.ts. Kept
321
347
  // inline here so runtime-react does not import generated kernel types directly
322
348
  // — apps and addons author ActionFieldDef literals.
@@ -493,6 +519,16 @@ export interface ActionFieldDef {
493
519
  storagePath?: string
494
520
  /** snake_case alias served by the kernel manifest for `storagePath`. */
495
521
  storage_path?: string
522
+ /**
523
+ * Conditional visibility: render this field only when a sibling field's
524
+ * current value matches the predicate. Mirrors the kernel v3
525
+ * `ActionField.visible_when`. Tolerates the camelCase alias. Absent = always
526
+ * visible; a hidden field is skipped by the required-gate so it never blocks
527
+ * submit. See `evaluateVisibleWhen`.
528
+ */
529
+ visible_when?: VisibleWhen
530
+ /** camelCase alias for `visible_when`. */
531
+ visibleWhen?: VisibleWhen
496
532
  }
497
533
 
498
534
  /**