@asteby/metacore-runtime-react 49.6.0 → 49.7.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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @asteby/metacore-runtime-react
2
2
 
3
+ ## 49.7.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 468bd2b: RecordPicker: al editar un registro con una relación ya guardada, `DynamicSelectField` y `DynamicMultiSelectField` resuelven la etiqueta del valor con UNA petición `GET /options/<ref>?field=id&ids=…` sin abrir el popover (antes mostraban el UUID crudo si no había semilla). Nuevo `useResolveOptionIds`: react-query por id (usa el `QueryClient` del host o uno propio), agrupa en una sola petición los ids pedidos por todos los pickers de la misma ref en pantalla (trozos de 100), no dispara con semilla ni con el valor ya cargado, y se invalida con `invalidateOptionsCache`. Mientras carga muestra «Cargando…»; un id que ya no existe se ve como «(registro eliminado)». Compatible con hosts sin `ids`: si la respuesta trae otros registros se filtra por id y, si no está, se muestra el valor como antes. Las lecturas con `ids` no pasan por el batch `/q`. El `DynamicSelectField` de solo lectura ya no carga la primera página para resolver su etiqueta. Requiere metacore-kernel con `?ids=` (y en ops, el handler de lookup que lo reenvía) para el modo completo.
8
+
3
9
  ## 49.6.0
4
10
 
5
11
  ### Minor Changes
@@ -1 +1 @@
1
- {"version":3,"file":"dynamic-multi-select-field.d.ts","sourceRoot":"","sources":["../src/dynamic-multi-select-field.tsx"],"names":[],"mappings":"AAyBA,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,+BAoD/F"}
1
+ {"version":3,"file":"dynamic-multi-select-field.d.ts","sourceRoot":"","sources":["../src/dynamic-multi-select-field.tsx"],"names":[],"mappings":"AA+BA,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,+BA0D/F"}
@@ -18,16 +18,22 @@ import { jsx as _jsx } from "react/jsx-runtime";
18
18
  // (segments, tags, categories — tens, not thousands of rows); a field with a
19
19
  // genuinely large option set should keep using a single dynamic_select per
20
20
  // value instead.
21
+ //
22
+ // Selected ids outside that page (or hidden by an option_filter) are labelled
23
+ // with ONE `?ids=` lookup (useResolveOptionIds): a chip reads "Cargando…" while
24
+ // it resolves and "(registro eliminado)" when the record is gone — never the id.
21
25
  import { useMemo, useState } from 'react';
22
26
  import { getOptionFilter } from './option-filter';
23
27
  import { RecordPicker } from './record-picker';
24
28
  import { OptionLead } from './record-picker-option';
25
29
  import { useOptionsResolver } from './use-options-resolver';
30
+ import { useResolveOptionIds } from './use-option-ids';
31
+ import { DELETED_RECORD_LABEL } from './dynamic-select-field';
26
32
  import { getFieldRef } from './dynamic-form-schema';
