@asteby/metacore-runtime-react 28.4.1 → 28.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/types.d.ts CHANGED
@@ -282,6 +282,12 @@ export interface ColumnDefinition {
282
282
  visible_when?: VisibleWhen;
283
283
  /** camelCase alias for `visible_when`. */
284
284
  visibleWhen?: VisibleWhen;
285
+ /**
286
+ * Form-layout membership: the key of the `form_layout` section this column's
287
+ * modal field belongs to (kernel PR #230). Absent → the default group. See
288
+ * `groupFieldsBySection`.
289
+ */
290
+ section?: string;
285
291
  }
286
292
  /**
287
293
  * One declared sub-field of a jsonb line-items column (see
@@ -477,6 +483,12 @@ export interface ActionFieldDef {
477
483
  visible_when?: VisibleWhen;
478
484
  /** camelCase alias for `visible_when`. */
479
485
  visibleWhen?: VisibleWhen;
486
+ /**
487
+ * Form-layout membership: the key of the `form_layout` section this field
488
+ * belongs to (kernel PR #230). Absent → the field lands in the default
489
+ * group. See `groupFieldsBySection`.
490
+ */
491
+ section?: string;
480
492
  }
481
493
  /**
482
494
  * 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":"AAGA,MAAM,WAAW,aAAa;IAC1B,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,gBAAgB,EAAE,CAAA;IAC3B,OAAO,EAAE,gBAAgB,EAAE,CAAA;IAC3B,OAAO,CAAC,EAAE,gBAAgB,EAAE,CAAA;IAC5B,cAAc,EAAE,MAAM,EAAE,CAAA;IACxB,cAAc,EAAE,MAAM,CAAA;IACtB,iBAAiB,EAAE,MAAM,CAAA;IACzB,iBAAiB,EAAE,OAAO,CAAA;IAC1B,UAAU,EAAE,OAAO,CAAA;IACnB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,YAAY,EAAE,CAAA;IAC1B;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAA;IAC9C;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,SAAS,EAAE,CAAA;IACpB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,aAAa,EAAE,CAAA;IAC7B;;;;;OAKG;IACH,WAAW,CAAC,EAAE,eAAe,EAAE,CAAA;CAClC;AAED;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACtB,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IACxD;;;;;OAKG;IACH,QAAQ,CAAC,EAAE;QACP,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,OAAO,CAAC,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,EAAE,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,EAAE,CAAA;KAC3D,CAAA;CACJ;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC1B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAC1D;AAED,4EAA4E;AAC5E,MAAM,WAAW,eAAe;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,MAAM,CAAA;CACb;AAED;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IACzB,4EAA4E;IAC5E,IAAI,EAAE,MAAM,CAAA;IACZ,kEAAkE;IAClE,IAAI,EAAE,aAAa,GAAG,cAAc,CAAA;IACpC;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAA;IACf,sDAAsD;IACtD,WAAW,EAAE,MAAM,CAAA;IACnB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC9B,mCAAmC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,sCAAsC;IACtC,QAAQ,CAAC,EAAE,OAAO,CAAA;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC7B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb;;;;;OAKG;IACH,IAAI,EAAE,QAAQ,GAAG,gBAAgB,GAAG,SAAS,GAAG,YAAY,GAAG,cAAc,GAAG,MAAM,CAAA;IACtF,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IACrF,cAAc,CAAC,EAAE,MAAM,CAAA;CAC1B;AAED;;;;;;;;;GASG;AACH,MAAM,MAAM,gBAAgB,GAAG,KAAK,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAA;AAEjF,MAAM,WAAW,gBAAgB;IAC7B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EACE,MAAM,GACN,QAAQ,GACR,MAAM,GAGN,UAAU,GACV,WAAW,GACX,aAAa,GACb,QAAQ,GACR,QAAQ,GACR,qBAAqB,GACrB,QAAQ,GACR,SAAS,GACT,OAAO,GACP,eAAe,GACf,OAAO,GAEP,KAAK,GACL,MAAM,GACN,OAAO,GACP,UAAU,GACV,SAAS,GACT,UAAU,GACV,OAAO,GACP,QAAQ,GACR,MAAM,GACN,OAAO,GACP,MAAM,GACN,eAAe,GACf,SAAS,GACT,MAAM,GAKN,UAAU,CAAA;IAChB,QAAQ,EAAE,OAAO,CAAA;IACjB,UAAU,EAAE,OAAO,CAAA;IACnB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,QAAQ,GAAG,gBAAgB,GAAG,SAAS,GAAG,YAAY,GAAG,cAAc,GAAG,MAAM,CAAA;IAC7F,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB;;;;OAIG;IACH,UAAU,CAAC,EAAE,gBAAgB,CAAA;IAC7B;;;;;OAKG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACjC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,OAAO,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAC3E;;;;;;OAMG;IACH,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ;;;;OAIG;IACH,UAAU,CAAC,EAAE,eAAe,CAAA;IAC5B;;;;;;;;;OASG;IACH,UAAU,CAAC,EAAE,eAAe,EAAE,CAAA;IAC9B,8DAA8D;IAC9D,WAAW,CAAC,EAAE,eAAe,EAAE,CAAA;IAC/B;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,WAAW,CAAA;IAC1B,0CAA0C;IAC1C,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC5B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,GAAG,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,eAAe;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,IAAI,GAAG,KAAK,GAAG,IAAI,GAAG,QAAQ,CAAA;IACxC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;CAC3B;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,WAAW;IACxB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,EAAE,CAAC,EAAE,MAAM,EAAE,CAAA;CAChB;AASD,MAAM,WAAW,eAAe;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,MAAM,CAAA;CAClB;AAID,MAAM,MAAM,WAAW,GACjB,MAAM,GACN,UAAU,GACV,UAAU,GACV,OAAO,GACP,QAAQ,GACR,MAAM,GACN,QAAQ,GACR,gBAAgB,GAChB,QAAQ,GACR,QAAQ,GACR,MAAM,CAAA;AAEZ;;;;;;;GAOG;AACH,MAAM,WAAW,UAAU;IACvB,8EAA8E;IAC9E,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,wEAAwE;IACxE,EAAE,CAAC,EAAE,MAAM,EAAE,CAAA;IACb,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;IAChB,kEAAkE;IAClE,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;CACpB;AAED;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,UAAU,CAAA;CACpB;AAED,MAAM,WAAW,cAAc;IAC3B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,OAAO,CAAC,EAAE,SAAS,EAAE,CAAA;IACrB,YAAY,CAAC,EAAE,GAAG,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,UAAU,CAAC,EAAE,eAAe,CAAA;IAC5B,MAAM,CAAC,EAAE,WAAW,GAAG,MAAM,CAAA;IAC7B;;;;OAIG;IACH,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;;;;;OAQG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;;;;;OAQG;IACH,aAAa,CAAC,EAAE,kBAAkB,CAAA;IAClC,0EAA0E;IAC1E,cAAc,CAAC,EAAE,kBAAkB,CAAA;IACnC;;;;;;;;OAQG;IACH,UAAU,CAAC,EAAE,cAAc,EAAE,CAAA;IAC7B;;;;;OAKG;IACH,KAAK,CAAC,EAAE,OAAO,CAAA;IACf;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,gBAAgB,CAAA;IAC1B;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,oEAAoE;IACpE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,wEAAwE;IACxE,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,WAAW,CAAA;IAC1B,0CAA0C;IAC1C,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,sDAAsD;IACtD,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,sDAAsD;IACtD,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,0EAA0E;IAC1E,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,eAAe,CAAC,EAAE,OAAO,CAAA;CAC5B;AAED;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IAC/B,uEAAuE;IACvE,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,uEAAuE;IACvE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,+EAA+E;IAC/E,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,yEAAyE;IACzE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,gCAAgC;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,qDAAqD;IACrD,KAAK,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC7B,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAA;IACpD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,SAAS,CAAC,EAAE,eAAe,CAAA;IAC3B,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,MAAM,CAAC,EAAE,cAAc,EAAE,CAAA;IACzB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAA;CACzC;AAED,MAAM,WAAW,WAAW,CAAC,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAA;IAChB,IAAI,EAAE,CAAC,CAAA;IACP,IAAI,CAAC,EAAE,cAAc,CAAA;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,cAAc;IAC3B,YAAY,EAAE,MAAM,CAAA;IACpB,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;CAChB;AAKD,8EAA8E;AAC9E,MAAM,WAAW,UAAU;IACvB,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,EAAE,cAAc,EAAE,CAAA;CAC3B;AAED,MAAM,WAAW,cAAc;IAC3B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,MAAM,CAAC,EAAE,cAAc,EAAE,CAAA;IACzB,4EAA4E;IAC5E,KAAK,CAAC,EAAE,UAAU,EAAE,CAAA;IACpB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,SAAS,CAAC,EAAE,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAA;CACzC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@asteby/metacore-runtime-react",
3
- "version": "28.4.1",
3
+ "version": "28.5.0",
4
4
  "description": "React runtime for metacore hosts — renders addon contributions dynamically",
5
5
  "repository": {
6
6
  "type": "git",
@@ -0,0 +1,78 @@
1
+ // DynamicRecordDialog payload contract: a field hidden by its `visible_when`
2
+ // predicate must NOT be POSTed. A DiscountRule with rule_scope=category shows
3
+ // only category_id; the product_id / customer_id it isn't showing must be
4
+ // stripped from the create payload. `stripHiddenFieldValues` is the pure helper
5
+ // the dialog runs just before `normalizeRefFieldsForSubmit`, mirroring
6
+ // dynamic-form.tsx, which builds its Zod schema only over the visible fields.
7
+ import { describe, expect, it } from 'vitest'
8
+ import { stripHiddenFieldValues } from '../dialogs/dynamic-record'
9
+
10
+ // Modal fields for a DiscountRule: a `rule_scope` discriminator gates the
11
+ // product / customer / category reference fields via visible_when.
12
+ const fields = [
13
+ { key: 'name', label: 'Nombre', type: 'text', required: true },
14
+ { key: 'rule_scope', label: 'Alcance', type: 'text', required: true },
15
+ {
16
+ key: 'product_id',
17
+ label: 'Producto',
18
+ type: 'text',
19
+ visible_when: { field: 'rule_scope', equals: 'product' },
20
+ },
21
+ {
22
+ key: 'customer_id',
23
+ label: 'Cliente',
24
+ type: 'text',
25
+ visible_when: { field: 'rule_scope', equals: 'customer' },
26
+ },
27
+ {
28
+ key: 'category_id',
29
+ label: 'Categoría',
30
+ type: 'text',
31
+ visible_when: { field: 'rule_scope', equals: 'category' },
32
+ },
33
+ ] as any
34
+
35
+ describe('stripHiddenFieldValues (visible_when submit parity)', () => {
36
+ it('excludes fields hidden by visible_when from the create payload', () => {
37
+ // Scope=category → only category_id visible; product_id / customer_id are
38
+ // hidden and must be dropped even though they carry a value.
39
+ const out = stripHiddenFieldValues(
40
+ {
41
+ name: 'Promo',
42
+ rule_scope: 'category',
43
+ category_id: 'cat-1',
44
+ product_id: 'prod-1',
45
+ customer_id: 'cust-1',
46
+ },
47
+ fields,
48
+ 'create',
49
+ )
50
+
51
+ expect(out.category_id).toBe('cat-1')
52
+ expect(out.rule_scope).toBe('category')
53
+ expect(out.name).toBe('Promo')
54
+ expect('product_id' in out).toBe(false)
55
+ expect('customer_id' in out).toBe(false)
56
+ })
57
+
58
+ it('keeps the matching branch field for a different scope', () => {
59
+ const out = stripHiddenFieldValues(
60
+ { rule_scope: 'product', product_id: 'prod-1', category_id: 'cat-1' },
61
+ fields,
62
+ 'create',
63
+ )
64
+ expect(out.product_id).toBe('prod-1')
65
+ expect('category_id' in out).toBe(false)
66
+ })
67
+
68
+ it('never drops fields that declare no visible_when', () => {
69
+ const out = stripHiddenFieldValues(
70
+ { name: '', rule_scope: '', extra: 'x' },
71
+ fields,
72
+ 'create',
73
+ )
74
+ // name has no visible_when → kept; extra has no declared field → kept.
75
+ expect('name' in out).toBe(true)
76
+ expect(out.extra).toBe('x')
77
+ })
78
+ })
@@ -0,0 +1,113 @@
1
+ // Declarative form_layout grouping contract (kernel PR #230). The two renderers
2
+ // (dynamic-form.tsx, dialogs/dynamic-record.tsx) both drive their sections /
3
+ // wizard steps off `groupFieldsBySection`, so — following PR #673 — we test the
4
+ // pure grouping helper directly instead of booting the full modal under
5
+ // happy-dom (which hangs on the async metadata/options fetches).
6
+ import { describe, expect, it } from 'vitest'
7
+ import {
8
+ groupFieldsBySection,
9
+ DEFAULT_SECTION_KEY,
10
+ type FormLayout,
11
+ } from '../form-layout'
12
+
13
+ interface F {
14
+ key: string
15
+ section?: string
16
+ }
17
+
18
+ const layout: FormLayout = {
19
+ mode: 'sections',
20
+ sections: [
21
+ { key: 'general', title: 'General' },
22
+ { key: 'billing', title: 'Facturación', collapsed: true },
23
+ { key: 'notes', title: 'Notas' }, // will end up empty → hidden
24
+ ],
25
+ }
26
+
27
+ describe('groupFieldsBySection — sections', () => {
28
+ it('groups visible fields by section respecting the sections order', () => {
29
+ const fields: F[] = [
30
+ { key: 'tax_id', section: 'billing' },
31
+ { key: 'name', section: 'general' },
32
+ { key: 'email', section: 'general' },
33
+ { key: 'currency', section: 'billing' },
34
+ ]
35
+ const groups = groupFieldsBySection(fields, layout)
36
+
37
+ // No orphans → no default group; sections in authored order; the empty
38
+ // `notes` section is omitted entirely.
39
+ expect(groups.map(g => g.key)).toEqual(['general', 'billing'])
40
+ expect(groups[0].fields.map(f => f.key)).toEqual(['name', 'email'])
41
+ expect(groups[1].fields.map(f => f.key)).toEqual(['tax_id', 'currency'])
42
+ // `collapsed` is carried through so the chrome can start collapsed.
43
+ expect(groups[1].collapsed).toBe(true)
44
+ })
45
+
46
+ it('puts section-less and unknown-section fields in a default group placed first', () => {
47
+ const fields: F[] = [
48
+ { key: 'name', section: 'general' },
49
+ { key: 'orphan' }, // no section
50
+ { key: 'stray', section: 'does_not_exist' }, // unknown section
51
+ ]
52
+ const groups = groupFieldsBySection(fields, layout)
53
+
54
+ expect(groups[0].key).toBe(DEFAULT_SECTION_KEY)
55
+ expect(groups[0].isDefault).toBe(true)
56
+ expect(groups[0].fields.map(f => f.key)).toEqual(['orphan', 'stray'])
57
+ expect(groups[1].key).toBe('general')
58
+ })
59
+
60
+ it('hides a section whose only members are filtered out (visible_when)', () => {
61
+ // The caller passes the ALREADY visibility-filtered list, so a section
62
+ // left with no fields must not render an empty shell / empty step.
63
+ const fields: F[] = [{ key: 'name', section: 'general' }]
64
+ const groups = groupFieldsBySection(fields, layout)
65
+ expect(groups.map(g => g.key)).toEqual(['general'])
66
+ })
67
+
68
+ it('without a layout returns a single default group carrying every field in order', () => {
69
+ const fields: F[] = [{ key: 'a' }, { key: 'b' }, { key: 'c', section: 'general' }]
70
+ const groups = groupFieldsBySection(fields, undefined)
71
+ expect(groups).toHaveLength(1)
72
+ expect(groups[0].key).toBe(DEFAULT_SECTION_KEY)
73
+ expect(groups[0].isDefault).toBe(true)
74
+ expect(groups[0].fields.map(f => f.key)).toEqual(['a', 'b', 'c'])
75
+ })
76
+ })
77
+
78
+ describe('groupFieldsBySection — steps', () => {
79
+ const stepsLayout: FormLayout = {
80
+ mode: 'steps',
81
+ sections: [
82
+ { key: 'who', title: 'Cliente' },
83
+ { key: 'what', title: 'Productos' },
84
+ { key: 'pay', title: 'Pago' },
85
+ ],
86
+ }
87
+
88
+ it('yields one group per non-empty step in order, ready for wizard navigation', () => {
89
+ const fields: F[] = [
90
+ { key: 'amount', section: 'pay' },
91
+ { key: 'customer_id', section: 'who' },
92
+ { key: 'items', section: 'what' },
93
+ ]
94
+ const groups = groupFieldsBySection(fields, stepsLayout)
95
+
96
+ // Ordered as authored → step 0 = who, last = pay, so a wizard advances
97
+ // who → what → pay and submits on `pay`.
98
+ expect(groups.map(g => g.key)).toEqual(['who', 'what', 'pay'])
99
+ const last = groups.length - 1
100
+ expect(groups[last].key).toBe('pay')
101
+ expect(groups[last].title).toBe('Pago')
102
+ })
103
+
104
+ it('drops an empty step so the wizard never stops on a blank page', () => {
105
+ const fields: F[] = [
106
+ { key: 'customer_id', section: 'who' },
107
+ { key: 'amount', section: 'pay' },
108
+ ]
109
+ const groups = groupFieldsBySection(fields, stepsLayout)
110
+ // `what` has no visible field → omitted; wizard is who → pay.
111
+ expect(groups.map(g => g.key)).toEqual(['who', 'pay'])
112
+ })
113
+ })
@@ -54,6 +54,8 @@ import { DynamicRelations } from '../dynamic-relations'
54
54
  import { useOptionsResolver, type ResolvedOption } from '../use-options-resolver'
55
55
  import { getFieldRef, getVisibleWhen, evaluateVisibleWhen } from '../dynamic-form-schema'
56
56
  import type { VisibleWhen } from '../types'
57
+ import { groupFieldsBySection, type FormLayout } from '../form-layout'
58
+ import { FieldSection, WizardProgress } from '../form-layout-ui'
57
59
  import { FieldCell } from '../field-grid'
58
60
  import { isNilUuid, normalizeNilUuid } from '../nil-uuid'
59
61
  import { normalizeRefFieldsForSubmit } from './normalize-submit'
@@ -158,6 +160,12 @@ export interface FieldDef {
158
160
  visible_when?: VisibleWhen
159
161
  /** camelCase alias for `visible_when`. */
