@asteby/metacore-runtime-react 47.0.0 → 48.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/dist/action-modal-dispatcher.d.ts.map +1 -1
  3. package/dist/action-modal-dispatcher.js +24 -4
  4. package/dist/audit-info.d.ts +54 -0
  5. package/dist/audit-info.d.ts.map +1 -0
  6. package/dist/audit-info.js +142 -0
  7. package/dist/business/catalog-product-search.d.ts +25 -0
  8. package/dist/business/catalog-product-search.d.ts.map +1 -0
  9. package/dist/business/catalog-product-search.js +57 -0
  10. package/dist/business/catalog.d.ts.map +1 -1
  11. package/dist/business/catalog.js +2 -0
  12. package/dist/business/document-editor-model.d.ts +107 -0
  13. package/dist/business/document-editor-model.d.ts.map +1 -0
  14. package/dist/business/document-editor-model.js +279 -0
  15. package/dist/business/document-editor-panels.d.ts +64 -0
  16. package/dist/business/document-editor-panels.d.ts.map +1 -0
  17. package/dist/business/document-editor-panels.js +58 -0
  18. package/dist/business/document-editor.d.ts +27 -0
  19. package/dist/business/document-editor.d.ts.map +1 -0
  20. package/dist/business/document-editor.js +445 -0
  21. package/dist/business/document-lines.d.ts.map +1 -1
  22. package/dist/business/document-lines.js +5 -1
  23. package/dist/business/index.d.ts +5 -0
  24. package/dist/business/index.d.ts.map +1 -1
  25. package/dist/business/index.js +5 -0
  26. package/dist/business/line-items.d.ts +7 -0
  27. package/dist/business/line-items.d.ts.map +1 -1
  28. package/dist/business/line-items.js +6 -1
  29. package/dist/business/payment-allocator.d.ts +19 -0
  30. package/dist/business/payment-allocator.d.ts.map +1 -0
  31. package/dist/business/payment-allocator.js +31 -0
  32. package/dist/business/product-search.d.ts +4 -0
  33. package/dist/business/product-search.d.ts.map +1 -1
  34. package/dist/dialogs/dynamic-record.d.ts.map +1 -1
  35. package/dist/dialogs/dynamic-record.js +19 -8
  36. package/dist/document-form-dialog.d.ts +31 -2
  37. package/dist/document-form-dialog.d.ts.map +1 -1
  38. package/dist/document-form-dialog.js +43 -11
  39. package/dist/dynamic-columns-helpers.d.ts +9 -0
  40. package/dist/dynamic-columns-helpers.d.ts.map +1 -1
  41. package/dist/dynamic-columns-helpers.js +19 -0
  42. package/dist/dynamic-columns.d.ts +8 -2
  43. package/dist/dynamic-columns.d.ts.map +1 -1
  44. package/dist/dynamic-columns.js +21 -5
  45. package/dist/dynamic-crud-page.d.ts +6 -0
  46. package/dist/dynamic-crud-page.d.ts.map +1 -1
  47. package/dist/dynamic-crud-page.js +14 -5
  48. package/dist/dynamic-form-schema.d.ts.map +1 -1
  49. package/dist/dynamic-form-schema.js +15 -3
  50. package/dist/dynamic-form.d.ts +16 -1
  51. package/dist/dynamic-form.d.ts.map +1 -1
  52. package/dist/dynamic-form.js +81 -20
  53. package/dist/dynamic-kanban.d.ts +9 -3
  54. package/dist/dynamic-kanban.d.ts.map +1 -1
  55. package/dist/dynamic-kanban.js +39 -13
  56. package/dist/dynamic-line-items.d.ts.map +1 -1
  57. package/dist/dynamic-line-items.js +3 -0
  58. package/dist/dynamic-multi-select-field.d.ts.map +1 -1
  59. package/dist/dynamic-multi-select-field.js +2 -0
  60. package/dist/dynamic-relation-helpers.d.ts.map +1 -1
  61. package/dist/dynamic-relation-helpers.js +8 -0
  62. package/dist/dynamic-relation.js +1 -1
  63. package/dist/dynamic-select-field.d.ts.map +1 -1
  64. package/dist/dynamic-select-field.js +4 -0
  65. package/dist/index.d.ts +8 -2
  66. package/dist/index.d.ts.map +1 -1
  67. package/dist/index.js +17 -2
  68. package/dist/installed-addons-context.d.ts +15 -0
  69. package/dist/installed-addons-context.d.ts.map +1 -0
  70. package/dist/installed-addons-context.js +39 -0
  71. package/dist/option-filter.d.ts +31 -0
  72. package/dist/option-filter.d.ts.map +1 -0
  73. package/dist/option-filter.js +49 -0
  74. package/dist/org-runtime-context.d.ts +8 -0
  75. package/dist/org-runtime-context.d.ts.map +1 -1
  76. package/dist/org-runtime-context.js +8 -0
  77. package/dist/org-runtime-provider.d.ts +3 -1
  78. package/dist/org-runtime-provider.d.ts.map +1 -1
  79. package/dist/org-runtime-provider.js +3 -3
  80. package/dist/primitives/aging.d.ts +34 -0
  81. package/dist/primitives/aging.d.ts.map +1 -0
  82. package/dist/primitives/aging.js +48 -0
  83. package/dist/primitives/allocation.d.ts +42 -0
  84. package/dist/primitives/allocation.d.ts.map +1 -0
  85. package/dist/primitives/allocation.js +59 -0
  86. package/dist/primitives/approvals.d.ts +71 -0
  87. package/dist/primitives/approvals.d.ts.map +1 -0
  88. package/dist/primitives/approvals.js +48 -0
  89. package/dist/primitives/contributions.d.ts +82 -0
  90. package/dist/primitives/contributions.d.ts.map +1 -0
  91. package/dist/primitives/contributions.js +126 -0
  92. package/dist/primitives/document-kinds.d.ts +25 -0
  93. package/dist/primitives/document-kinds.d.ts.map +1 -0
  94. package/dist/primitives/document-kinds.js +26 -0
  95. package/dist/primitives/index.d.ts +8 -0
  96. package/dist/primitives/index.d.ts.map +1 -0
  97. package/dist/primitives/index.js +23 -0
  98. package/dist/primitives/panels.d.ts +68 -0
  99. package/dist/primitives/panels.d.ts.map +1 -0
  100. package/dist/primitives/panels.js +3 -0
  101. package/dist/primitives/use-contributions.d.ts +15 -0
  102. package/dist/primitives/use-contributions.d.ts.map +1 -0
  103. package/dist/primitives/use-contributions.js +30 -0
  104. package/dist/record-prefill-bar.d.ts +11 -0
  105. package/dist/record-prefill-bar.d.ts.map +1 -0
  106. package/dist/record-prefill-bar.js +33 -0
  107. package/dist/types.d.ts +142 -0
  108. package/dist/types.d.ts.map +1 -1
  109. package/dist/use-options-resolver.d.ts +8 -0
  110. package/dist/use-options-resolver.d.ts.map +1 -1
  111. package/dist/use-options-resolver.js +8 -2
  112. package/package.json +5 -5
  113. package/src/__tests__/action-modal-grid-layout.test.tsx +1 -1
  114. package/src/__tests__/apply-sidebar-layout.test.ts +1 -1
  115. package/src/__tests__/audit-info.test.tsx +103 -0
  116. package/src/__tests__/catalog-product-search.test.ts +61 -0
  117. package/src/__tests__/dependent-options.test.tsx +12 -5
  118. package/src/__tests__/document-editor-model.test.ts +167 -0
  119. package/src/__tests__/document-editor.test.tsx +261 -0
  120. package/src/__tests__/document-form-dialog.test.tsx +118 -3
  121. package/src/__tests__/dynamic-form-select-edit.test.tsx +155 -0
  122. package/src/__tests__/dynamic-kanban.test.tsx +24 -3
  123. package/src/__tests__/dynamic-record-extension-label.test.tsx +88 -0
  124. package/src/__tests__/dynamic-table-infinite-scroll.test.tsx +4 -0
  125. package/src/__tests__/installed-addons-context.test.tsx +50 -0
  126. package/src/__tests__/line-item-totals.test.ts +28 -0
  127. package/src/__tests__/line-items-lock-rows.test.tsx +2 -1
  128. package/src/__tests__/option-filter.test.tsx +115 -0
  129. package/src/__tests__/primitives-federated.test.tsx +156 -0
  130. package/src/__tests__/primitives.test.ts +224 -0
  131. package/src/__tests__/record-prefill-bar.test.tsx +50 -0
  132. package/src/__tests__/structured-view-value.test.tsx +5 -1
  133. package/src/__tests__/wizard-action-modal.test.tsx +1 -1
  134. package/src/action-modal-dispatcher.tsx +47 -4
  135. package/src/audit-info.tsx +219 -0
  136. package/src/business/catalog-product-search.ts +87 -0
  137. package/src/business/catalog.ts +2 -0
  138. package/src/business/document-editor-model.ts +325 -0
  139. package/src/business/document-editor-panels.tsx +164 -0
  140. package/src/business/document-editor.tsx +727 -0
  141. package/src/business/document-lines.ts +5 -1
  142. package/src/business/index.ts +5 -0
  143. package/src/business/line-items.ts +15 -1
  144. package/src/business/payment-allocator.tsx +110 -0
  145. package/src/business/product-search.ts +4 -0
  146. package/src/dialogs/dynamic-record.tsx +42 -6
  147. package/src/document-form-dialog.tsx +92 -12
  148. package/src/dropdown-menu-entry.test.ts +23 -0
  149. package/src/dynamic-columns-helpers.ts +22 -0
  150. package/src/dynamic-columns.tsx +23 -6
  151. package/src/dynamic-crud-page.tsx +22 -3
  152. package/src/dynamic-form-schema.ts +15 -2
  153. package/src/dynamic-form.tsx +120 -39
  154. package/src/dynamic-kanban.tsx +41 -10
  155. package/src/dynamic-line-items.tsx +3 -0
  156. package/src/dynamic-multi-select-field.tsx +2 -0
  157. package/src/dynamic-relation-helpers.ts +9 -0
  158. package/src/dynamic-relation.tsx +7 -2
  159. package/src/dynamic-select-field.tsx +4 -0
  160. package/src/index.ts +34 -1
  161. package/src/installed-addons-context.tsx +58 -0
  162. package/src/option-filter.ts +88 -0
  163. package/src/org-runtime-context.ts +10 -0
  164. package/src/org-runtime-provider.tsx +7 -2
  165. package/src/primitives/aging.ts +76 -0
  166. package/src/primitives/allocation.ts +104 -0
  167. package/src/primitives/approvals.ts +103 -0
  168. package/src/primitives/contributions.ts +208 -0
  169. package/src/primitives/document-kinds.ts +62 -0
  170. package/src/primitives/index.ts +23 -0
  171. package/src/primitives/panels.ts +59 -0
  172. package/src/primitives/use-contributions.ts +42 -0
  173. package/src/record-prefill-bar.tsx +62 -0
  174. package/src/row-action-stage-gate.test.ts +33 -0
  175. package/src/types.ts +142 -0
  176. package/src/use-options-resolver.ts +19 -1
  177. package/vitest.config.ts +1 -0
  178. package/vitest.setup.ts +7 -0
