@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 +6 -0
- package/dist/dynamic-multi-select-field.d.ts.map +1 -1
- package/dist/dynamic-multi-select-field.js +22 -4
- package/dist/dynamic-select-field.d.ts +2 -0
- package/dist/dynamic-select-field.d.ts.map +1 -1
- package/dist/dynamic-select-field.js +42 -15
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/query-batch.d.ts.map +1 -1
- package/dist/query-batch.js +4 -0
- package/dist/use-option-ids.d.ts +34 -0
- package/dist/use-option-ids.d.ts.map +1 -0
- package/dist/use-option-ids.js +138 -0
- package/dist/use-options-resolver.d.ts +4 -0
- package/dist/use-options-resolver.d.ts.map +1 -1
- package/dist/use-options-resolver.js +10 -1
- package/package.json +1 -1
- package/src/__tests__/edit-field-polish.test.tsx +6 -3
- package/src/__tests__/option-ids.test.tsx +181 -0
- package/src/dynamic-multi-select-field.tsx +20 -8
- package/src/dynamic-select-field.tsx +51 -17
- package/src/index.ts +8 -0
- package/src/query-batch.ts +3 -0
- package/src/use-option-ids.ts +168 -0
- package/src/use-options-resolver.ts +11 -1
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":"
|
|
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
|
|
40
|
-
|
|
41
|
-
|
|
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":"
|
|
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
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
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
|
-
|
|
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
|
|
94
|
-
//
|
|
95
|
-
|
|
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
|
|
120
|
-
|
|
121
|
-
(
|
|
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
|
-
|
|
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 ??
|
|
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';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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,
|
|
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"}
|
package/dist/query-batch.js
CHANGED
|
@@ -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,
|
|
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
|
-
|
|
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
|
@@ -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 →
|
|
168
|
-
|
|
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
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
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
|
-
|
|
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
|
|
184
|
-
//
|
|
185
|
-
|
|
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
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
(
|
|
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
|
-
|
|
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
|
-
{
|
|
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,
|
package/src/query-batch.ts
CHANGED
|
@@ -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
|
-
|
|
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 {
|