160
162
  visibleWhen?: VisibleWhen
163
+ /**
164
+ * Form-layout membership: the key of the `form_layout` section this field
165
+ * belongs to (kernel PR #230). Absent → the default group. See
166
+ * `groupFieldsBySection`.
167
+ */
168
+ section?: string
161
169
  }
162
170
 
163
171
  // Permissive shape: the wire payload may omit some fields (e.g. `title` is
@@ -175,6 +183,15 @@ interface ModalMetadata {
175
183
  createTitle?: string
176
184
  editTitle?: string
177
185
  fields?: FieldDef[]
186
+ /**
187
+ * Declarative form layout (kernel PR #230): groups the fields into named
188
+ * sections rendered stacked (`mode:"sections"`) or as a wizard
189
+ * (`mode:"steps"`). Absent → the legacy flat two-column grid. Tolerates the
190
+ * camelCase alias an app might author.
191
+ */
192
+ form_layout?: FormLayout
193
+ /** camelCase alias for `form_layout`. */
194
+ formLayout?: FormLayout
178
195
  /**
179
196
  * Backend-localized CRUD success messages (modal metadata). Preferred over
180
197
  * the raw response message which is not localized.
@@ -490,6 +507,29 @@ export function filterVisibleFields(
490
507
  })
491
508
  }
492
509
 
510
+ // stripHiddenFieldValues drops from a flat form-values object the keys of any
511
+ // declared field currently hidden by its `visible_when` predicate, so the
512
+ // submit never POSTs a value the form isn't showing (e.g. a DiscountRule with
513
+ // rule_scope=category must not send the product_id / customer_id it hides).
514
+ // This mirrors dynamic-form.tsx, which builds its Zod schema only over the
515
+ // visible fields — hidden fields are dropped from BOTH the render/required-gate
516
+ // AND the submitted values. Keys with no matching declared field, or whose
517
+ // field carries no `visible_when`, always pass through (retrocompat).
518
+ export function stripHiddenFieldValues(
519
+ values: Record<string, any>,
520
+ fields: FieldDef[] | undefined,
521
+ mode: 'view' | 'edit' | 'create',
522
+ ): Record<string, any> {
523
+ const visibleKeys = new Set(filterVisibleFields(fields, mode, values).map(f => f.key))
524
+ const out: Record<string, any> = {}
525
+ for (const [key, value] of Object.entries(values)) {
526
+ const field = (fields ?? []).find(f => f.key === key)
527
+ if (field && !visibleKeys.has(key) && getVisibleWhen(field)) continue
528
+ out[key] = value
529
+ }
530
+ return out
531
+ }
532
+
493
533
  export function DynamicRecordDialog({
494
534
  open,
495
535
  onOpenChange,
@@ -526,6 +566,8 @@ export function DynamicRecordDialog({
526
566
  const [loading, setLoading] = useState(false)
527
567
  const [saving, setSaving] = useState(false)
528
568
  const [deleting, setDeleting] = useState(false)
569
+ // Wizard step cursor (form_layout mode:"steps" only).
570
+ const [stepIndex, setStepIndex] = useState(0)
529
571
 
530
572
  const isCreate = mode === 'create'
531
573
  const isView = mode === 'view'
@@ -537,8 +579,10 @@ export function DynamicRecordDialog({
537
579
  if (!open) return
538
580
  if (!isCreate && !recordId) return
539
581
 
540
- // Fresh open → drop any validation errors from a prior submit.
582
+ // Fresh open → drop any validation errors from a prior submit and reset
583
+ // the wizard to its first step.
541
584
  setFieldErrors({})
585
+ setStepIndex(0)
542
586
 
543
587
  let cancelled = false
544
588
 
@@ -752,9 +796,16 @@ export function DynamicRecordDialog({
752
796
  // Required check passed → clear any prior validation errors.
753
797
  setFieldErrors({})
754
798
 
799
+ // Fields hidden by their `visible_when` predicate must not be POSTed:
800
+ // the render, the required-gate and the payload all drive off the same
801
+ // filter, so a DiscountRule with scope=category never submits the
802
+ // product_id / customer_id it isn't showing. Mirrors dynamic-form.tsx,
803
+ // which builds its Zod only over visibleFields.
804
+ const submittedValues = stripHiddenFieldValues(formValues, modalMeta.fields, mode)
805
+
755
806
  // Empty reference pickers → null (not "" / nil-UUID) so nullable FK
756
807
  // columns accept them instead of raising a 23503 FK violation.
757
- const payload = normalizeRefFieldsForSubmit(formValues, modalMeta.fields)
808
+ const payload = normalizeRefFieldsForSubmit(submittedValues, modalMeta.fields)
758
809
 
759
810
  setSaving(true)
760
811
  try {
@@ -829,6 +880,65 @@ export function DynamicRecordDialog({
829
880
 
830
881
  const visibleFields = filterVisibleFields(modalMeta?.fields, mode, formValues)
831
882
 
883
+ // Declarative form layout: group the (already visibility-filtered) fields by
884
+ // their section. Empty sections drop out for free. Steps mode only drives a
885
+ // wizard in editable modes — view mode always stacks the sections.
886
+ const formLayout = modalMeta?.form_layout ?? modalMeta?.formLayout
887
+ const groups = groupFieldsBySection(visibleFields, formLayout)
888
+ const isSteps = isEditable && formLayout?.mode === 'steps' && groups.length > 1
889
+ const clampedStep = Math.min(stepIndex, Math.max(groups.length - 1, 0))
890
+ const isLastStep = clampedStep === groups.length - 1
891
+
892
+ // Renders a list of fields into the shared two-column grid (each FieldCell
893
+ // gives min-w-0 so long values can't blow the columns past the dialog).
894
+ const renderFields = (groupFields: FieldDef[]) =>
895
+ groupFields.map(field => {
896
+ const isFullWidth =
897
+ field.type === 'textarea' ||
898
+ field.widget === 'textarea' ||
899
+ field.widget === 'richtext'
900
+ return (
901
+ <FieldCell key={field.key} fullWidth={isFullWidth}>
902
+ <FieldRow
903
+ field={field}
904
+ record={record}
905
+ value={formValues[field.key] ?? ''}
906
+ mode={mode}
907
+ error={fieldErrors[field.key]}
908
+ onChange={val => {
909
+ setFormValues((prev: Record<string, any>) => ({ ...prev, [field.key]: val }))
910
+ setFieldErrors(prev => {
911
+ if (!prev[field.key]) return prev
912
+ const next = { ...prev }
913
+ delete next[field.key]
914
+ return next
915
+ })
916
+ }}
917
+ />
918
+ </FieldCell>
919
+ )
920
+ })
921
+
922
+ // Wizard "Siguiente": gate only the CURRENT step's required (visible) fields,
923
+ // then advance. Mirrors handleSubmit's required check but scoped to the step.
924
+ const goNextStep = () => {
925
+ const step = groups[clampedStep]
926
+ const missing: Record<string, string> = {}
927
+ for (const field of step?.fields ?? []) {
928
+ if (field.required && !formValues[field.key] && formValues[field.key] !== 0 && formValues[field.key] !== false) {
929
+ missing[field.key] = localizeFieldIssue({ code: 'required' }, field.label, t)
930
+ }
931
+ }
932
+ if (Object.keys(missing).length) {
933
+ setFieldErrors(missing)
934
+ toast.error(t('dynamic.validation_failed', { defaultValue: 'Revisa los campos marcados' }))
935
+ return
936
+ }
937
+ setFieldErrors({})
938
+ setStepIndex(Math.min(clampedStep + 1, groups.length - 1))
939
+ }
940
+ const goBackStep = () => setStepIndex(Math.max(clampedStep - 1, 0))
941
+
832
942
  return (
833
943
  <Dialog open={open} onOpenChange={onOpenChange}>
834
944
  <DialogContent className="sm:max-w-2xl max-h-[90vh] flex flex-col p-0 gap-0 overflow-hidden">
@@ -845,54 +955,42 @@ export function DynamicRecordDialog({
845
955
  <ImageUrlContext.Provider value={getImageUrl}>
846
956
  <TimeZoneContext.Provider value={timeZone}>
847
957
  <CurrencyContext.Provider value={currency}>
848
- {/* The grid IS the form element (the footer submit
849
- button targets it by id). FieldCell gives each
958
+ {/* The form element groups its fields by the declared
959
+ form_layout (sections stacked, or the current
960
+ wizard step). Without a layout this is a single
961
+ default group rendered with no chrome — the legacy
962
+ two-column grid, unchanged. FieldCell gives each
850
963
  cell `min-w-0` so a long select/input value can't
851
964
  blow the two columns past the dialog width. */}