@@ -97,14 +97,26 @@ export function toNumber(v) {
97
97
  * drift (0.1 + 0.2 noise) that would make a genuinely balanced entry look off.
98
98
  */
99
99
  export function computeLineItemTotals(field, rows) {
100
- const cols = getItemFields(field).filter((c) => c.total);
100
+ const itemFields = getItemFields(field);
101
+ const cols = itemFields.filter((c) => c.total);
102
+ // A `total` UNIT PRICE column on a grid with a quantity and no line amount
103
+ // column sums the line amounts (qty × price − discount): adding unit prices
104
+ // is meaningless once a quantity exists (2 × 750 footed 750, not 1500).
105
+ const qtyKey = firstKey(itemFields, LINE_QTY_KEYS);
106
+ const lineValued = !!qtyKey && !firstKey(itemFields, LINE_AMOUNT_KEYS);
107
+ const discountKey = firstKey(itemFields, LINE_DISCOUNT_KEYS);
101
108
  const totals = {};
102
109
  for (const c of cols)
103
110
  totals[c.key] = 0;
104
111
  if (Array.isArray(rows)) {
105
112
  for (const row of rows) {
106
- for (const c of cols)
107
- totals[c.key] += toNumber(row?.[c.key]);
113
+ for (const c of cols) {
114
+ const v = toNumber(row?.[c.key]);
115
+ totals[c.key] +=
116
+ lineValued && LINE_PRICE_KEYS.has(c.key)
117
+ ? toNumber(row?.[qtyKey]) * v - (discountKey ? toNumber(row?.[discountKey]) : 0)
118
+ : v;
119
+ }
108
120
  }
109
121
  }
110
122
  for (const k of Object.keys(totals))
@@ -25,6 +25,21 @@ export interface DynamicFormProps {
25
25
  * legacy flat list (unchanged).
26
26
  */
27
27
  formLayout?: FormLayout;
28
+ /**
29
+ * Para formularios dentro de un modal con alto acotado: el cuerpo de campos
30
+ * hace scroll propio y la barra de botones (Guardar/Cancelar) queda fija
31
+ * abajo, así nunca queda fuera de pantalla con muchos campos. El contenedor
32
+ * debe ser flex-col con alto máximo (p.ej. `max-h-[90dvh]`).
33
+ */
34
+ scrollableBody?: boolean;
28
35
  }
29
- export declare function DynamicForm({ fields, initialValues, onSubmit, onCancel, submitLabel, cancelLabel, disabled, formLayout, }: DynamicFormProps): import("react").JSX.Element;
36
+ export declare function DynamicForm({ fields, initialValues, onSubmit, onCancel, submitLabel, cancelLabel, disabled, formLayout, scrollableBody, }: DynamicFormProps): import("react").JSX.Element;
37
+ /**
38
+ * Radix Select puede emitir `onValueChange('')` sin interacción del usuario (su
39
+ * <select> nativo oculto colapsa a '' si recibe el valor antes que sus
40
+ * <option>s, p.ej. dentro de un <form> en el primer render). Ese '' pisaría el
41
+ * valor real del registro y viajaría en el PATCH. Se ignora cuando ya hay un
42
+ * valor y '' no es una opción válida — el usuario nunca puede elegir '' así.
43
+ */
44
+ export declare function guardEmptySelect(current: unknown, onChange: (v: any) => void, optionValues: readonly unknown[]): (v: string) => void;
30
45
  //# sourceMappingURL=dynamic-form.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dynamic-form.d.ts","sourceRoot":"","sources":["../src/dynamic-form.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAwB,KAAK,UAAU,EAAE,MAAM,eAAe,CAAA;AAerE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAC7C,OAAO,EACH,cAAc,EACd,aAAa,EAOhB,MAAM,uBAAuB,CAAA;AAY9B,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,uBAAuB,EAAE,MAAM,8BAA8B,CAAA;AACtE,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AACrD,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAEzF,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;IAClB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,UAAU,CAAA;CAC1B;AAED,wBAAgB,WAAW,CAAC,EACxB,MAAM,EACN,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,WAAuB,EACvB,WAAwB,EACxB,QAAgB,EAChB,UAAU,GACb,EAAE,gBAAgB,+BAwMlB"}
1
+ {"version":3,"file":"dynamic-form.d.ts","sourceRoot":"","sources":["../src/dynamic-form.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAwB,KAAK,UAAU,EAAE,MAAM,eAAe,CAAA;AAerE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAC7C,OAAO,EACH,cAAc,EACd,aAAa,EAOhB,MAAM,uBAAuB,CAAA;AAa9B,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,uBAAuB,EAAE,MAAM,8BAA8B,CAAA;AACtE,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AACrD,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAEzF,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;IAClB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,UAAU,CAAA;IACvB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;CAC3B;AAsBD,wBAAgB,WAAW,CAAC,EACxB,MAAM,EACN,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,WAAuB,EACvB,WAAwB,EACxB,QAAgB,EAChB,UAAU,EACV,cAAsB,GACzB,EAAE,gBAAgB,+BAiOlB;AA4PD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC5B,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,IAAI,EAC1B,YAAY,EAAE,SAAS,OAAO,EAAE,GACjC,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAMrB"}
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  // Minimal standalone DynamicForm. Factored from the dynamic-record-dialog
3
3
  // pattern + ActionFieldDef renderer so callers can reuse the form layout
4
4
  // outside the full record-edit modal.
5
- import { useEffect, useMemo, useState } from 'react';
5
+ import { useEffect, useMemo, useRef, useState } from 'react';
6
6
  import { groupFieldsBySection } from './form-layout';
7
7
  import { FieldSection, WizardProgress } from './form-layout-ui';
8
8
  import { AssistInterview } from './assist-interview';
@@ -10,6 +10,7 @@ import { Input, Textarea, Label, Switch, Button, Select, SelectContent, SelectIt
10
10
  import { buildZodSchema, resolveWidget, isLineItemsField, evaluateBalance, applyOptionWhen, getDependsOn, getVisibleWhen, evaluateVisibleWhen, } from './dynamic-form-schema';
11
11
  import { ScanLine } from 'lucide-react';
12
12
  import { BarcodeScanner } from './barcode-scanner';
13
+ import { getOptionFilter } from './option-filter';
13
14
  import { useOptionsResolver } from './use-options-resolver';
14
15
  import { DynamicLineItems } from './dynamic-line-items';
15
16
  import { DynamicSelectField } from './dynamic-select-field';
@@ -26,8 +27,24 @@ export { DynamicDateField } from './dynamic-date-field';
26
27
  export { UploadField } from './upload-field';
27
28
  export { IconPickerField } from './icon-picker-field';
28
29
  export { ColorPickerField, DEFAULT_ROLE_COLOR, normalizeHex } from './color-picker-field';
29
- export function DynamicForm({ fields, initialValues, onSubmit, onCancel, submitLabel = 'Guardar', cancelLabel = 'Cancelar', disabled = false, formLayout, }) {
30
- const [values, setValues] = useState({});
30
+ // Valores iniciales del formulario: el registro (edición) gana sobre el default
31
+ // del campo. Se calcula de forma síncrona en el primer render para que ningún
32
+ // widget (p.ej. un Select de Radix) monte con '' y emita un cambio espurio que
33
+ // pise el valor real del registro.
34
+ function seedValues(fields, initialValues) {
35
+ const defaults = {};
36
+ for (const f of fields) {
37
+ if (isLineItemsField(f)) {
38
+ defaults[f.key] = initialValues?.[f.key] ?? f.defaultValue ?? [];
39
+ continue;
40
+ }
41
+ defaults[f.key] = initialValues?.[f.key] ?? f.defaultValue ?? (f.type === 'boolean' ? false : '');
42
+ }
43
+ return defaults;
44
+ }
45
+ const isReadonlyField = (f) => !!f.readonly ||
46
+ !!f.readOnly;
47
+ export function DynamicForm({ fields, initialValues, onSubmit, onCancel, submitLabel = 'Guardar', cancelLabel = 'Cancelar', disabled = false, formLayout, scrollableBody = false, }) {
31
48
  const [errors, setErrors] = useState({});
32
49
  const [submitting, setSubmitting] = useState(false);
33
50
  // Readonly fields are system-owned (connection state, sync-written values):
@@ -35,7 +52,11 @@ export function DynamicForm({ fields, initialValues, onSubmit, onCancel, submitL
35
52
  // instead of rendered disabled, matching the metadata contract
36
53
  // (ColumnDef.Readonly). They are also excluded from defaults/submit so a
37
54
  // create never posts e.g. `status: ""` over the column default.
38
- const editableFields = useMemo(() => fields.filter((f) => !f.readonly), [fields]);
55
+ const editableFields = useMemo(() => fields.filter((f) => !isReadonlyField(f)), [fields]);
56
+ const [values, setValues] = useState(() => seedValues(editableFields, initialValues));
57
+ // Campos que el usuario (o un reset dependiente) cambió desde la siembra.
58
+ // En edición, un select que sigue vacío y nadie tocó no viaja en el PATCH.
59
+ const touched = useRef(new Set());
39
60
  // Conditional visibility: a field carrying `visible_when` is rendered — and
40
61
  // validated — only while the referenced sibling field's current value
41
62
  // matches the predicate. Driving BOTH the schema and the render off the same
@@ -68,19 +89,22 @@ export function DynamicForm({ fields, initialValues, onSubmit, onCancel, submitL
68
89
  useEffect(() => {
69
90
  setStepIndex((i) => Math.min(i, Math.max(groups.length - 1, 0)));
70
91
  }, [groups.length]);
92
+ // Re-siembra cuando cambia el registro o los campos DESPUÉS del montaje (el
93
+ // primer render ya quedó sembrado por el inicializador de useState).
94
+ const seededFrom = useRef({ editableFields, initialValues });
71
95
  useEffect(() => {
72
- const defaults = {};
73
- for (const f of editableFields) {
74
- if (isLineItemsField(f)) {
75
- defaults[f.key] = initialValues?.[f.key] ?? f.defaultValue ?? [];
76
- continue;
77
- }
78
- defaults[f.key] = initialValues?.[f.key] ?? f.defaultValue ?? (f.type === 'boolean' ? false : '');
79
- }
80
- setValues(defaults);
96
+ const prev = seededFrom.current;
97
+ if (prev.editableFields === editableFields && prev.initialValues === initialValues)
98
+ return;
99
+ seededFrom.current = { editableFields, initialValues };
100
+ touched.current = new Set();
101
+ setValues(seedValues(editableFields, initialValues));
81
102
  setErrors({});
82
103
  }, [editableFields, initialValues]);
83
- const update = (k, v) => setValues((prev) => ({ ...prev, [k]: v }));
104
+ const update = (k, v) => {
105
+ touched.current.add(k);
106
+ setValues((prev) => ({ ...prev, [k]: v }));
107
+ };
84
108
  const handleSubmit = async (e) => {
85
109
  e.preventDefault();
86
110
  if (balanceBlocked)
@@ -97,9 +121,23 @@ export function DynamicForm({ fields, initialValues, onSubmit, onCancel, submitL
97
121
  return;
98
122
  }
99
123
  setErrors({});
124
+ const data = { ...result.data };
125
+ // Edición: un select vacío que el usuario no tocó no se manda — el update
126
+ // es parcial, así que omitirlo conserva lo que tenga el registro en vez de
127
+ // pisarlo con ''.
128
+ if (initialValues) {
129
+ for (const f of visibleFields) {
130
+ if (resolveWidget(f) !== 'select')
131
+ continue;
132
+ if (touched.current.has(f.key))
133
+ continue;
134
+ if (data[f.key] === '' || data[f.key] == null)
135
+ delete data[f.key];
136
+ }
137
+ }
100
138
  setSubmitting(true);
101
139
  try {
102
- await onSubmit(result.data);
140
+ await onSubmit(data);
103
141
  }
104
142
  finally {
105
143
  setSubmitting(false);
@@ -109,6 +147,10 @@ export function DynamicForm({ fields, initialValues, onSubmit, onCancel, submitL
109
147
  // header fields flow through it; line-items grids (and textareas) span full
110
148
  // width so the row table / memo gets room. Shared by every layout mode.
111
149
  const renderGrid = (groupFields) => (_jsx("div", { className: "grid gap-4 sm:grid-cols-2", children: groupFields.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))) }));
150
+ // scrollableBody: el cuerpo hace scroll y el footer de botones queda fijo.
151
+ const formClass = scrollableBody ? 'flex min-h-0 flex-1 flex-col gap-4' : 'grid gap-4';
152
+ const bodyClass = scrollableBody ? '-mx-1 grid min-h-0 flex-1 gap-4 overflow-y-auto px-1' : 'grid gap-4';
153
+ const footerExtra = scrollableBody ? ' shrink-0 border-t' : '';
112
154
  // ── Steps (wizard) mode ────────────────────────────────────────────────
113
155
  // One step per section; Anterior/Siguiente navigate, submit only on the
114
156
  // last step. Advancing validates just the current step's fields so a later
@@ -133,7 +175,7 @@ export function DynamicForm({ fields, initialValues, onSubmit, onCancel, submitL
133
175
  setStepIndex((i) => Math.min(i + 1, groups.length - 1));
134
176
  };
135
177
  const goBack = () => setStepIndex((i) => Math.max(i - 1, 0));
136
- return (_jsxs("form", { onSubmit: handleSubmit, className: "grid gap-4", children: [_jsx(WizardProgress, { groups: groups, stepIndex: stepIndex, onStepClick: i => setStepIndex(i) }), step.assist ? (_jsx(AssistInterview, { assist: step.assist, values: values, eyebrow: step.title, autoStart: step.assist.trigger !== 'button', onApply: fields => setValues(prev => ({ ...prev, ...fields })) })) : (renderGrid(step.fields)), _jsxs("div", { className: "flex justify-between gap-2 pt-2", children: [stepIndex > 0 ? (_jsx(Button, { type: "button", variant: "outline", onClick: goBack, disabled: submitting || disabled, children: "Anterior" })) : onCancel ? (_jsx(Button, { type: "button", variant: "outline", onClick: onCancel, disabled: submitting || disabled, children: cancelLabel })) : (_jsx("span", {})), isLast ? (_jsx(Button, { type: "submit", disabled: submitting || disabled || balanceBlocked, children: submitLabel })) : (_jsx(Button, { type: "button", onClick: goNext, disabled: submitting || disabled, children: "Siguiente" }))] })] }));
178
+ return (_jsxs("form", { onSubmit: handleSubmit, className: formClass, children: [_jsxs("div", { className: bodyClass, children: [_jsx(WizardProgress, { groups: groups, stepIndex: stepIndex, onStepClick: i => setStepIndex(i) }), step.assist ? (_jsx(AssistInterview, { assist: step.assist, values: values, eyebrow: step.title, autoStart: step.assist.trigger !== 'button', onApply: fields => setValues(prev => ({ ...prev, ...fields })) })) : (renderGrid(step.fields))] }), _jsxs("div", { className: 'flex justify-between gap-2 pt-2' + footerExtra, children: [stepIndex > 0 ? (_jsx(Button, { type: "button", variant: "outline", onClick: goBack, disabled: submitting || disabled, children: "Anterior" })) : onCancel ? (_jsx(Button, { type: "button", variant: "outline", onClick: onCancel, disabled: submitting || disabled, children: cancelLabel })) : (_jsx("span", {})), isLast ? (_jsx(Button, { type: "submit", disabled: submitting || disabled || balanceBlocked, children: submitLabel })) : (_jsx(Button, { type: "button", onClick: goNext, disabled: submitting || disabled, children: "Siguiente" }))] })] }));
137
179
  }
138
180
  // ── Sections / flat mode ───────────────────────────────────────────────
139
181
  // Layout: scalar header fields flow through a responsive 2-column grid;
@@ -141,7 +183,7 @@ export function DynamicForm({ fields, initialValues, onSubmit, onCancel, submitL
141
183
  // memo gets room. Mirrors the pro look of the federated journal modal but
142
184
  // stays fully declarative — driven only by field shape. With no layout this
143
185
  // is a single default group rendered without section chrome (unchanged).
144
- return (_jsxs("form", { onSubmit: handleSubmit, className: "grid gap-4", children: [groups.map((group) => (_jsx(FieldSection, { group: group, children: renderGrid(group.fields) }, group.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 })] })] }));
186
+ return (_jsxs("form", { onSubmit: handleSubmit, className: formClass, children: [_jsx("div", { className: bodyClass, children: groups.map((group) => (_jsx(FieldSection, { group: group, children: renderGrid(group.fields) }, group.key))) }), _jsxs("div", { className: 'flex justify-end gap-2 pt-2' + footerExtra, 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 })] })] }));
145
187
  }
146
188
  // One form field row: label + renderer + inline error. Encapsulated as its own
147
189
  // component so a STATIC enum select whose options are all gated out by a sibling
@@ -242,8 +284,10 @@ function FieldRenderer({ field, value, onChange, initialValues, effectiveOptions
242
284
  return _jsx(Textarea, { id: field.key, "data-widget": "richtext", value: value || '', onChange: (e) => onChange(e.target.value), placeholder: field.placeholder });
243
285
  case 'color':
244
286
  return (_jsx(ColorPickerField, { "aria-label": field.label || field.key, value: typeof value === 'string' ? value : '', onChange: onChange }));
245
- case 'select':
246
- return (_jsxs(Select, { value: value || '', onValueChange: onChange, children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, { placeholder: field.placeholder || 'Seleccionar...' }) }), _jsx(SelectContent, { children: (effectiveOptions ?? field.options)?.map((opt) => _jsx(SelectItem, { value: opt.value, children: opt.label }, opt.value)) })] }));
287
+ case 'select': {
288
+ const opts = effectiveOptions ?? field.options;
289
+ return (_jsxs(Select, { value: value || '', onValueChange: guardEmptySelect(value, onChange, (opts ?? []).map((o) => o.value)), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, { placeholder: field.placeholder || 'Seleccionar...' }) }), _jsx(SelectContent, { children: opts?.map((opt) => _jsx(SelectItem, { value: opt.value, children: opt.label }, opt.value)) })] }));
290
+ }
247
291
  case 'switch':
248
292
  return _jsx(Switch, { id: field.key, checked: !!value, onCheckedChange: onChange });
249
293
  case 'number':
@@ -290,11 +334,28 @@ function ScannableInput({ field, value, onChange, type, }) {
290
334
  // (default) porque suma varios productos seguidos.
291
335
  continuous: false, position: "fixed", title: `Escanear ${field.label ?? ''}`.trim() })] }));