27
33
  export function DynamicMultiSelectField({ field, value, onChange }) {
28
34
  const ref = getFieldRef(field);
29
35
  const [query, setQuery] = useState('');
30
- const { options, loading } = useOptionsResolver({
36
+ const { options, loading, meta, error } = useOptionsResolver({
31
37
  modelKey: '',
32
38
  fieldKey: 'id',
33
39
  ref,
@@ -36,9 +42,21 @@ export function DynamicMultiSelectField({ field, value, onChange }) {
36
42
  optionFilter: getOptionFilter(field),
37
43
  });
38
44
  const selected = useMemo(() => (Array.isArray(value) ? value.map(String) : []), [value]);
39
- const selectedItems = useMemo(() => selected
40
- .map((id) => options.find((o) => String(o.id) === id))
41
- .filter((o) => !!o), [selected, options]);
45
+ const endpoint = !ref && field.searchEndpoint ? field.searchEndpoint : undefined;
46
+ // Ask for the ids the loaded page does not cover — only once it is in.
47
+ const pageSettled = !loading && (meta !== null || error !== null);
48
+ const unresolved = pageSettled ? selected.filter((id) => !options.some((o) => String(o.id) === id)) : [];
49
+ const { resolved } = useResolveOptionIds({ ref, endpoint, field: 'id', ids: unresolved, enabled: unresolved.length > 0 });
50
+ const selectedItems = selected.map((id) => {
51
+ const loaded = options.find((o) => String(o.id) === id);
52
+ if (loaded)
53
+ return loaded;
54
+ const r = pageSettled ? resolved.get(id) : undefined;
55
+ if (r?.status === 'found')
56
+ return r.option;
57
+ const label = !r ? 'Cargando…' : r.status === 'missing' ? DELETED_RECORD_LABEL : id;
58
+ return { id, value: id, label, name: label };
59
+ });
42
60
  const shown = useMemo(() => {
43
61
  const q = query.trim().toLowerCase();
44
62
  return q ? options.filter((o) => String(o.label ?? '').toLowerCase().includes(q)) : options;
@@ -7,6 +7,8 @@ export { OptionLead, OptionThumb } from './record-picker-option';
7
7
  * `dependsHint`.
8
8
  */
9
9
  export declare const DEFAULT_DEPENDS_HINT = "Selecciona primero el campo del que depende";
10
+ /** Label of a held value whose record no longer exists (or is not visible). */
11
+ export declare const DELETED_RECORD_LABEL = "(registro eliminado)";
10
12
  export interface DynamicSelectFieldProps {
11
13
  field: ActionFieldDef;
12
14
  value: any;
@@ -1 +1 @@
1
- {"version":3,"file":"dynamic-select-field.d.ts","sourceRoot":"","sources":["../src/dynamic-select-field.tsx"],"names":[],"mappings":"AAiCA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAGhF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AAEhE;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,gDAAgD,CAAA;AAEjF,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;;;;;OAKG;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;;;;;GAKG;AACH,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,+BAwNzB;AAED,eAAe,kBAAkB,CAAA"}
1
+ {"version":3,"file":"dynamic-select-field.d.ts","sourceRoot":"","sources":["../src/dynamic-select-field.tsx"],"names":[],"mappings":"AAkCA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAIhF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AAEhE;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,gDAAgD,CAAA;AAEjF,+EAA+E;AAC/E,eAAO,MAAM,oBAAoB,yBAAyB,CAAA;AAE1D,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;;;;;OAKG;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;;;;;GAKG;AACH,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,+BAqPzB;AAED,eAAe,kBAAkB,CAAA"}
@@ -19,10 +19,11 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
19
19
  // 1. field.ref → /options/<ref>?field=id (canonical, preferred)
20
20
  // 2. field.searchEndpoint→ used verbatim as the options endpoint (escape hatch)
21
21
  //
22
- // Edit-mode caveat: resolving an EXISTING value's label requires the id to be
23
- // in a fetched page (we match by id against loaded options, else show the raw
24
- // value). A dedicated `?ids=` lookup is a follow-up; create flows — the common
25
- // case — start empty and never hit this.
22
+ // Edit mode: an EXISTING value is labelled from (in order) the option the user
23
+ // just picked, the loaded page, the caller's `seedOption`, and otherwise one
24
+ // `?ids=` lookup (useResolveOptionIds — batched and shared with every picker of
25
+ // the same ref on screen). While it resolves the trigger reads "Cargando…",
26
+ // never the raw id; an id that no longer exists reads "(registro eliminado)".
26
27
  import { useEffect, useRef, useState } from 'react';
27
28
  import { getOptionFilter } from './option-filter';
28
29
  import { useTranslation } from 'react-i18next';
@@ -33,6 +34,7 @@ import { RecordPicker } from './record-picker';
33
34
  import { OptionLead } from './record-picker-option';
34
35
  import { recordLabel, requestRecordCreate, requestRecordEdit, withSearchPrefill } from './record-picker-actions';
35
36
  import { useOptionsResolver } from './use-options-resolver';
37
+ import { useResolveOptionIds } from './use-option-ids';
36
38
  import { useDebouncedValue } from './use-debounced-value';
37
39
  import { getDependsOn, getFieldRef, resolveOptionsSource } from './dynamic-form-schema';
38
40
  export { OptionLead, OptionThumb } from './record-picker-option';
@@ -42,6 +44,8 @@ export { OptionLead, OptionThumb } from './record-picker-option';
42
44
  * `dependsHint`.
43
45
  */
44
46
  export const DEFAULT_DEPENDS_HINT = 'Selecciona primero el campo del que depende';
47
+ /** Label of a held value whose record no longer exists (or is not visible). */
48
+ export const DELETED_RECORD_LABEL = '(registro eliminado)';
45
49
  /**
46
50
  * Declarative `dynamic_select` (manifest `ref` / `optionsConfig.source`) on top
47
51
  * of the shared {@link RecordPicker}: options from the canonical options
@@ -78,21 +82,23 @@ export function DynamicSelectField({ field, value, onChange, onPick, seedOption,
78
82
  const blockedByDependency = !!dependsOn && scope === '';
79
83
  const useStatic = Array.isArray(staticOptions);
80
84
  const optionFilter = getOptionFilter(field);
85
+ // optionsConfig.source → `/options/<source>`. Else searchEndpoint only
86
+ // drives the URL when there's no ref (ref is canonical and wins).
87
+ const optionsEndpoint = source.endpoint ?? (source.ref ? undefined : field.searchEndpoint);
81
88
  const { options: fetchedOptions, loading: fetchLoading } = useOptionsResolver({
82
89
  modelKey: '',
83
90
  fieldKey: source.fieldKey,
84
91
  ref: source.ref,
85
- // optionsConfig.source → `/options/<source>`. Else searchEndpoint only
86
- // drives the URL when there's no ref (ref is canonical and wins).
87
- endpoint: source.endpoint ?? (source.ref ? undefined : field.searchEndpoint),
92
+ endpoint: optionsEndpoint,
88
93
  query: debounced,
89
94
  limit: 20,
90
95
  filterValue: dependsOn ? scope : undefined,
91
96
  optionFilter,
92
97
  keepValue: value,
93
- // Fetch only while open; a readonly cell fetches eagerly so its label
94
- // resolves to the name. Blocked cascades and static lists never fetch.
95
- enabled: !useStatic && (open || readonly) && !blockedByDependency,
98
+ // Fetch only while open (a held value's label comes from the `?ids=`
99
+ // lookup below, readonly included). Blocked cascades and static lists
100
+ // never fetch.
101
+ enabled: !useStatic && open && !blockedByDependency,
96
102
  });
97
103
  const options = useStatic
98
104
  ? staticOptions.filter((o) => {
@@ -116,11 +122,27 @@ export function DynamicSelectField({ field, value, onChange, onPick, seedOption,
116
122
  onChange('');
117
123
  }
118
124
  }, [dependsOn, scope, value, onChange]);
119
- const selectedOption = (picked && String(picked.id) === String(value) ? picked : null) ??
120
- options.find((o) => String(o.id) === String(value)) ??
121
- (seedOption && String(seedOption.id) === String(value) ? seedOption : null) ??
125
+ const valueKey = value != null && value !== '' ? String(value) : '';
126
+ const knownOption = (picked && String(picked.id) === valueKey ? picked : null) ??
127
+ options.find((o) => String(o.id) === valueKey) ??
128
+ (seedOption && String(seedOption.id) === valueKey ? seedOption : null) ??
122
129
  null;
123
- const selectedLabel = selectedOption?.label ?? (value ? String(value) : '');
130
+ // A held value nobody has labelled yet: one `?ids=` lookup (no popover).
131
+ const needsLookup = !!valueKey && !knownOption && !useStatic;
132
+ const { resolved, loading: idsLoading } = useResolveOptionIds({
133
+ ref: source.ref,
134
+ endpoint: optionsEndpoint,
135
+ field: source.fieldKey,
136
+ ids: needsLookup ? [valueKey] : [],
137
+ enabled: needsLookup,
138
+ });
139
+ const resolution = needsLookup ? resolved.get(valueKey) : undefined;
140
+ const selectedOption = knownOption ?? (resolution?.status === 'found' ? resolution.option : null);
141
+ const valueResolving = needsLookup && !selectedOption && idsLoading;
142
+ const valueMissing = needsLookup && !selectedOption && resolution?.status === 'missing';
143
+ const selectedLabel = selectedOption?.label ?? (valueResolving ? '' : valueMissing ? DELETED_RECORD_LABEL : valueKey);
144
+ const resolvingNode = (_jsx("span", { className: "min-w-0 flex-1 truncate animate-pulse text-muted-foreground", "data-slot": "record-picker-value-loading", children: "Cargando\u2026" }));
145
+ const missingNode = (_jsx("span", { className: "min-w-0 flex-1 truncate italic text-muted-foreground", "data-slot": "record-picker-value-missing", children: DELETED_RECORD_LABEL }));
124
146
  const handlePick = (opt) => {
125
147
  setPicked(opt);
126
148
  onChange(String(opt.id));
@@ -171,8 +193,13 @@ export function DynamicSelectField({ field, value, onChange, onPick, seedOption,
171
193
  if (!blockedByDependency)
172
194
  setOpen(o);
173
195
  }, disabled: blockedByDependency, readOnly: readonly, invalid: invalid, renderLead: (o, where) => _jsx(OptionLead, { option: o, size: where === 'option' ? 24 : 20 }), getDescription: descriptionAsBadge ? undefined : (o) => o.description, renderTrailing: descriptionAsBadge ? (o) => badge(o) : undefined, renderValue: () => {
196
+ // Never flash the raw id while the label resolves.
197
+ if (valueResolving)
198
+ return resolvingNode;
199
+ if (valueMissing)
200
+ return missingNode;
174
201
  if (readonly) {
175
- return (_jsxs(_Fragment, { children: [selectedOption ? _jsx(OptionLead, { option: selectedOption, size: 20 }) : null, _jsx("span", { className: 'min-w-0 flex-1 truncate ' + (selectedOption ? '' : 'text-muted-foreground'), children: selectedOption?.label ?? (loading ? 'Cargando…' : ph('—')) })] }));
202
+ return (_jsxs(_Fragment, { children: [selectedOption ? _jsx(OptionLead, { option: selectedOption, size: 20 }) : null, _jsx("span", { className: 'min-w-0 flex-1 truncate ' + (selectedOption ? '' : 'text-muted-foreground'), children: selectedOption?.label ?? ph('—') })] }));
176
203
  }
177
204
  return (_jsxs(_Fragment, { children: [value && selectedOption ? _jsx(OptionLead, { option: selectedOption, size: 20 }) : null, _jsx("span", { className: 'min-w-0 flex-1 truncate ' + (selectedLabel ? '' : 'text-muted-foreground'), children: blockedByDependency ? dependsHint || DEFAULT_DEPENDS_HINT : selectedLabel || ph('Buscar…') }), descriptionAsBadge && selectedOption ? badge(selectedOption) : null] }));
178
205
  }, placeholder: ph('Buscar…'), searchPlaceholder: ph('Buscar…'), emptyText: debounced ? 'Sin resultados' : useStatic ? 'Sin opciones' : 'Sin resultados — escribe para filtrar', entityLabel: fieldName, onCreate: canMutate ? openCreate : undefined, onEdit: canMutate ? openEdit : undefined, triggerProps: {
package/dist/index.d.ts CHANGED
@@ -104,6 +104,8 @@ export { registerAgentResultRenderer, resolveAgentResultRenderer, listAgentResul
104
104
  export { isColumnVisibleInTable, isColumnVisibleInModal, isColumnVisibleInLineSubtable, AUDIT_SUBTABLE_COLUMN_KEYS, getSearchableColumnKeys, } from './column-visibility';
105
105
  export { batchGet, loadQueryPart, nameBatchTokens, narrowInToken, optionsBatchToken, optionsModelFromUrl, rememberInRows, invalidateQueryBatchData, resetQueryBatchCache, splitInToken, tokenForGet, type InListToken, type NarrowedIn, type QueryPart, } from './query-batch';
106
106
  export { useOptionsResolver, invalidateOptionsCache, projectOption, type ResolvedOption, type OptionsMeta, type UseOptionsResolverArgs, type UseOptionsResolverResult, } from './use-options-resolver';
107
+ export { useResolveOptionIds, OPTION_IDS_CHUNK, type OptionIdResolution, type UseResolveOptionIdsArgs, type UseResolveOptionIdsResult, } from './use-option-ids';
108
+ export { DELETED_RECORD_LABEL } from './dynamic-select-field';
107
109
  export { setOrgConfigBridge, getOrgConfigBridge, resolveValidatorToken, type OrgConfigBridge, } from './use-org-config-bridge';
108
110
  export { registerValidator, getDependsOn, resolveDependsValue, getOptionsConfig, resolveOptionsSource, getVisibleWhen, evaluateVisibleWhen, ATTRIBUTE_CLASSES_KEY, scopeValueFromFilterToken, buildListScopeValues, evaluateVisibleWhenForListScope, applyLineItemRowFormulas, computeLineItemTotals, toNumber, } from './dynamic-form-schema';
109
111
  export { BranchCreateGateProvider, useBranchCreateGate, type BranchCreateGateApi, } from './branch-create-gate';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -108,6 +108,8 @@ export { registerAgentResultRenderer, resolveAgentResultRenderer, listAgentResul
108
108
  export { isColumnVisibleInTable, isColumnVisibleInModal, isColumnVisibleInLineSubtable, AUDIT_SUBTABLE_COLUMN_KEYS, getSearchableColumnKeys, } from './column-visibility';
109
109
  export { batchGet, loadQueryPart, nameBatchTokens, narrowInToken, optionsBatchToken, optionsModelFromUrl, rememberInRows, invalidateQueryBatchData, resetQueryBatchCache, splitInToken, tokenForGet, } from './query-batch';
110
110
  export { useOptionsResolver, invalidateOptionsCache, projectOption, } from './use-options-resolver';
111
+ export { useResolveOptionIds, OPTION_IDS_CHUNK, } from './use-option-ids';
112
+ export { DELETED_RECORD_LABEL } from './dynamic-select-field';
111
113
  export { setOrgConfigBridge, getOrgConfigBridge, resolveValidatorToken, } from './use-org-config-bridge';
112
114
  export { registerValidator, getDependsOn, resolveDependsValue, getOptionsConfig, resolveOptionsSource, getVisibleWhen, evaluateVisibleWhen, ATTRIBUTE_CLASSES_KEY, scopeValueFromFilterToken, buildListScopeValues, evaluateVisibleWhenForListScope, applyLineItemRowFormulas, computeLineItemTotals, toNumber, } from './dynamic-form-schema';
113
115
  export { BranchCreateGateProvider, useBranchCreateGate, } from './branch-create-gate';
@@ -1 +1 @@
1
- {"version":3,"file":"query-batch.d.ts","sourceRoot":"","sources":["../src/query-batch.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAE9C,MAAM,WAAW,SAAS;IACtB,OAAO,EAAE,OAAO,CAAA;IAChB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,YAAY,CAAC,EAAE,OAAO,CAAA;CACzB;AAyCD,MAAM,WAAW,WAAW;IACxB,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,EAAE,MAAM,EAAE,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;CAChB;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAuB9D;AAkBD,MAAM,WAAW,UAAU;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;IAC/B,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;CAChB;AAED,0FAA0F;AAC1F,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG,UAAU,GAAG,IAAI,CAsBhF;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC1B,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EAAE,EACb,IAAI,EAAE,OAAO,EACb,GAAG,SAAa,GACjB,IAAI,CAkBN;AAaD,wBAAgB,oBAAoB,IAAI,IAAI,CAO3C;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,IAAI,IAAI,CAM/C;AAsBD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG9D;AAED,wBAAgB,iBAAiB,CAC7B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,MAAM,EAAE,MAAM,GAAG,SAAS,GAC3B,MAAM,CAOR;AAED,oEAAoE;AACpE,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,CAYjG;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAsB/E;AAwED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,GAAG,IAAI,CA2BxF;AAMD;;;GAGG;AACH,wBAAsB,QAAQ,CAAC,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE;;;GA2BxG"}
1
+ {"version":3,"file":"query-batch.d.ts","sourceRoot":"","sources":["../src/query-batch.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAE9C,MAAM,WAAW,SAAS;IACtB,OAAO,EAAE,OAAO,CAAA;IAChB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,YAAY,CAAC,EAAE,OAAO,CAAA;CACzB;AAyCD,MAAM,WAAW,WAAW;IACxB,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,EAAE,MAAM,EAAE,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;CAChB;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAuB9D;AAkBD,MAAM,WAAW,UAAU;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;IAC/B,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;CAChB;AAED,0FAA0F;AAC1F,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG,UAAU,GAAG,IAAI,CAsBhF;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC1B,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EAAE,EACb,IAAI,EAAE,OAAO,EACb,GAAG,SAAa,GACjB,IAAI,CAkBN;AAaD,wBAAgB,oBAAoB,IAAI,IAAI,CAO3C;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,IAAI,IAAI,CAM/C;AAsBD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG9D;AAED,wBAAgB,iBAAiB,CAC7B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,MAAM,EAAE,MAAM,GAAG,SAAS,GAC3B,MAAM,CAOR;AAED,oEAAoE;AACpE,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,CAYjG;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAsB/E;AAwED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,GAAG,IAAI,CA8BxF;AAMD;;;GAGG;AACH,wBAAsB,QAAQ,CAAC,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE;;;GA2BxG"}
@@ -331,6 +331,10 @@ export function tokenForGet(url, params) {
331
331
  search.set(key, String(value));
332
332
  }
333
333
  }
334
+ // An `?ids=` resolve is not a page: the `o:` token has no slot for it, so
335
+ // it goes as its own GET instead of being turned into a first-page read.
336
+ if (search.has('ids'))
337
+ return null;
334
338
  const limit = Number(search.get('limit'));
335
339
  return optionsBatchToken(opt[1], search.get('field') || 'id', search.get('q') || search.get('search') || '', Number.isFinite(limit) && limit > 0 ? limit : undefined, search.get('filter_value') || undefined);
336
340
  }
@@ -0,0 +1,34 @@
1
+ import { type ResolvedOption } from './use-options-resolver';
2
+ export type OptionIdResolution = {
3
+ status: 'found';
4
+ option: ResolvedOption;
5
+ }
6
+ /** The host resolved the ids and this one is not there (deleted, or not visible). */
7
+ | {
8
+ status: 'missing';
9
+ }
10
+ /** Could not tell (old host, error): show the raw value. */
11
+ | {
12
+ status: 'unknown';
13
+ };
14
+ /** Ids the kernel accepts per request (dynamic.MaxOptionsIDs). */
15
+ export declare const OPTION_IDS_CHUNK = 100;
16
+ export interface UseResolveOptionIdsArgs {
17
+ /** FK target: `/options/<ref>`. */
18
+ ref?: string;
19
+ /** Explicit options endpoint (wins over `ref`), as in useOptionsResolver. */
20
+ endpoint?: string;
21
+ /** `?field=`; defaults to `id` with a `ref`. */
22
+ field?: string;
23
+ /** Ids to label. Pass only those not already loaded / seeded. */
24
+ ids: readonly (string | number)[];
25
+ enabled?: boolean;
26
+ }
27
+ export interface UseResolveOptionIdsResult {
28
+ /** Settled resolutions by id (as given, stringified). */
29
+ resolved: ReadonlyMap<string, OptionIdResolution>;
30
+ /** Some id is still being resolved. */
31
+ loading: boolean;
32
+ }
33
+ export declare function useResolveOptionIds(args: UseResolveOptionIdsArgs): UseResolveOptionIdsResult;
34
+ //# sourceMappingURL=use-option-ids.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-option-ids.d.ts","sourceRoot":"","sources":["../src/use-option-ids.ts"],"names":[],"mappings":"AAuBA,OAAO,EAA0D,KAAK,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAEpH,MAAM,MAAM,kBAAkB,GACxB;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,cAAc,CAAA;CAAE;AAC7C,qFAAqF;GACnF;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE;AACvB,4DAA4D;GAC1D;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,CAAA;AAE3B,kEAAkE;AAClE,eAAO,MAAM,gBAAgB,MAAM,CAAA;AA8EnC,MAAM,WAAW,uBAAuB;IACpC,mCAAmC;IACnC,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,gDAAgD;IAChD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,iEAAiE;IACjE,GAAG,EAAE,SAAS,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAA;IACjC,OAAO,CAAC,EAAE,OAAO,CAAA;CACpB;AAED,MAAM,WAAW,yBAAyB;IACtC,yDAAyD;IACzD,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAA;IACjD,uCAAuC;IACvC,OAAO,EAAE,OAAO,CAAA;CACnB;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,uBAAuB,GAAG,yBAAyB,CAqC5F"}
@@ -0,0 +1,138 @@
1
+ // useResolveOptionIds — labels for values a picker already holds.
2
+ //
3
+ // A RecordPicker editing a saved record (a product with its brand) holds an id
4
+ // whose label is in no loaded page: options load only once the popover opens,
5
+ // so the trigger used to show the raw UUID unless the caller had a seed (the
6
+ // relation sibling the table served). This hook asks the canonical options
7
+ // endpoint for exactly those ids:
8
+ //
9
+ // GET /api/options/<ref>?field=id&ids=a,b
10
+ //
11
+ // - One react-query entry per (org/branch, endpoint, field, id): two pickers
12
+ // on the same value share it, and nothing is refetched for 30 s.
13
+ // - Every id requested within a few ms for the same endpoint + field travels in
14
+ // ONE request (a column of cells, a multi-select's chips), chunked at the
15
+ // kernel's 100-id cap.
16
+ // - A host that predates `ids` ignores the parameter and answers a first page.
17
+ // The answer is then filtered by id: a hit still resolves, a miss stays
18
+ // `unknown` (the caller shows the value as before) instead of being reported
19
+ // as deleted. A response that only holds requested ids is a host that
20
+ // understood `ids`, so a requested id absent from it is `missing`.
21
+ import { useContext, useMemo } from 'react';
22
+ import { QueryClient, QueryClientContext, useQueries } from '@tanstack/react-query';
23
+ import { useApi } from './api-context';
24
+ import { onOptionsCacheInvalidated, optionsScope, projectOption } from './use-options-resolver';
25
+ /** Ids the kernel accepts per request (dynamic.MaxOptionsIDs). */
26
+ export const OPTION_IDS_CHUNK = 100;
27
+ const OPTION_IDS_TTL_MS = 30_000;
28
+ const OPTION_IDS_WINDOW_MS = 5;
29
+ const QUERY_ROOT = 'metacore-option-ids';
30
+ // Hosts without a QueryClientProvider still get dedup + cache.
31
+ let fallbackClient = null;
32
+ function getFallbackClient() {
33
+ if (!fallbackClient)
34
+ fallbackClient = new QueryClient({ defaultOptions: { queries: { retry: false } } });
35
+ return fallbackClient;
36
+ }
37
+ const knownClients = new Set();
38
+ onOptionsCacheInvalidated(() => {
39
+ for (const client of knownClients)
40
+ void client.invalidateQueries({ queryKey: [QUERY_ROOT] });
41
+ });
42
+ const pendingBatches = new Map();
43
+ function enqueue(api, scope, url, field, id) {
44
+ const key = [scope, url, field].join('\n');
45
+ // Keyed without the client: useApi() hands each component its own wrapper
46
+ // of the same host client, and pickers of one ref must share the request.
47
+ let batch = pendingBatches.get(key);
48
+ if (!batch) {
49
+ batch = { api, url, field, waiters: new Map() };
50
+ pendingBatches.set(key, batch);
51
+ const flushing = batch;
52
+ setTimeout(() => {
53
+ if (pendingBatches.get(key) === flushing)
54
+ pendingBatches.delete(key);
55
+ void flush(flushing);
56
+ }, OPTION_IDS_WINDOW_MS);
57
+ }
58
+ const target = batch;
59
+ return new Promise((resolve) => {
60
+ const list = target.waiters.get(id) ?? [];
61
+ list.push({ resolve });
62
+ target.waiters.set(id, list);
63
+ });
64
+ }
65
+ async function flush(batch) {
66
+ const ids = [...batch.waiters.keys()];
67
+ for (let i = 0; i < ids.length; i += OPTION_IDS_CHUNK) {
68
+ const chunk = ids.slice(i, i + OPTION_IDS_CHUNK);
69
+ const result = await fetchIds(batch.api, batch.url, batch.field, chunk);
70
+ for (const id of chunk) {
71
+ const r = result.get(id) ?? { status: 'unknown' };
72
+ for (const w of batch.waiters.get(id) ?? [])
73
+ w.resolve(r);
74
+ }
75
+ }
76
+ }
77
+ async function fetchIds(api, url, field, ids) {
78
+ const out = new Map();
79
+ try {
80
+ const res = await api.get(url, { params: { field, ids: ids.join(',') } });
81
+ const body = res?.data;
82
+ if (!body || body.success !== true || !Array.isArray(body.data))
83
+ return out;
84
+ const options = body.data.map(projectOption);
85
+ const byId = new Map();
86
+ for (const o of options)
87
+ byId.set(String(o.id).toLowerCase(), o);
88
+ const asked = new Set(ids.map((id) => id.toLowerCase()));
89
+ // Only requested ids back → the host honoured `ids`.
90
+ const honoured = options.every((o) => asked.has(String(o.id).toLowerCase()));
91
+ for (const id of ids) {
92
+ const hit = byId.get(id.toLowerCase());
93
+ if (hit)
94
+ out.set(id, { status: 'found', option: hit });
95
+ else if (honoured)
96
+ out.set(id, { status: 'missing' });
97
+ }
98
+ }
99
+ catch {
100
+ // Network / permission error: leave every id `unknown`.
101
+ }
102
+ return out;
103
+ }
104
+ export function useResolveOptionIds(args) {
105
+ const { ref, endpoint, field, ids, enabled = true } = args;
106
+ const api = useApi();
107
+ const contextClient = useContext(QueryClientContext);
108
+ const client = contextClient ?? getFallbackClient();
109
+ knownClients.add(client);
110
+ const url = endpoint || (ref ? `/options/${ref}` : '');
111
+ const effectiveField = field || (ref ? 'id' : '');
112
+ const scope = optionsScope();
113
+ const list = useMemo(() => [...new Set(ids.map(String).filter((id) => id !== ''))], [ids.map(String).join('\u0001')]);
114
+ const active = enabled && !!url && !!effectiveField && list.length > 0;
115
+ const results = useQueries({
116
+ queries: (active ? list : []).map((id) => ({
117
+ queryKey: [QUERY_ROOT, scope, url, effectiveField, id],
118
+ queryFn: () => enqueue(api, scope, url, effectiveField, id),
119
+ staleTime: OPTION_IDS_TTL_MS,
120
+ retry: false,
121
+ refetchOnWindowFocus: false,
122
+ })),
123
+ }, client);
124
+ const resolved = new Map();
125
+ let loading = false;
126
+ if (active) {
127
+ list.forEach((id, i) => {
128
+ const r = results[i];
129
+ if (r?.data)
130
+ resolved.set(id, r.data);
131
+ else if (r?.isError)
132
+ resolved.set(id, { status: 'unknown' });
133
+ else
134
+ loading = true;
135
+ });
136
+ }
137
+ return { resolved, loading };
138
+ }
@@ -95,7 +95,11 @@ export interface UseOptionsResolverResult {
95
95
  * lists it instead of serving the previous 30 s snapshot.
96
96
  */
97
97
  export declare function invalidateOptionsCache(): void;
98
+ /** Runs `fn` on every invalidateOptionsCache (id-label lookups drop with it). */
99
+ export declare function onOptionsCacheInvalidated(fn: () => void): () => void;
98
100
  export declare function optionsRequestKey(scope: string, url: string, field: string, query: string, limit: number | undefined, filter: string | undefined): string;
101
+ /** Org + branch of the host session: part of every options cache key. */
102
+ export declare function optionsScope(): string;
99
103
  export declare function useOptionsResolver(args: UseOptionsResolverArgs): UseOptionsResolverResult;
100
104
  export declare function projectOption(raw: any): ResolvedOption;
101
105
  //# sourceMappingURL=use-options-resolver.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-options-resolver.d.ts","sourceRoot":"","sources":["../src/use-options-resolver.ts"],"names":[],"mappings":"AAcA,OAAO,EAAqB,KAAK,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAG1E,MAAM,WAAW,cAAc;IAC3B,8CAA8C;IAC9C,EAAE,EAAE,MAAM,GAAG,MAAM,CAAA;IACnB,2DAA2D;IAC3D,KAAK,EAAE,MAAM,GAAG,MAAM,CAAA;IACtB,sBAAsB;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,8DAA8D;IAC9D,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACjC;AAED,MAAM,WAAW,WAAW;IACxB,oEAAoE;IACpE,IAAI,EAAE,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAA;IACnC,2DAA2D;IAC3D,KAAK,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,sBAAsB;IACnC;;;;OAIG;IACH,QAAQ,EAAE,MAAM,CAAA;IAChB;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAA;IAChB;;;;;OAKG;IACH,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;OAGG;IACH,YAAY,CAAC,EAAE,gBAAgB,EAAE,CAAA;IACjC,gFAAgF;IAChF,SAAS,CAAC,EAAE,OAAO,CAAA;CACtB;AAED,MAAM,WAAW,wBAAwB;IACrC,OAAO,EAAE,cAAc,EAAE,CAAA;IACzB,IAAI,EAAE,WAAW,GAAG,IAAI,CAAA;IACxB,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;IACnB,8DAA8D;IAC9D,OAAO,EAAE,MAAM,IAAI,CAAA;CACtB;AAuBD;;;;GAIG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAI7C;AAED,wBAAgB,iBAAiB,CAC7B,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,MAAM,EAAE,MAAM,GAAG,SAAS,GAC3B,MAAM,CAER;AA8BD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,sBAAsB,GAAG,wBAAwB,CAuKzF;AASD,wBAAgB,aAAa,CAAC,GAAG,EAAE,GAAG,GAAG,cAAc,CAqBtD"}
1
+ {"version":3,"file":"use-options-resolver.d.ts","sourceRoot":"","sources":["../src/use-options-resolver.ts"],"names":[],"mappings":"AAcA,OAAO,EAAqB,KAAK,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAG1E,MAAM,WAAW,cAAc;IAC3B,8CAA8C;IAC9C,EAAE,EAAE,MAAM,GAAG,MAAM,CAAA;IACnB,2DAA2D;IAC3D,KAAK,EAAE,MAAM,GAAG,MAAM,CAAA;IACtB,sBAAsB;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,8DAA8D;IAC9D,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACjC;AAED,MAAM,WAAW,WAAW;IACxB,oEAAoE;IACpE,IAAI,EAAE,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAA;IACnC,2DAA2D;IAC3D,KAAK,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,sBAAsB;IACnC;;;;OAIG;IACH,QAAQ,EAAE,MAAM,CAAA;IAChB;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAA;IAChB;;;;;OAKG;IACH,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;OAGG;IACH,YAAY,CAAC,EAAE,gBAAgB,EAAE,CAAA;IACjC,gFAAgF;IAChF,SAAS,CAAC,EAAE,OAAO,CAAA;CACtB;AAED,MAAM,WAAW,wBAAwB;IACrC,OAAO,EAAE,cAAc,EAAE,CAAA;IACzB,IAAI,EAAE,WAAW,GAAG,IAAI,CAAA;IACxB,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;IACnB,8DAA8D;IAC9D,OAAO,EAAE,MAAM,IAAI,CAAA;CACtB;AAuBD;;;;GAIG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAK7C;AAID,iFAAiF;AACjF,wBAAgB,yBAAyB,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAGpE;AAED,wBAAgB,iBAAiB,CAC7B,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,MAAM,EAAE,MAAM,GAAG,SAAS,GAC3B,MAAM,CAER;AAED,yEAAyE;AACzE,wBAAgB,YAAY,IAAI,MAAM,CAWrC;AAiBD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,sBAAsB,GAAG,wBAAwB,CAuKzF;AASD,wBAAgB,aAAa,CAAC,GAAG,EAAE,GAAG,GAAG,cAAc,CAqBtD"}
@@ -40,11 +40,20 @@ export function invalidateOptionsCache() {
40
40
  optionsGeneration++;
41
41
  optionsCache.clear();
42
42
  optionsInflight.clear();
43
+ for (const fn of invalidationListeners)
44
+ fn();
45
+ }
46
+ const invalidationListeners = new Set();
47
+ /** Runs `fn` on every invalidateOptionsCache (id-label lookups drop with it). */
48
+ export function onOptionsCacheInvalidated(fn) {
49
+ invalidationListeners.add(fn);
50
+ return () => invalidationListeners.delete(fn);
43
51
  }
44
52
  export function optionsRequestKey(scope, url, field, query, limit, filter) {
45
53
  return [scope, url, field, query, String(limit ?? ''), filter ?? ''].join('\n');
46
54
  }
47
- function optionsScope() {
55
+ /** Org + branch of the host session: part of every options cache key. */
56
+ export function optionsScope() {
48
57
  if (typeof localStorage === 'undefined')
49
58
  return '';
50
59
  let org = '';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@asteby/metacore-runtime-react",
3
- "version": "49.6.0",
3
+ "version": "49.7.0",
4
4
  "description": "React runtime for metacore hosts — renders addon contributions dynamically",
5
5
  "repository": {
6
6
  "type": "git",
@@ -153,7 +153,7 @@ describe('EditField — PART 2: FK select shows the resolved label', () => {
153
153
  expect(screen.queryByText('550e8400-e29b-41d4-a716-446655440000')).toBeNull()
154
154
  })
155
155
 
156
- it('keeps the raw value as fallback when no sibling was injected', () => {
156
+ it('keeps the raw value as fallback when no sibling was injected and the host cannot resolve ids', async () => {
157
157
  render(
158
158
  <ApiProvider client={noopApi}>
159
159
  <EditField
@@ -164,7 +164,10 @@ describe('EditField — PART 2: FK select shows the resolved label', () => {
164
164
  />
165
165
  </ApiProvider>,
166
166
  )
167
- // No sibling → existing behaviour: the raw value is shown on the trigger.
168
- expect(screen.getByText('wh-xyz')).toBeTruthy()
167
+ // No sibling → an `?ids=` lookup runs first ("Cargando…", never the id);
168
+ // this host answers without resolving it, so the raw value is the
169
+ // fallback on the trigger, as before.
170
+ expect(screen.getByText('Cargando…')).toBeTruthy()
171
+ expect(await screen.findByText('wh-xyz')).toBeTruthy()
169
172
  })
170
173
  })
@@ -0,0 +1,181 @@
1
+ // @vitest-environment happy-dom
2
+ //
3
+ // RecordPicker editing a saved value: the label comes from ONE `?ids=` lookup
4
+ // (useResolveOptionIds) without opening the popover — batched per ref, shared
5
+ // between pickers, skipped with a seed, "(registro eliminado)" for a gone id
6
+ // and the raw value when the host predates `ids`.
7
+ import { afterEach, describe, expect, it, vi } from 'vitest'
8
+ import { cleanup, render, screen, waitFor } from '@testing-library/react'
9
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
10
+
11
+ vi.mock('react-i18next', () => {
12
+ const value = { t: (k: string, o?: Record<string, any>) => o?.defaultValue ?? k, i18n: { language: 'es' } }
13
+ return { useTranslation: () => value }
14
+ })
15
+
16
+ import { DynamicSelectField } from '../dynamic-select-field'
17
+ import { DynamicMultiSelectField } from '../dynamic-multi-select-field'
18
+ import { ApiProvider, type ApiClient } from '../api-context'
19
+ import { invalidateOptionsCache } from '../use-options-resolver'
20
+ import type { ActionFieldDef } from '../types'
21
+
22
+ afterEach(() => {
23
+ cleanup()
24
+ invalidateOptionsCache()
25
+ })
26
+
27
+ let seq = 0
28
+ /** Fresh ids per test: the id-label cache is module-wide on purpose. */
29
+ const uid = (p: string) => `${p}-${++seq}-${Math.random().toString(36).slice(2, 8)}`
30
+
31
+ type Row = { id: string; label: string }
32
+
33
+ /** A host that understands `ids`: answers exactly the requested rows it has. */
34
+ function idsHost(rows: Row[], page: Row[] = rows) {
35
+ const get = vi.fn(async (_url: string, config?: { params?: Record<string, unknown> }) => {
36
+ const ids = config?.params?.ids
37
+ if (typeof ids === 'string') {
38
+ const want = ids.split(',')
39
+ return { data: { success: true, data: rows.filter((r) => want.includes(r.id)), meta: { type: 'dynamic' } } }
40
+ }
41
+ return { data: { success: true, data: page, meta: { type: 'dynamic' } } }
42
+ })
43
+ // batch=false: no POST /q in these tests (ids reads bypass it anyway).
44
+ const api = { get, post: vi.fn(async () => ({ data: {} })), put: vi.fn(), delete: vi.fn() } as unknown as ApiClient
45
+ return { api, get, idsCalls: () => get.mock.calls.filter(([, c]) => typeof c?.params?.ids === 'string') }
46
+ }
47
+
48
+ const brand: ActionFieldDef = { key: 'brand_id', label: 'Marca', type: 'dynamic_select', ref: 'Brand' }
49
+
50
+ describe('RecordPicker: etiqueta de un valor guardado por ?ids=', () => {
51
+ it('sin semilla → una sola petición ids y muestra el nombre sin abrir el popover', async () => {
52
+ const id = uid('b')
53
+ const host = idsHost([{ id, label: 'Michelin' }])
54
+ render(
55
+ <ApiProvider client={host.api}>
56
+ <DynamicSelectField field={brand} value={id} onChange={vi.fn()} />
57
+ </ApiProvider>,
58
+ )
59
+ // Mientras resuelve: "Cargando…", nunca el UUID.
60
+ expect(screen.getByText('Cargando…')).toBeTruthy()
61
+ expect(screen.queryByText(id)).toBeNull()
62
+ expect(await screen.findByText('Michelin')).toBeTruthy()
63
+ expect(host.get).toHaveBeenCalledTimes(1)
64
+ expect(host.get).toHaveBeenCalledWith('/options/Brand', { params: { field: 'id', ids: id } })
65
+ expect(document.querySelector('[data-slot="record-picker-content"]')).toBeNull()
66
+ })
67
+
68
+ it('dos pickers de la misma ref (y uno repetido) → UNA petición con todos los ids', async () => {
69
+ const a = uid('b')
70
+ const b = uid('b')
71
+ const host = idsHost([
72
+ { id: a, label: 'Michelin' },
73
+ { id: b, label: 'Pirelli' },
74
+ ])
75
+ render(
76
+ <ApiProvider client={host.api}>
77
+ <DynamicSelectField field={brand} value={a} onChange={vi.fn()} />
78
+ <DynamicSelectField field={{ ...brand, key: 'alt_brand_id' }} value={b} onChange={vi.fn()} />
79
+ <DynamicSelectField field={{ ...brand, key: 'other_brand_id' }} value={a} onChange={vi.fn()} />
80
+ </ApiProvider>,
81
+ )
82
+ await waitFor(() => expect(screen.getAllByText('Michelin')).toHaveLength(2))
83
+ expect(screen.getByText('Pirelli')).toBeTruthy()
84
+ expect(host.get).toHaveBeenCalledTimes(1)
85
+ const ids = String(host.idsCalls()[0][1].params.ids).split(',').sort()
86
+ expect(ids).toEqual([a, b].sort())
87
+ })
88
+
89
+ it('con semilla → cero peticiones', async () => {
90
+ const id = uid('b')
91
+ const host = idsHost([{ id, label: 'Michelin' }])
92
+ render(
93
+ <ApiProvider client={host.api}>
94
+ <DynamicSelectField
95
+ field={brand}
96
+ value={id}
97
+ onChange={vi.fn()}
98
+ seedOption={{ id, value: id, label: 'Bridgestone', name: 'Bridgestone' }}
99
+ />
100
+ </ApiProvider>,
101
+ )
102
+ expect(screen.getByText('Bridgestone')).toBeTruthy()
103
+ await new Promise((r) => setTimeout(r, 30))
104
+ expect(host.get).not.toHaveBeenCalled()
105
+ })
106
+
107
+ it('id que ya no existe → «(registro eliminado)» sutil, no el UUID', async () => {
108
+ const id = uid('b')
109
+ const host = idsHost([])
110
+ render(
111
+ <ApiProvider client={host.api}>
112
+ <DynamicSelectField field={brand} value={id} onChange={vi.fn()} />
113
+ </ApiProvider>,
114
+ )
115
+ const missing = await screen.findByText('(registro eliminado)')
116
+ expect(missing.getAttribute('data-slot')).toBe('record-picker-value-missing')
117
+ expect(screen.queryByText(id)).toBeNull()
118
+ })
119
+
120
+ it('host sin soporte de ids (responde una página): filtra por id; si no está, muestra el valor como antes', async () => {
121
+ const known = uid('b')
122
+ const unknown = uid('b')
123
+ const page = [
124
+ { id: known, label: 'Michelin' },
125
+ { id: uid('x'), label: 'Otra' },
126
+ ]
127
+ // Ignores `ids` entirely.
128
+ const get = vi.fn(async () => ({ data: { success: true, data: page } }))
129
+ const api = { get, post: vi.fn(async () => ({ data: {} })), put: vi.fn(), delete: vi.fn() } as unknown as ApiClient
130
+ render(
131
+ <ApiProvider client={api}>
132
+ <DynamicSelectField field={brand} value={known} onChange={vi.fn()} />
133
+ <DynamicSelectField field={{ ...brand, key: 'b2' }} value={unknown} onChange={vi.fn()} />
134
+ </ApiProvider>,
135
+ )
136
+ expect(await screen.findByText('Michelin')).toBeTruthy()
137
+ expect(await screen.findByText(unknown)).toBeTruthy()
138
+ expect(screen.queryByText('(registro eliminado)')).toBeNull()
139
+ })
140
+
141
+ it('usa el QueryClient del host cuando hay QueryClientProvider', async () => {
142
+ const id = uid('b')
143
+ const host = idsHost([{ id, label: 'Goodyear' }])
144
+ const client = new QueryClient()
145
+ render(
146
+ <QueryClientProvider client={client}>
147
+ <ApiProvider client={host.api}>
148
+ <DynamicSelectField field={brand} value={id} onChange={vi.fn()} />
149
+ </ApiProvider>
150
+ </QueryClientProvider>,
151
+ )
152
+ expect(await screen.findByText('Goodyear')).toBeTruthy()
153
+ expect(client.getQueryCache().findAll({ queryKey: ['metacore-option-ids'] })).toHaveLength(1)
154
+ })
155
+
156
+ it('múltiple: los ids fuera de la página se resuelven en una petición; el borrado se marca', async () => {
157
+ const inPage = uid('t')
158
+ const outside = uid('t')
159
+ const gone = uid('t')
160
+ const host = idsHost(
161
+ [
162
+ { id: inPage, label: 'Mayoreo' },
163
+ { id: outside, label: 'Flotillas' },
164
+ ],
165
+ [{ id: inPage, label: 'Mayoreo' }],
166
+ )
167
+ const field: ActionFieldDef = { key: 'segment_ids', label: 'Segmentos', type: 'dynamic_select', ref: 'Segment', multiple: true }
168
+ render(
169
+ <ApiProvider client={host.api} batch={false}>
170
+ <DynamicMultiSelectField field={field} value={[inPage, outside, gone]} onChange={vi.fn()} />
171
+ </ApiProvider>,
172
+ )
173
+ expect(await screen.findByText('Flotillas')).toBeTruthy()
174
+ expect(screen.getByText('Mayoreo')).toBeTruthy()
175
+ expect(await screen.findByText('(registro eliminado)')).toBeTruthy()
176
+ expect(screen.queryByText(outside)).toBeNull()
177
+ const calls = host.idsCalls()
178
+ expect(calls).toHaveLength(1)
179
+ expect(String(calls[0][1].params.ids).split(',').sort()).toEqual([outside, gone].sort())
180
+ })
181
+ })
@@ -17,11 +17,17 @@
17
17
  // (segments, tags, categories — tens, not thousands of rows); a field with a
18
18
  // genuinely large option set should keep using a single dynamic_select per
19
19
  // value instead.
20
+ //
21
+ // Selected ids outside that page (or hidden by an option_filter) are labelled
22
+ // with ONE `?ids=` lookup (useResolveOptionIds): a chip reads "Cargando…" while
23
+ // it resolves and "(registro eliminado)" when the record is gone — never the id.
20
24
  import { useMemo, useState } from 'react'
21
25
  import { getOptionFilter } from './option-filter'
22
26
  import { RecordPicker } from './record-picker'
23
27
  import { OptionLead } from './record-picker-option'
24
28
  import { useOptionsResolver, type ResolvedOption } from './use-options-resolver'
29
+ import { useResolveOptionIds } from './use-option-ids'
30
+ import { DELETED_RECORD_LABEL } from './dynamic-select-field'
25
31
  import { getFieldRef } from './dynamic-form-schema'
26
32
  import type { ActionFieldDef } from './types'
27
33
 
@@ -35,7 +41,7 @@ export interface DynamicMultiSelectFieldProps {
35
41
  export function DynamicMultiSelectField({ field, value, onChange }: DynamicMultiSelectFieldProps) {
36
42
  const ref = getFieldRef(field)
37
43
  const [query, setQuery] = useState('')
38
- const { options, loading } = useOptionsResolver({
44
+ const { options, loading, meta, error } = useOptionsResolver({
39
45
  modelKey: '',
40
46
  fieldKey: 'id',
41
47
  ref,
@@ -45,13 +51,19 @@ export function DynamicMultiSelectField({ field, value, onChange }: DynamicMulti
45
51
  })
46
52
 
47
53
  const selected = useMemo(() => (Array.isArray(value) ? value.map(String) : []), [value])
48
- const selectedItems = useMemo(
49
- () =>
50
- selected
51
- .map((id) => options.find((o) => String(o.id) === id))
52
- .filter((o): o is ResolvedOption => !!o),
53
- [selected, options],
54
- )
54
+ const endpoint = !ref && field.searchEndpoint ? field.searchEndpoint : undefined
55
+ // Ask for the ids the loaded page does not cover — only once it is in.
56
+ const pageSettled = !loading && (meta !== null || error !== null)
57
+ const unresolved = pageSettled ? selected.filter((id) => !options.some((o) => String(o.id) === id)) : []
58
+ const { resolved } = useResolveOptionIds({ ref, endpoint, field: 'id', ids: unresolved, enabled: unresolved.length > 0 })
59
+ const selectedItems = selected.map((id): ResolvedOption => {
60
+ const loaded = options.find((o) => String(o.id) === id)
61
+ if (loaded) return loaded
62
+ const r = pageSettled ? resolved.get(id) : undefined
63
+ if (r?.status === 'found') return r.option
64
+ const label = !r ? 'Cargando…' : r.status === 'missing' ? DELETED_RECORD_LABEL : id
65
+ return { id, value: id, label, name: label }
66
+ })
55
67
  const shown = useMemo(() => {
56
68
  const q = query.trim().toLowerCase()
57
69
  return q ? options.filter((o) => String(o.label ?? '').toLowerCase().includes(q)) : options
@@ -18,10 +18,11 @@
18
18
  // 1. field.ref → /options/<ref>?field=id (canonical, preferred)
19
19
  // 2. field.searchEndpoint→ used verbatim as the options endpoint (escape hatch)
20
20
  //
21
- // Edit-mode caveat: resolving an EXISTING value's label requires the id to be
22
- // in a fetched page (we match by id against loaded options, else show the raw
23
- // value). A dedicated `?ids=` lookup is a follow-up; create flows — the common
24
- // case — start empty and never hit this.
21
+ // Edit mode: an EXISTING value is labelled from (in order) the option the user
22
+ // just picked, the loaded page, the caller's `seedOption`, and otherwise one
23
+ // `?ids=` lookup (useResolveOptionIds — batched and shared with every picker of
24
+ // the same ref on screen). While it resolves the trigger reads "Cargando…",
25
+ // never the raw id; an id that no longer exists reads "(registro eliminado)".
25
26
  import { useEffect, useRef, useState } from 'react'
26
27
  import { getOptionFilter } from './option-filter'
27
28
  import { useTranslation } from 'react-i18next'
@@ -32,6 +33,7 @@ import { RecordPicker } from './record-picker'
32
33
  import { OptionLead } from './record-picker-option'
33
34
  import { recordLabel, requestRecordCreate, requestRecordEdit, withSearchPrefill } from './record-picker-actions'
34
35
  import { useOptionsResolver, type ResolvedOption } from './use-options-resolver'
36
+ import { useResolveOptionIds } from './use-option-ids'
35
37
  import { useDebouncedValue } from './use-debounced-value'
36
38
  import { getDependsOn, getFieldRef, resolveOptionsSource } from './dynamic-form-schema'
37
39
  import type { ActionFieldDef } from './types'
@@ -45,6 +47,9 @@ export { OptionLead, OptionThumb } from './record-picker-option'
45
47
  */
46
48
  export const DEFAULT_DEPENDS_HINT = 'Selecciona primero el campo del que depende'
47
49
 
50
+ /** Label of a held value whose record no longer exists (or is not visible). */
51
+ export const DELETED_RECORD_LABEL = '(registro eliminado)'
52
+
48
53
  export interface DynamicSelectFieldProps {
49
54
  field: ActionFieldDef
50
55
  value: any
@@ -168,21 +173,23 @@ export function DynamicSelectField({
168
173
  const useStatic = Array.isArray(staticOptions)
169
174
  const optionFilter = getOptionFilter(field)
170
175
 
176
+ // optionsConfig.source → `/options/<source>`. Else searchEndpoint only
177
+ // drives the URL when there's no ref (ref is canonical and wins).
178
+ const optionsEndpoint = source.endpoint ?? (source.ref ? undefined : field.searchEndpoint)
171
179
  const { options: fetchedOptions, loading: fetchLoading } = useOptionsResolver({
172
180
  modelKey: '',
173
181
  fieldKey: source.fieldKey,
174
182
  ref: source.ref,
175
- // optionsConfig.source → `/options/<source>`. Else searchEndpoint only
176
- // drives the URL when there's no ref (ref is canonical and wins).
177
- endpoint: source.endpoint ?? (source.ref ? undefined : field.searchEndpoint),
183
+ endpoint: optionsEndpoint,
178
184
  query: debounced,
179
185
  limit: 20,
180
186
  filterValue: dependsOn ? scope : undefined,
181
187
  optionFilter,
182
188
  keepValue: value,
183
- // Fetch only while open; a readonly cell fetches eagerly so its label
184
- // resolves to the name. Blocked cascades and static lists never fetch.
185
- enabled: !useStatic && (open || readonly) && !blockedByDependency,
189
+ // Fetch only while open (a held value's label comes from the `?ids=`
190
+ // lookup below, readonly included). Blocked cascades and static lists
191
+ // never fetch.
192
+ enabled: !useStatic && open && !blockedByDependency,
186
193
  })
187
194
 
188
195
  const options = useStatic
@@ -208,12 +215,37 @@ export function DynamicSelectField({
208
215
  }
209
216
  }, [dependsOn, scope, value, onChange])
210
217
 
211
- const selectedOption =
212
- (picked && String(picked.id) === String(value) ? picked : null) ??
213
- options.find((o) => String(o.id) === String(value)) ??
214
- (seedOption && String(seedOption.id) === String(value) ? seedOption : null) ??
218
+ const valueKey = value != null && value !== '' ? String(value) : ''
219
+ const knownOption =
220
+ (picked && String(picked.id) === valueKey ? picked : null) ??
221
+ options.find((o) => String(o.id) === valueKey) ??
222
+ (seedOption && String(seedOption.id) === valueKey ? seedOption : null) ??
215
223
  null
216
- const selectedLabel = selectedOption?.label ?? (value ? String(value) : '')
224
+ // A held value nobody has labelled yet: one `?ids=` lookup (no popover).
225
+ const needsLookup = !!valueKey && !knownOption && !useStatic
226
+ const { resolved, loading: idsLoading } = useResolveOptionIds({
227
+ ref: source.ref,
228
+ endpoint: optionsEndpoint,
229
+ field: source.fieldKey,
230
+ ids: needsLookup ? [valueKey] : [],
231
+ enabled: needsLookup,
232
+ })
233
+ const resolution = needsLookup ? resolved.get(valueKey) : undefined
234
+ const selectedOption = knownOption ?? (resolution?.status === 'found' ? resolution.option : null)
235
+ const valueResolving = needsLookup && !selectedOption && idsLoading
236
+ const valueMissing = needsLookup && !selectedOption && resolution?.status === 'missing'
237
+ const selectedLabel =
238
+ selectedOption?.label ?? (valueResolving ? '' : valueMissing ? DELETED_RECORD_LABEL : valueKey)
239
+ const resolvingNode = (
240
+ <span className="min-w-0 flex-1 truncate animate-pulse text-muted-foreground" data-slot="record-picker-value-loading">
241
+ Cargando…
242
+ </span>
243
+ )
244
+ const missingNode = (
245
+ <span className="min-w-0 flex-1 truncate italic text-muted-foreground" data-slot="record-picker-value-missing">
246
+ {DELETED_RECORD_LABEL}
247
+ </span>
248
+ )
217
249
 
218
250
  const handlePick = (opt: ResolvedOption) => {
219
251
  setPicked(opt)
@@ -291,13 +323,15 @@ export function DynamicSelectField({
291
323
  getDescription={descriptionAsBadge ? undefined : (o) => o.description}
292
324
  renderTrailing={descriptionAsBadge ? (o) => badge(o) : undefined}
293
325
  renderValue={() => {
326
+ // Never flash the raw id while the label resolves.
327
+ if (valueResolving) return resolvingNode
328
+ if (valueMissing) return missingNode
294
329
  if (readonly) {
295
330
  return (
296
331
  <>
297
332
  {selectedOption ? <OptionLead option={selectedOption} size={20} /> : null}
298
333
  <span className={'min-w-0 flex-1 truncate ' + (selectedOption ? '' : 'text-muted-foreground')}>
299
- {/* Never flash the raw id while the eager fetch resolves. */}
300
- {selectedOption?.label ?? (loading ? 'Cargando…' : ph('—'))}
334
+ {selectedOption?.label ?? ph('—')}
301
335
  </span>
302
336
  </>
303
337
  )
package/src/index.ts CHANGED
@@ -588,6 +588,14 @@ export {
588
588
  type UseOptionsResolverArgs,
589
589
  type UseOptionsResolverResult,
590
590
  } from './use-options-resolver'
591
+ export {
592
+ useResolveOptionIds,
593
+ OPTION_IDS_CHUNK,
594
+ type OptionIdResolution,
595
+ type UseResolveOptionIdsArgs,
596
+ type UseResolveOptionIdsResult,
597
+ } from './use-option-ids'
598
+ export { DELETED_RECORD_LABEL } from './dynamic-select-field'
591
599
  export {
592
600
  setOrgConfigBridge,
593
601
  getOrgConfigBridge,
@@ -375,6 +375,9 @@ export function tokenForGet(url: string, params?: Record<string, unknown>): stri
375
375
  if (value != null && value !== '') search.set(key, String(value))
376
376
  }
377
377
  }
378
+ // An `?ids=` resolve is not a page: the `o:` token has no slot for it, so
379
+ // it goes as its own GET instead of being turned into a first-page read.
380
+ if (search.has('ids')) return null
378
381
  const limit = Number(search.get('limit'))
379
382
  return optionsBatchToken(
380
383
  opt[1],
@@ -0,0 +1,168 @@
1
+ // useResolveOptionIds — labels for values a picker already holds.
2
+ //
3
+ // A RecordPicker editing a saved record (a product with its brand) holds an id
4
+ // whose label is in no loaded page: options load only once the popover opens,
5
+ // so the trigger used to show the raw UUID unless the caller had a seed (the
6
+ // relation sibling the table served). This hook asks the canonical options
7
+ // endpoint for exactly those ids:
8
+ //
9
+ // GET /api/options/<ref>?field=id&ids=a,b
10
+ //
11
+ // - One react-query entry per (org/branch, endpoint, field, id): two pickers
12
+ // on the same value share it, and nothing is refetched for 30 s.
13
+ // - Every id requested within a few ms for the same endpoint + field travels in
14
+ // ONE request (a column of cells, a multi-select's chips), chunked at the
15
+ // kernel's 100-id cap.
16
+ // - A host that predates `ids` ignores the parameter and answers a first page.
17
+ // The answer is then filtered by id: a hit still resolves, a miss stays
18
+ // `unknown` (the caller shows the value as before) instead of being reported
19
+ // as deleted. A response that only holds requested ids is a host that
20
+ // understood `ids`, so a requested id absent from it is `missing`.
21
+ import { useContext, useMemo } from 'react'
22
+ import { QueryClient, QueryClientContext, useQueries } from '@tanstack/react-query'
23
+ import { useApi, type ApiClient } from './api-context'
24
+ import { onOptionsCacheInvalidated, optionsScope, projectOption, type ResolvedOption } from './use-options-resolver'
25
+
26
+ export type OptionIdResolution =
27
+ | { status: 'found'; option: ResolvedOption }
28
+ /** The host resolved the ids and this one is not there (deleted, or not visible). */
29
+ | { status: 'missing' }
30
+ /** Could not tell (old host, error): show the raw value. */
31
+ | { status: 'unknown' }
32
+
33
+ /** Ids the kernel accepts per request (dynamic.MaxOptionsIDs). */
34
+ export const OPTION_IDS_CHUNK = 100
35
+ const OPTION_IDS_TTL_MS = 30_000
36
+ const OPTION_IDS_WINDOW_MS = 5
37
+ const QUERY_ROOT = 'metacore-option-ids'
38
+
39
+ // Hosts without a QueryClientProvider still get dedup + cache.
40
+ let fallbackClient: QueryClient | null = null
41
+ function getFallbackClient(): QueryClient {
42
+ if (!fallbackClient) fallbackClient = new QueryClient({ defaultOptions: { queries: { retry: false } } })
43
+ return fallbackClient
44
+ }
45
+
46
+ const knownClients = new Set<QueryClient>()
47
+ onOptionsCacheInvalidated(() => {
48
+ for (const client of knownClients) void client.invalidateQueries({ queryKey: [QUERY_ROOT] })
49
+ })
50
+
51
+ type Waiter = { resolve: (r: OptionIdResolution) => void }
52
+ type Pending = { api: ApiClient; url: string; field: string; waiters: Map<string, Waiter[]> }
53
+ const pendingBatches = new Map<string, Pending>()
54
+
55
+ function enqueue(api: ApiClient, scope: string, url: string, field: string, id: string): Promise<OptionIdResolution> {
56
+ const key = [scope, url, field].join('\n')
57
+ // Keyed without the client: useApi() hands each component its own wrapper
58
+ // of the same host client, and pickers of one ref must share the request.
59
+ let batch = pendingBatches.get(key)
60
+ if (!batch) {
61
+ batch = { api, url, field, waiters: new Map() }
62
+ pendingBatches.set(key, batch)
63
+ const flushing = batch
64
+ setTimeout(() => {
65
+ if (pendingBatches.get(key) === flushing) pendingBatches.delete(key)
66
+ void flush(flushing)
67
+ }, OPTION_IDS_WINDOW_MS)
68
+ }
69
+ const target = batch
70
+ return new Promise((resolve) => {
71
+ const list = target.waiters.get(id) ?? []
72
+ list.push({ resolve })
73
+ target.waiters.set(id, list)
74
+ })
75
+ }
76
+
77
+ async function flush(batch: Pending): Promise<void> {
78
+ const ids = [...batch.waiters.keys()]
79
+ for (let i = 0; i < ids.length; i += OPTION_IDS_CHUNK) {
80
+ const chunk = ids.slice(i, i + OPTION_IDS_CHUNK)
81
+ const result = await fetchIds(batch.api, batch.url, batch.field, chunk)
82
+ for (const id of chunk) {
83
+ const r = result.get(id) ?? { status: 'unknown' as const }
84
+ for (const w of batch.waiters.get(id) ?? []) w.resolve(r)
85
+ }
86
+ }
87
+ }
88
+
89
+ async function fetchIds(api: ApiClient, url: string, field: string, ids: string[]): Promise<Map<string, OptionIdResolution>> {
90
+ const out = new Map<string, OptionIdResolution>()
91
+ try {
92
+ const res = await api.get(url, { params: { field, ids: ids.join(',') } })
93
+ const body = (res as { data: any })?.data
94
+ if (!body || body.success !== true || !Array.isArray(body.data)) return out
95
+ const options = (body.data as unknown[]).map(projectOption)
96
+ const byId = new Map<string, ResolvedOption>()
97
+ for (const o of options) byId.set(String(o.id).toLowerCase(), o)
98
+ const asked = new Set(ids.map((id) => id.toLowerCase()))
99
+ // Only requested ids back → the host honoured `ids`.
100
+ const honoured = options.every((o) => asked.has(String(o.id).toLowerCase()))
101
+ for (const id of ids) {
102
+ const hit = byId.get(id.toLowerCase())
103
+ if (hit) out.set(id, { status: 'found', option: hit })
104
+ else if (honoured) out.set(id, { status: 'missing' })
105
+ }
106
+ } catch {
107
+ // Network / permission error: leave every id `unknown`.
108
+ }
109
+ return out
110
+ }
111
+
112
+ export interface UseResolveOptionIdsArgs {
113
+ /** FK target: `/options/<ref>`. */
114
+ ref?: string
115
+ /** Explicit options endpoint (wins over `ref`), as in useOptionsResolver. */
116
+ endpoint?: string
117
+ /** `?field=`; defaults to `id` with a `ref`. */
118
+ field?: string
119
+ /** Ids to label. Pass only those not already loaded / seeded. */
120
+ ids: readonly (string | number)[]
121
+ enabled?: boolean
122
+ }
123
+
124
+ export interface UseResolveOptionIdsResult {
125
+ /** Settled resolutions by id (as given, stringified). */
126
+ resolved: ReadonlyMap<string, OptionIdResolution>
127
+ /** Some id is still being resolved. */
128
+ loading: boolean
129
+ }
130
+
131
+ export function useResolveOptionIds(args: UseResolveOptionIdsArgs): UseResolveOptionIdsResult {
132
+ const { ref, endpoint, field, ids, enabled = true } = args
133
+ const api = useApi()
134
+ const contextClient = useContext(QueryClientContext)
135
+ const client = contextClient ?? getFallbackClient()
136
+ knownClients.add(client)
137
+
138
+ const url = endpoint || (ref ? `/options/${ref}` : '')
139
+ const effectiveField = field || (ref ? 'id' : '')
140
+ const scope = optionsScope()
141
+ const list = useMemo(() => [...new Set(ids.map(String).filter((id) => id !== ''))], [ids.map(String).join('\u0001')])
142
+ const active = enabled && !!url && !!effectiveField && list.length > 0
143
+
144
+ const results = useQueries(
145
+ {
146
+ queries: (active ? list : []).map((id) => ({
147
+ queryKey: [QUERY_ROOT, scope, url, effectiveField, id],
148
+ queryFn: () => enqueue(api, scope, url, effectiveField, id),
149
+ staleTime: OPTION_IDS_TTL_MS,
150
+ retry: false,
151
+ refetchOnWindowFocus: false,
152
+ })),
153
+ },
154
+ client,
155
+ )
156
+
157
+ const resolved = new Map<string, OptionIdResolution>()
158
+ let loading = false
159
+ if (active) {
160
+ list.forEach((id, i) => {
161
+ const r = results[i]
162
+ if (r?.data) resolved.set(id, r.data)
163
+ else if (r?.isError) resolved.set(id, { status: 'unknown' })
164
+ else loading = true
165
+ })
166
+ }
167
+ return { resolved, loading }
168
+ }
@@ -139,6 +139,15 @@ export function invalidateOptionsCache(): void {
139
139
  optionsGeneration++
140
140
  optionsCache.clear()
141
141
  optionsInflight.clear()
142
+ for (const fn of invalidationListeners) fn()
143
+ }
144
+
145
+ const invalidationListeners = new Set<() => void>()
146
+
147
+ /** Runs `fn` on every invalidateOptionsCache (id-label lookups drop with it). */
148
+ export function onOptionsCacheInvalidated(fn: () => void): () => void {
149
+ invalidationListeners.add(fn)
150
+ return () => invalidationListeners.delete(fn)
142
151
  }
143
152
 
144
153
  export function optionsRequestKey(
@@ -152,7 +161,8 @@ export function optionsRequestKey(
152
161
  return [scope, url, field, query, String(limit ?? ''), filter ?? ''].join('\n')
153
162
  }
154
163
 
155
- function optionsScope(): string {
164
+ /** Org + branch of the host session: part of every options cache key. */
165
+ export function optionsScope(): string {
156
166
  if (typeof localStorage === 'undefined') return ''
157
167
  let org = ''
158
168
  try {