852
965
  <form
853
966
  id="dynamic-record-form"
854
967
  onSubmit={handleSubmit}
855
- className="grid grid-cols-1 gap-x-6 gap-y-4 sm:grid-cols-2"
968
+ className="grid gap-y-4"
856
969
  >
857
- {visibleFields.map(field => {
858
- const isFullWidth =
859
- field.type === 'textarea' ||
860
- field.widget === 'textarea' ||
861
- field.widget === 'richtext'
862
- return (
863
- <FieldCell key={field.key} fullWidth={isFullWidth}>
864
- <FieldRow
865
- field={field}
866
- record={record}
867
- value={formValues[field.key] ?? ''}
868
- mode={mode}
869
- error={fieldErrors[field.key]}
870
- onChange={val => {
871
- setFormValues((prev: Record<string, any>) => ({ ...prev, [field.key]: val }))
872
- // Clear this field's error as soon as the user edits it.
873
- setFieldErrors(prev => {
874
- if (!prev[field.key]) return prev
875
- const next = { ...prev }
876
- delete next[field.key]
877
- return next
878
- })
879
- }}
880
- />
881
- </FieldCell>
882
- )
883
- })}
970
+ {isSteps && (
971
+ <WizardProgress groups={groups} stepIndex={clampedStep} />
972
+ )}
973
+ {(isSteps ? [groups[clampedStep]] : groups).map(group => (
974
+ <FieldSection key={group.key} group={group}>
975
+ <div className="grid grid-cols-1 gap-x-6 gap-y-4 sm:grid-cols-2">
976
+ {renderFields(group.fields)}
977
+ </div>
978
+ </FieldSection>
979
+ ))}
884
980
 