292
336
  }
337
+ /**
338
+ * Radix Select puede emitir `onValueChange('')` sin interacción del usuario (su
339
+ * <select> nativo oculto colapsa a '' si recibe el valor antes que sus
340
+ * <option>s, p.ej. dentro de un <form> en el primer render). Ese '' pisaría el
341
+ * valor real del registro y viajaría en el PATCH. Se ignora cuando ya hay un
342
+ * valor y '' no es una opción válida — el usuario nunca puede elegir '' así.
343
+ */
344
+ export function guardEmptySelect(current, onChange, optionValues) {
345
+ return (v) => {
346
+ const hasValue = current !== undefined && current !== null && current !== '';
347
+ if (v === '' && hasValue && !optionValues.some((o) => String(o) === ''))
348
+ return;
349
+ onChange(v);
350
+ };
351
+ }
293
352
  function RefSelect({ field, value, onChange }) {
294
353
  const { options, loading } = useOptionsResolver({
295
354
  modelKey: '', // unused — `ref` drives the URL
296
355
  fieldKey: 'id',
297
356
  ref: field.ref,
357
+ optionFilter: getOptionFilter(field),
358
+ keepValue: value,
298
359
  });
299
- return (_jsxs(Select, { value: value || '', onValueChange: onChange, disabled: loading, children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, { placeholder: loading ? 'Cargando…' : (field.placeholder || 'Seleccionar...') }) }), _jsx(SelectContent, { children: options.map((opt) => (_jsx(SelectItem, { value: String(opt.id), children: opt.label }, String(opt.id)))) })] }));
360
+ return (_jsxs(Select, { value: value || '', onValueChange: guardEmptySelect(value, onChange, options.map((o) => String(o.id))), disabled: loading, children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, { placeholder: loading ? 'Cargando…' : (field.placeholder || 'Seleccionar...') }) }), _jsx(SelectContent, { children: options.map((opt) => (_jsx(SelectItem, { value: String(opt.id), children: opt.label }, String(opt.id)))) })] }));
300
361
  }
@@ -91,9 +91,15 @@ export declare function isCardOverdue(card: any, col: ColumnDefinition, now?: nu
91
91
  * The stage machine (stages, transitions, smart lanes) belongs to the model's
92
92
  * stage column (`stage_field`; older hosts that do not serve it: the served
93
93
  * group_by): grouping by it keeps the machine, grouping by anything else drops
94
- * it and the lanes come from the new column. Returns the SAME object when there
95
- * is nothing to change (no override, the served group_by already, or a column the
96
- * model does not have). Pure — exported for unit tests.
94
+ * it and the lanes come from the new column.
95
+ *
96
+ * The served group_by is the LAST kanban nav entry's, which need not be the
97
+ * stage column: a board opened without `?group_by=` (the list's view toggle)
98
+ * groups by the stage column, and a board grouped by the served column keeps
99
+ * the machine only when that column IS the stage column. Otherwise the stage
100
+ * lanes were painted over another column (by bay: every card under «Sin etapa»
101
+ * and a drop wrote `service_bay_id = 'diagnosis'`, 422). Returns the SAME
102
+ * object when there is nothing to change. Pure — exported for unit tests.
97
103
  */
98
104
  export declare function withGroupBy(metadata: TableMetadata | null, groupBy: string | undefined): TableMetadata | null;
99
105
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"dynamic-kanban.d.ts","sourceRoot":"","sources":["../src/dynamic-kanban.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dynamic-kanban.d.ts","sourceRoot":"","sources":["../src/dynamic-kanban.tsx"],"names":[],"mappings":"AA0BA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAyH9B,OAAO,EAEH,qBAAqB,EACrB,qBAAqB,EACxB,MAAM,gBAAgB,CAAA;AAuBvB,OAAO,KAAK,EACR,aAAa,EACb,gBAAgB,EAGhB,SAAS,EACT,eAAe,EAClB,MAAM,SAAS,CAAA;AAIhB,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,CAAA;AAMvD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,aAAa,GAAG,SAAS,EAAE,CAejE;AAQD;;;;;GAKG;AACH,eAAO,MAAM,eAAe,mBAAmB,CAAA;AAE/C,wBAAgB,YAAY,CACxB,OAAO,EAAE,GAAG,EAAE,EACd,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,SAAS,EAAE,GACpB,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,CAepB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAC/B,WAAW,EAAE,eAAe,EAAE,GAAG,SAAS,EAC1C,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,EACV,SAAS,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,GAChC,OAAO,CAUT;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAC/B,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,EAC3B,MAAM,EAAE,MAAM,GAAG,MAAM,EACvB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,GACnB,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,CAYpB;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,SAAS;IAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EACpE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,EAC7B,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,GAChB,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAWnB;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAC3B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAAG,IAAI,EAC1B,UAAU,EAAE,OAAO,GACpB,MAAM,CAMR;AAQD;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAC7B,QAAQ,EAAE,aAAa,EACvB,SAAS,SAAI,GACd;IAAE,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAsBnF;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,gBAAgB,EAAE,GAAG,GAAE,MAAmB,GAAG,OAAO,CAcjG;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,WAAW,CACvB,QAAQ,EAAE,aAAa,GAAG,IAAI,EAC9B,OAAO,EAAE,MAAM,GAAG,SAAS,GAC5B,aAAa,GAAG,IAAI,CA2BtB;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CACzB,QAAQ,EAAE,aAAa,GAAG,IAAI,GAC/B;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAQ5C;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CACxB,QAAQ,EAAE,aAAa,EACvB,OAAO,EAAE;IAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EAAE,GAC5E,aAAa,CAWf;AAKD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,gBAAgB,GAAG,OAAO,CASvE;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,gBAAgB,GAAG,MAAM,CAarE;AAID;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAU1E;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC7B,IAAI,EAAE,GAAG,EACT,UAAU,EAAE,gBAAgB,EAAE,EAC9B,GAAG,SAAI,GACR,gBAAgB,EAAE,CAOpB;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAC/B,OAAO,EAAE,GAAG,EAAE,EACd,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,EAC7C,UAAU,EAAE,MAAM,GACnB,GAAG,EAAE,CAOP;AAED,oEAAoE;AACpE,MAAM,WAAW,SAAS;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC/B;AAQD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACnC,OAAO,EAAE,GAAG,EAAE,EACd,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,EAC7C,UAAU,EAAE,MAAM,EAClB,aAAa,GAAE,MAAM,EAAO,GAC7B,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAoB3B;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CACrC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAClD,IAAI,EAAE,GAAG,EACT,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,MAAM,EAAE,GACxB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAexC;AAED,mEAAmE;AACnE,MAAM,WAAW,QAAQ;IACrB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;CAChB;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAC/B,SAAS,EAAE,MAAM,GAAG,OAAO,EAC3B,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EACrD,KAAK,EAAE,QAAQ,EAAE,GAClB;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAYtC;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACjC,IAAI,EAAE,GAAG,EACT,MAAM,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,GACzE,OAAO,CAUT;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC3B,KAAK,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,GACxD,MAAM,CAIR;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAChC,IAAI,EAAE,GAAG,EACT,IAAI,EAAE,gBAAgB,EAAE,EACxB,KAAK,EAAE,MAAM,GACd,OAAO,CAUT;AAwDD,MAAM,WAAW,kBAAkB;IAC/B,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAA;IACb;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,kFAAkF;IAClF,cAAc,CAAC,EAAE,GAAG,CAAA;IACpB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,+DAA+D;IAC/D,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,CAAA;IAChC;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,IAAI,CAAA;IAC7C;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CACvC;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,kBAAkB,qBAetD"}
@@ -10,7 +10,14 @@ import { CSS } from '@dnd-kit/utilities';
10
10
  import { arrayMove } from '@dnd-kit/sortable';
11
11
  import { Calendar, CircleDot, GripVertical, Hash, ListFilter, MoreHorizontal, RotateCcw, Search, Settings2, Tag, ToggleLeft, Type, X, } from 'lucide-react';
12
12
  import { toast } from 'sonner';
13
- import { Badge, Button, Card, CardContent, DropdownMenu, DropdownMenuContent, DropdownMenuTrigger, Input, Popover, PopoverContent, PopoverTrigger, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger, Skeleton, } from '@asteby/metacore-ui/primitives';
13
+ import { Badge, Button, Card, CardContent, Input, Popover, PopoverContent, PopoverTrigger, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger, Skeleton, } from '@asteby/metacore-ui/primitives';
14
+ // The card "…" menu renders RowActionMenuItem (dynamic-columns), whose
15
+ // DropdownMenuItem comes from the ROOT '@asteby/metacore-ui' entry. The menu
16
+ // shell MUST come from that same entry: hosts share the root entry as a Module
17
+ // Federation singleton while '/primitives' is bundled locally, so mixing them
18
+ // yields two Radix Menu contexts and the click crashes the app with
19
+ // "`MenuItem` must be used within `Menu`" (Pitsline taller kanban, 2026-10-05).
20
+ import { DropdownMenu, DropdownMenuContent, DropdownMenuTrigger } from '@asteby/metacore-ui';
14
21
  import { ColumnFilterControl, FilterValueCombobox } from '@asteby/metacore-ui/data-table';
15
22
  import { generateBadgeStyles, optionColor } from '@asteby/metacore-ui/lib';
16
23
  import { useApi } from './api-context';
@@ -30,7 +37,7 @@ import { useRecordMutationTick } from './use-record-mutation-tick';
30
37
  import { useOptionsResolver } from './use-options-resolver';
31
38
  import { ActivityValueRenderer } from './activity-value-renderer';
32
39
  import { isColumnVisibleInTable } from './column-visibility';
33
- import { aggregateOf, formatAggregateTotal, isRowActionVisible, relationKeyFor, RowActionMenuItem, } from './dynamic-columns';
40
+ import { aggregateOf, formatAggregateTotal, isRowActionVisible, lifecycleStageField, relationKeyFor, RowActionMenuItem, } from './dynamic-columns';
34
41
  import { useCan, usePermissionsActive, resolveRowActions } from './permissions-context';
35
42
  import { useDynamicRowActions } from './dynamic-row-actions';
36
43
  import { useStageLayout } from './stage-layout';
@@ -244,18 +251,37 @@ export function isCardOverdue(card, col, now = Date.now()) {
244
251
  * The stage machine (stages, transitions, smart lanes) belongs to the model's
245
252
  * stage column (`stage_field`; older hosts that do not serve it: the served
246
253
  * group_by): grouping by it keeps the machine, grouping by anything else drops
247
- * it and the lanes come from the new column. Returns the SAME object when there
248
- * is nothing to change (no override, the served group_by already, or a column the
249
- * model does not have). Pure — exported for unit tests.
254
+ * it and the lanes come from the new column.
255
+ *
256
+ * The served group_by is the LAST kanban nav entry's, which need not be the
257
+ * stage column: a board opened without `?group_by=` (the list's view toggle)
258
+ * groups by the stage column, and a board grouped by the served column keeps
259
+ * the machine only when that column IS the stage column. Otherwise the stage
260
+ * lanes were painted over another column (by bay: every card under «Sin etapa»
261
+ * and a drop wrote `service_bay_id = 'diagnosis'`, 422). Returns the SAME
262
+ * object when there is nothing to change. Pure — exported for unit tests.
250
263
  */
251
264
  export function withGroupBy(metadata, groupBy) {
252
- if (!metadata || !groupBy || groupBy === metadata.group_by)
265
+ if (!metadata)
253
266
  return metadata;
267
+ const hasMachine = (metadata.stages?.length ?? 0) > 0;
268
+ const stageColumn = metadata.stage_field ?? (hasMachine ? metadata.group_by : undefined);
269
+ if (!groupBy) {
270
+ if (!hasMachine || !stageColumn || stageColumn === metadata.group_by)
271
+ return metadata;
272
+ return { ...metadata, group_by: stageColumn };
273
+ }
274
+ if (groupBy === stageColumn) {
275
+ return groupBy === metadata.group_by ? metadata : { ...metadata, group_by: groupBy };
276
+ }
254
277
  if (!metadata.columns.some((c) => c.key === groupBy))
255
278
  return metadata;
256
- const stageColumn = metadata.stage_field ?? (metadata.stages?.length ? metadata.group_by : undefined);
257
- if (groupBy === stageColumn)
258
- return { ...metadata, group_by: groupBy };
279
+ if (groupBy === metadata.group_by &&
280
+ !hasMachine &&
281
+ !metadata.transitions &&
282
+ !metadata.smart_lanes) {
283
+ return metadata;
284
+ }
259
285
  return {
260
286
  ...metadata,
261
287
  group_by: groupBy,
@@ -1543,7 +1569,7 @@ function DynamicKanbanBoard({ model, endpoint, refreshTrigger, realtime: realtim
1543
1569
  extraFilters,
1544
1570
  configurable: isConfigurable(stage),
1545
1571
  onConfigure: () => openConfigStage(stage),
1546
- children: loadingData && cards.length === 0 ? (_jsxs(_Fragment, { children: [_jsx(Skeleton, { className: "h-20 w-full" }), _jsx(Skeleton, { className: "h-20 w-full" })] })) : cards.length === 0 ? (_jsx("p", { className: "px-1 py-6 text-center text-xs text-muted-foreground", children: t('kanban.emptyLane', { defaultValue: 'Sin tarjetas' }) })) : (cards.map((card) => (_jsx(KanbanCard, { card: card, titleCol: titleCol, fieldCols: fieldCols, showAllFields: explicitCard, actions: rowActions, locale: i18n.language, timeZone: timeZone, currency: currency, onClick: onCardClick, onAction: handleInternalAction, pending: card.id != null && String(card.id) in moveOverrides }, String(card.id))))),
1572
+ children: loadingData && cards.length === 0 ? (_jsxs(_Fragment, { children: [_jsx(Skeleton, { className: "h-20 w-full" }), _jsx(Skeleton, { className: "h-20 w-full" })] })) : cards.length === 0 ? (_jsx("p", { className: "px-1 py-6 text-center text-xs text-muted-foreground", children: t('kanban.emptyLane', { defaultValue: 'Sin tarjetas' }) })) : (cards.map((card) => (_jsx(KanbanCard, { card: card, titleCol: titleCol, fieldCols: fieldCols, showAllFields: explicitCard, actions: rowActions, stageField: lifecycleStageField(metadata), locale: i18n.language, timeZone: timeZone, currency: currency, onClick: onCardClick, onAction: handleInternalAction, pending: card.id != null && String(card.id) in moveOverrides }, String(card.id))))),
1547
1573
  };
1548
1574
  };
1549
1575
  // Props for one smart (virtual) lane — a read-only, filter-defined column.
@@ -1557,7 +1583,7 @@ function DynamicKanbanBoard({ model, endpoint, refreshTrigger, realtime: realtim
1557
1583
  refreshTrigger,
1558
1584
  onEdit: openEditStage,
1559
1585
  onDelete: openDeleteStage,
1560
- renderCard: (card) => (_jsx(KanbanCard, { card: card, titleCol: titleCol, fieldCols: fieldCols, showAllFields: explicitCard, actions: rowActions, locale: i18n.language, timeZone: timeZone, currency: currency, onClick: onCardClick, onAction: handleInternalAction, draggable: false })),
1586
+ renderCard: (card) => (_jsx(KanbanCard, { card: card, titleCol: titleCol, fieldCols: fieldCols, showAllFields: explicitCard, actions: rowActions, stageField: lifecycleStageField(metadata), locale: i18n.language, timeZone: timeZone, currency: currency, onClick: onCardClick, onAction: handleInternalAction, draggable: false })),
1561
1587
  });
1562
1588
  const showResetOrder = laneReorderEnabled && (stageLayout.hasCustomLayout || !!laneOrderOverride);
1563
1589
  return (_jsxs("div", { className: "flex flex-col gap-3", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", "data-testid": "kanban-filters", children: [_jsxs("div", { className: "relative", children: [_jsx(Search, { className: "pointer-events-none absolute left-2.5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { value: globalFilter, onChange: (e) => setGlobalFilter(e.target.value), placeholder: t('kanban.searchPlaceholder', { defaultValue: 'Buscar...' }), className: "h-8 w-52 pl-8 text-sm" })] }), filterFields.length > 0 && (_jsxs(Sheet, { open: filtersOpen, onOpenChange: setFiltersOpen, children: [_jsx(SheetTrigger, { asChild: true, children: _jsxs(Button, { variant: "outline", size: "sm", className: "h-8 gap-1.5", children: [_jsx(ListFilter, { className: "h-3.5 w-3.5" }), t('kanban.filters', { defaultValue: 'Filtros' }), activeFilterCount > 0 && (_jsx(Badge, { variant: "secondary", className: "ml-0.5 h-4 min-w-4 justify-center rounded-full px-1 text-[10px] tabular-nums", children: activeFilterCount }))] }) }), _jsxs(SheetContent, { side: "right", className: "flex w-80 flex-col gap-0 p-0 sm:max-w-sm", children: [_jsx(SheetHeader, { className: "space-y-0 border-b px-4 py-3", children: _jsx(SheetTitle, { className: "text-sm", children: t('kanban.filters', { defaultValue: 'Filtros' }) }) }), _jsxs("div", { className: "flex flex-1 flex-col gap-1 overflow-y-auto px-4 py-3", children: [activeFields.length > 0 && (_jsxs(_Fragment, { children: [_jsx("p", { className: "px-0.5 pb-1 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: t('kanban.activeFilters', {
@@ -1875,13 +1901,13 @@ function CardBody({ card, titleCol, fieldCols, showAllFields, locale, timeZone,
1875
1901
  ? 'font-medium text-destructive'
1876
1902
  : 'text-muted-foreground'}`, "data-overdue": isCardOverdue(card, col) || undefined, children: [_jsxs("span", { className: "shrink-0 opacity-70", children: [col.label, ":"] }), _jsx("span", { className: "min-w-0 break-words", children: _jsx(ActivityValueRenderer, { value: cardCellValue(card, col), col: col, locale: locale, timeZone: timeZone, currency: currency }) })] }, col.key)))] }));
1877
1903
  }
1878
- function KanbanCard({ card, titleCol, fieldCols, showAllFields, actions, locale, timeZone, currency, onClick, onAction, draggable = true, pending = false, }) {
1904
+ function KanbanCard({ card, titleCol, fieldCols, showAllFields, actions, stageField, locale, timeZone, currency, onClick, onAction, draggable = true, pending = false, }) {
1879
1905
  const { attributes, listeners, setNodeRef, isDragging } = useDraggable({
1880
1906
  id: String(card.id),
1881
1907
  data: { type: 'card' },
1882
1908
  disabled: !draggable,
1883
1909
  });
1884
- const visibleActions = actions.filter((a) => isRowActionVisible(a, card));
1910
+ const visibleActions = actions.filter((a) => isRowActionVisible(a, card, stageField));
1885
1911
  const menu = visibleActions.length > 0 ? (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon", className: "h-6 w-6 shrink-0 -mr-1 -mt-1",
1886
1912
  // Don't start a drag / card click from the menu button.
1887
1913
  onPointerDown: (e) => e.stopPropagation(), onKeyDown: (e) => e.stopPropagation(), onClick: (e) => e.stopPropagation(), children: _jsx(MoreHorizontal, { className: "h-4 w-4" }) }) }), _jsx(DropdownMenuContent, { align: "end", onClick: (e) => e.stopPropagation(), children: visibleActions.map((a) => (_jsx(RowActionMenuItem, { action: a, label: a.label, onSelect: (e) => {
@@ -1 +1 @@
1
- {"version":3,"file":"dynamic-line-items.d.ts","sourceRoot":"","sources":["../src/dynamic-line-items.tsx"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAkB7C,MAAM,WAAW,qBAAqB;IAClC,KAAK,EAAE,cAAc,CAAA;IACrB,KAAK,EAAE,GAAG,EAAE,GAAG,SAAS,CAAA;IACxB,QAAQ,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAA;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAChC;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAClC;AAyBD,wBAAgB,gBAAgB,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAgB,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,qBAAqB,+BAoRvH"}
1
+ {"version":3,"file":"dynamic-line-items.d.ts","sourceRoot":"","sources":["../src/dynamic-line-items.tsx"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAmB7C,MAAM,WAAW,qBAAqB;IAClC,KAAK,EAAE,cAAc,CAAA;IACrB,KAAK,EAAE,GAAG,EAAE,GAAG,SAAS,CAAA;IACxB,QAAQ,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAA;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAChC;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAClC;AAyBD,wBAAgB,gBAAgB,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAgB,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,qBAAqB,+BAoRvH"}
@@ -13,6 +13,7 @@ import { useEffect, useRef } from 'react';
13
13
  import { Plus, Trash2, Check } from 'lucide-react';
14
14
  import { resolveWidget, getItemFields, computeLineItemTotals, applyLineItemRowFormulas, autofillLineFromOption, evaluateBalance, toNumber, getDependsOn, resolveDependsValue, getOptionsConfig, resolveOptionsSource, applyOptionWhen, } from './dynamic-form-schema';
15
15
  import { DynamicSelectField, DEFAULT_DEPENDS_HINT } from './dynamic-select-field';
16
+ import { getOptionFilter } from './option-filter';
16
17
  import { useOptionsResolver } from './use-options-resolver';
17
18
  const fmtNumber = (n) => n.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });
18
19
  /** Numeric columns render right-aligned (debit/credit/amount feel). */
@@ -197,6 +198,8 @@ function RefCell({ field, value, onChange, onPick, disabled, formValues, rowValu
197
198
  ref: optSource.ref,
198
199
  endpoint: optSource.endpoint,
199
200
  filterValue: dependsOn ? scope : undefined,
201
+ optionFilter: getOptionFilter(field),
202
+ keepValue: value,
200
203
  enabled: !blockedByDependency,
201
204
  });
202
205
  // Clear the selection when the parent scope changes (skip initial mount).
@@ -1 +1 @@
1
- {"version":3,"file":"dynamic-multi-select-field.d.ts","sourceRoot":"","sources":["../src/dynamic-multi-select-field.tsx"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C,MAAM,WAAW,4BAA4B;IACzC,KAAK,EAAE,cAAc,CAAA;IACrB,qFAAqF;IACrF,KAAK,EAAE,OAAO,CAAA;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,CAAA;CACtC;AAED,wBAAgB,uBAAuB,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,4BAA4B,+BA0B/F"}
1
+ {"version":3,"file":"dynamic-multi-select-field.d.ts","sourceRoot":"","sources":["../src/dynamic-multi-select-field.tsx"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C,MAAM,WAAW,4BAA4B;IACzC,KAAK,EAAE,cAAc,CAAA;IACrB,qFAAqF;IACrF,KAAK,EAAE,OAAO,CAAA;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,CAAA;CACtC;AAED,wBAAgB,uBAAuB,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,4BAA4B,+BA2B/F"}
@@ -20,6 +20,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
20
20
  // value instead.
21
21
  import { useMemo } from 'react';
22
22
  import { MultiSelect } from '@asteby/metacore-ui/primitives';
23
+ import { getOptionFilter } from './option-filter';
23
24
  import { useOptionsResolver } from './use-options-resolver';
24
25
  import { getFieldRef } from './dynamic-form-schema';
25
26
  export function DynamicMultiSelectField({ field, value, onChange }) {
@@ -30,6 +31,7 @@ export function DynamicMultiSelectField({ field, value, onChange }) {
30
31
  ref,
31
32
  endpoint: !ref && field.searchEndpoint ? field.searchEndpoint : undefined,
32
33
  limit: 200,
34
+ optionFilter: getOptionFilter(field),
33
35
  });
34
36
  const selected = useMemo(() => (Array.isArray(value) ? value.map(String) : []), [value]);
35
37
  const uiOptions = useMemo(() => options.map((o) => ({ value: String(o.id), label: o.label })), [options]);
@@ -1 +1 @@
1
- {"version":3,"file":"dynamic-relation-helpers.d.ts","sourceRoot":"","sources":["../src/dynamic-relation-helpers.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dynamic-relation-helpers.d.ts","sourceRoot":"","sources":["../src/dynamic-relation-helpers.ts"],"names":[],"mappings":"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"}
@@ -151,10 +151,18 @@ export function deriveRelationFormFields(metadata, foreignKey) {
151
151
  // than a raw uuid text input. Without this the column lost its `ref`
152
152
  // crossing the column→field boundary and degraded to plain text.
153
153
  ref: col.ref,
154
+ // Columnas de solo lectura (escritas por el servidor/sistema): el
155
+ // DynamicForm las oculta y no las manda en el submit. Sin copiarla,
156
+ // el renglón las exponía editables y el PATCH las pisaba.
157
+ ...(isReadonlyColumn(col) ? { readonly: true } : {}),
154
158
  });
155
159
  }
156
160
  return out;
157
161
  }
162
+ function isReadonlyColumn(col) {
163
+ const c = col;
164
+ return c.readonly === true || c.readOnly === true || c.read_only === true;
165
+ }
158
166
  function columnTypeToFieldType(col) {
159
167
  switch (col.type) {
160
168
  case 'number': return 'number';
@@ -315,7 +315,7 @@ function OneToManyRelation({ kind, model, foreignKey, parentId, filters, endpoin
315
315
  }) }, headerGroup.id))) }), _jsx(TableBody, { children: table.getRowModel().rows.map((row, idx) => (_jsx(TableRow, { children: row.getVisibleCells().map((cell) => (_jsx(TableCell, { style: cell.column.columnDef.size ? { width: cell.column.columnDef.size } : undefined, className: "py-2", children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id))) }, relationRowKey(row.original, idx, foreignKey)))) })] }), loadingMore && (_jsx("div", { className: "p-2", children: _jsx(Skeleton, { className: "h-8 w-full" }) })), _jsx("div", { ref: sentinelRef, "aria-hidden": true, className: "h-px w-full" })] }) })), !loading && rows.length > 0 && total > rows.length && (_jsx("div", { className: "pt-2 text-end text-xs text-muted-foreground", children: labels.countLabel
316
316
  .replace('{{loaded}}', String(rows.length))
317
317
  .replace('{{total}}', String(total)) })), _jsx(Dialog, { open: formOpen, onOpenChange: (open) => { setFormOpen(open); if (!open)
318
- setEditingRow(null); }, children: _jsxs(DialogContent, { children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: editingRow ? labels.editLabel : labels.addLabel }) }), _jsx(DynamicForm, { fields: formFields, initialValues: editingRow || undefined, onSubmit: handleSubmit, onCancel: () => { setFormOpen(false); setEditingRow(null); }, submitLabel: labels.saveLabel, cancelLabel: labels.cancelLabel, disabled: submitting })] }) }), _jsx(AlertDialog, { open: !!rowToDelete, onOpenChange: (open) => !open && setRowToDelete(null), children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: labels.confirmRemoveTitle }), _jsx(AlertDialogDescription, { children: labels.confirmRemoveDescription })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { disabled: submitting, children: labels.cancelLabel }), _jsx(AlertDialogAction, { onClick: (e) => { e.preventDefault(); handleDelete(); }, className: "bg-red-600 hover:bg-red-700", disabled: submitting, children: labels.removeLabel })] })] }) })] }));
318
+ setEditingRow(null); }, children: _jsxs(DialogContent, { className: "flex max-h-[90dvh] flex-col overflow-hidden", children: [_jsx(DialogHeader, { className: "shrink-0", children: _jsx(DialogTitle, { children: editingRow ? labels.editLabel : labels.addLabel }) }), _jsx(DynamicForm, { scrollableBody: true, fields: formFields, initialValues: editingRow || undefined, onSubmit: handleSubmit, onCancel: () => { setFormOpen(false); setEditingRow(null); }, submitLabel: labels.saveLabel, cancelLabel: labels.cancelLabel, disabled: submitting }, editingRow ? `edit-${editingRow.id ?? ''}` : 'new')] }) }), _jsx(AlertDialog, { open: !!rowToDelete, onOpenChange: (open) => !open && setRowToDelete(null), children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: labels.confirmRemoveTitle }), _jsx(AlertDialogDescription, { children: labels.confirmRemoveDescription })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { disabled: submitting, children: labels.cancelLabel }), _jsx(AlertDialogAction, { onClick: (e) => { e.preventDefault(); handleDelete(); }, className: "bg-red-600 hover:bg-red-700", disabled: submitting, children: labels.removeLabel })] })] }) })] }));
319
319
  }
320
320
  function ManyToManyRelation({ kind, through, references, foreignKey, referencesKey, parentId, filters, pivotEndpoint, referencesEndpoint, displayKey, canCreate = true, canDelete = true, readonly, readOnly, strings, className, onChange, }) {
321
321
  // Read-only relation always wins over host-passed perms (AND).
@@ -1 +1 @@
1
- {"version":3,"file":"dynamic-select-field.d.ts","sourceRoot":"","sources":["../src/dynamic-select-field.tsx"],"names":[],"mappings":"AA4CA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAEhF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,gDAAgD,CAAA;AAEjF;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,EACxB,KAAK,EACL,IAAI,EACJ,IAAS,GACZ,EAAE;IACC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,IAAI,CAAC,EAAE,MAAM,CAAA;CAChB,+BA4BA;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,EACvB,MAAM,EACN,IAAS,GACZ,EAAE;IACC,MAAM,CAAC,EAAE,IAAI,CAAC,cAAc,EAAE,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC,GAAG,IAAI,CAAA;IAC1E,IAAI,CAAC,EAAE,MAAM,CAAA;CAChB,sCA4BA;AAMD,MAAM,WAAW,uBAAuB;IACpC,KAAK,EAAE,cAAc,CAAA;IACrB,KAAK,EAAE,GAAG,CAAA;IACV,QAAQ,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,IAAI,CAAA;IAC1B,yFAAyF;IACzF,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,CAAA;IACtC;;;;;OAKG;IACH,UAAU,CAAC,EAAE,cAAc,GAAG,IAAI,CAAA;IAClC;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,6EAA6E;IAC7E,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,SAAS,cAAc,EAAE,GAAG,IAAI,CAAA;IAChD;;;OAGG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACxC;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC7B,oEAAoE;IACpE,OAAO,CAAC,EAAE,OAAO,CAAA;CACpB;AAED,wBAAgB,kBAAkB,CAAC,EAC/B,KAAK,EACL,KAAK,EACL,QAAQ,EACR,MAAM,EACN,UAAU,EACV,YAAY,EACZ,WAAW,EACX,QAAgB,EAChB,aAAoB,EACpB,kBAA0B,EAC1B,UAAkB,EAClB,cAAc,EACd,kBAAkB,EAClB,OAAe,GAClB,EAAE,uBAAuB,+BA2TzB;AAED,eAAe,kBAAkB,CAAA"}
1
+ {"version":3,"file":"dynamic-select-field.d.ts","sourceRoot":"","sources":["../src/dynamic-select-field.tsx"],"names":[],"mappings":"AA6CA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAEhF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,gDAAgD,CAAA;AAEjF;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,EACxB,KAAK,EACL,IAAI,EACJ,IAAS,GACZ,EAAE;IACC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,IAAI,CAAC,EAAE,MAAM,CAAA;CAChB,+BA4BA;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,EACvB,MAAM,EACN,IAAS,GACZ,EAAE;IACC,MAAM,CAAC,EAAE,IAAI,CAAC,cAAc,EAAE,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC,GAAG,IAAI,CAAA;IAC1E,IAAI,CAAC,EAAE,MAAM,CAAA;CAChB,sCA4BA;AAMD,MAAM,WAAW,uBAAuB;IACpC,KAAK,EAAE,cAAc,CAAA;IACrB,KAAK,EAAE,GAAG,CAAA;IACV,QAAQ,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,IAAI,CAAA;IAC1B,yFAAyF;IACzF,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,CAAA;IACtC;;;;;OAKG;IACH,UAAU,CAAC,EAAE,cAAc,GAAG,IAAI,CAAA;IAClC;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,6EAA6E;IAC7E,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,SAAS,cAAc,EAAE,GAAG,IAAI,CAAA;IAChD;;;OAGG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACxC;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC7B,oEAAoE;IACpE,OAAO,CAAC,EAAE,OAAO,CAAA;CACpB;AAED,wBAAgB,kBAAkB,CAAC,EAC/B,KAAK,EACL,KAAK,EACL,QAAQ,EACR,MAAM,EACN,UAAU,EACV,YAAY,EACZ,WAAW,EACX,QAAgB,EAChB,aAAoB,EACpB,kBAA0B,EAC1B,UAAkB,EAClB,cAAc,EACd,kBAAkB,EAClB,OAAe,GAClB,EAAE,uBAAuB,+BA8TzB;AAED,eAAe,kBAAkB,CAAA"}
@@ -23,6 +23,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
23
23
  // value). A dedicated `?ids=` lookup is a follow-up; create flows — the common
24
24
  // case — start empty and never hit this.
25
25
  import { useEffect, useRef, useState } from 'react';
26
+ import { getOptionFilter } from './option-filter';
26
27
  import { useTranslation } from 'react-i18next';
27
28
  import { Badge, Button, Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, Popover, PopoverContent, PopoverTrigger, InitialsAvatar, } from '@asteby/metacore-ui/primitives';
28
29
  import { Check, ChevronsUpDown, Loader2, Plus, ScanLine } from 'lucide-react';
@@ -123,6 +124,7 @@ export function DynamicSelectField({ field, value, onChange, onPick, seedOption,
123
124
  const scope = dependsValue ? String(dependsValue) : '';
124
125
  const blockedByDependency = !!dependsOn && scope === '';
125
126
  const useStatic = Array.isArray(staticOptions);
127
+ const optionFilter = getOptionFilter(field);
126
128
  const { options: fetchedOptions, loading: fetchLoading } = useOptionsResolver({
127
129
  modelKey: '',
128
130
  fieldKey: source.fieldKey,
@@ -136,6 +138,8 @@ export function DynamicSelectField({ field, value, onChange, onPick, seedOption,
136
138
  // Cascade scope forwarded as filter_value (only when this field
137
139
  // declares a dependency). Re-fetches when the parent value changes.
138
140
  filterValue: dependsOn ? scope : undefined,
141
+ optionFilter,
142
+ keepValue: value,
139
143
  // Don't fetch until the popover opens (and keep fetching as the query
140
144
  // changes while open). A picker blocked by an unset dependency never
141
145
  // fetches. A readonly cell fetches eagerly so its value's label resolves
package/dist/index.d.ts CHANGED
@@ -32,7 +32,7 @@ export { setFederatedActionLoader, type FederatedActionLoader } from './federate
32
32
  export { ModelActionToolbar, useModelActions, type ModelActionToolbarProps, type ActionPlacement, } from './model-action-toolbar';
33
33
  export { resolveListPrimaryAction, actionReplacesCreate, type ListPrimaryActionInput, type ListPrimaryActionResult, type ListActionRef, type CreateMode, } from './list-primary-action';
34
34
  export { emitRecordMutation, subscribeRecordMutations, RECORD_MUTATION_EVENT, type RecordMutationKind, type RecordMutationDetail, } from './record-mutation-events';
35
- export { DocumentFormDialog, resolveDocumentForms, type DocumentFormDialogProps, } from './document-form-dialog';
35
+ export { DocumentFormDialog, resolveDocumentForms, scopeDocumentFormsToFilter, type DocumentFormDialogProps, } from './document-form-dialog';
36
36
  export { modelFromNavUrl, capabilityForNavItem, isNavItemAllowed, useNavItemVisible, type NavLeafLike, } from './nav-permissions';
37
37
  export { CfdiStampPanel, CfdiStampResultDialog, extractStampResult, type CfdiStampResult, type CfdiStampPanelProps, type CfdiStampResultDialogProps, } from './cfdi-stamp-panel';
38
38
  export { useRecordMutationTick } from './use-record-mutation-tick';
@@ -54,6 +54,7 @@ export { applySidebarLayout, collectFolderKeys, folderAccessCapability, coreRefK
54
54
  export { SidebarLayoutManager, type SidebarLayoutManagerProps, } from './sidebar-layout-manager';
55
55
  export * from './org-runtime-context';
56
56
  export * from './business';
57
+ export * from './primitives';
57
58
  export * from './org-runtime-provider';
58
59
  export * from './navigation-builder';
59
60
  export * from './i18n-provider';
@@ -71,7 +72,7 @@ export { useHotSwapReload, applyHotSwapReload, withVersionParam, clearFederation
71
72
  export * from './dynamic-icon';
72
73
  export type { ColumnFilterConfig, FilterOption as DynamicColumnFilterOption, GetDynamicColumns, DynamicIconComponent, } from './dynamic-columns-shim';
73
74
  export { defaultGetDynamicColumns, makeDefaultGetDynamicColumns, relationKeyFor, resolveAvatarSrc, resolveMissingActorLabel, resolveActorDisplayName, resolveRelationLabel, type DynamicColumnsHelpers, } from './dynamic-columns';
74
- export { humanizeToken, translateMetadataLabel } from './dynamic-columns-helpers';
75
+ export { humanizeToken, localizeFieldLabel, translateMetadataLabel } from './dynamic-columns-helpers';
75
76
  export type { MetadataTranslator } from './dynamic-columns-helpers';
76
77
  export { UrlChip, FileChip, ImageThumbnail, MediaValue, RichText, linkifyText, classifyUrl, isImageUrl, isFileUrl, ensureHref, smartUrlLabel, fileNameFromUrl, splitTrailingPunct, type UrlKind, type LinkifyOptions, } from './rich-url';
77
78
  export { ImageStack, OptionBadge, statusColorFor, useIsDarkTheme, } from './display-value';
@@ -100,6 +101,7 @@ export { registerValidator, getDependsOn, resolveDependsValue, getOptionsConfig,
100
101
  export { BranchCreateGateProvider, useBranchCreateGate, type BranchCreateGateApi, } from './branch-create-gate';
101
102
  export { ActivityValueRenderer, type ActivityValueRendererProps, } from './activity-value-renderer';
102
103
  export { ActivityDiff, type ActivityEvent, type ActivityDiffProps, } from './activity-diff';
104
+ export { AuditInfo, readAuditMeta, resolveAuditActor, SYSTEM_ACTOR_ID, type AuditInfoProps, type AuditActor, } from './audit-info';
103
105
  export { RecordHistory, type RecordHistoryProps, } from './record-history';
104
106
  export { ActivityTimeline, type ActivityTimelineProps, } from './activity-timeline';
105
107
  export { DashboardGrid, normalizeGroups, } from './dashboard-grid';
@@ -113,4 +115,8 @@ export { AssistInterview, AssistCardView } from './assist-interview';
113
115
  export type { AssistSession, AssistTurn, AssistQuestion, AssistCard, AssistProgressStep } from './assist-interview';
114
116
  export { useAttributeClasses, resolveAttributeClasses, resetAttributeClassCache, type AttributeClass, type AttributeClassSection, } from './attribute-classes';
115
117
  export * from './document';
118
+ export { getOptionFilter, applyOptionFilter, optionPassesRule } from './option-filter';
119
+ export type { OptionFilter, OptionFilterRule } from './option-filter';
120
+ export { InstalledAddonsProvider, useAddonInstalled, useCapabilityProvided, useInstalledAddons, type InstalledAddonsValue, } from './installed-addons-context';
121
+ export { RecordPrefillBar, type RecordPrefillBarProps } from './record-prefill-bar';
116
122
  //# sourceMappingURL=index.d.ts.map