885
981
  {record?.external_url && (
886
- <div className="sm:col-span-2 min-w-0">
887
- <a
888
- href={record.external_url}
889
- target="_blank"
890
- rel="noreferrer"
891
- className="inline-flex items-center gap-1.5 text-sm text-primary hover:underline mt-1"
892
- >
893
- <ExternalLink className="h-3.5 w-3.5" />
894
- Ver en {record.external_provider ?? 'proveedor externo'}
895
- </a>
982
+ <div className="grid grid-cols-1 sm:grid-cols-2">
983
+ <div className="sm:col-span-2 min-w-0">
984
+ <a
985
+ href={record.external_url}
986
+ target="_blank"
987
+ rel="noreferrer"
988
+ className="inline-flex items-center gap-1.5 text-sm text-primary hover:underline mt-1"
989
+ >
990
+ <ExternalLink className="h-3.5 w-3.5" />
991
+ Ver en {record.external_provider ?? 'proveedor externo'}
992
+ </a>
993
+ </div>
896
994
  </div>
897
995
  )}
898
996
  </form>
@@ -933,9 +1031,17 @@ export function DynamicRecordDialog({
933
1031
  </Button>
934
1032
  ) : <span />}
935
1033
  <div className="flex items-center gap-2">
936
- <Button variant="outline" onClick={() => onOpenChange(false)} disabled={saving || deleting}>
937
- {config.cancelLabel}
938
- </Button>
1034
+ {/* Wizard "Anterior" replaces Cancel from the second step
1035
+ on; the first step still shows Cancel. */}
1036
+ {isSteps && clampedStep > 0 ? (
1037
+ <Button variant="outline" onClick={goBackStep} disabled={saving || deleting}>
1038
+ Anterior
1039
+ </Button>
1040
+ ) : (
1041
+ <Button variant="outline" onClick={() => onOpenChange(false)} disabled={saving || deleting}>
1042
+ {config.cancelLabel}
1043
+ </Button>
1044
+ )}
939
1045
  {isView && onDelete && (
940
1046
  <Button
941
1047
  variant="destructive"
@@ -951,7 +1057,15 @@ export function DynamicRecordDialog({
951
1057
  Editar
952
1058
  </Button>
953
1059
  )}
954
- {isEditable && (
1060
+ {/* Non-final wizard step → "Siguiente" validates the step
1061
+ and advances instead of submitting. The last step (and
1062
+ every non-wizard form) keeps the real submit button. */}
1063
+ {isEditable && isSteps && !isLastStep && (
1064
+ <Button type="button" onClick={goNextStep} disabled={saving || loading}>
1065
+ Siguiente
1066
+ </Button>
1067
+ )}
1068
+ {isEditable && (!isSteps || isLastStep) && (
955
1069
  <Button
956
1070
  type="submit"
957
1071
  form="dynamic-record-form"