@asteby/metacore-runtime-react 37.1.1 → 37.2.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,21 @@
1
1
  # @asteby/metacore-runtime-react
2
2
 
3
+ ## 37.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 362ffda: Add `useResource`/`useMutation` — a tanstack-free polling GET/mutation hook for federated addons, with a `stuck` field on `useResource` that flips true once `isLoading` stays on past a configurable threshold (default 8s) without landing. Lets addon UIs offer a retry instead of an indefinite skeleton when a fetch run gets lost to a remount/abort race. Extracted from the pattern independently reimplemented in the `pos` addon (PRs #1455, #1460).
8
+
9
+ ### Patch Changes
10
+
11
+ - 1e35269: Preserve `?action=` in DynamicTable URL sync so notification deep-links can open ActionModalDispatcher on list routes.
12
+
13
+ ## 37.1.2
14
+
15
+ ### Patch Changes
16
+
17
+ - 2d4e1d8: Fail closed when an action declares `modal` but no federated component is registered — never fall back to the generic confirm dialog.
18
+
3
19
  ## 37.1.1
4
20
 
5
21
  ### Patch Changes
@@ -1 +1 @@
1
- {"version":3,"file":"action-modal-dispatcher.d.ts","sourceRoot":"","sources":["../src/action-modal-dispatcher.tsx"],"names":[],"mappings":"AAoEA,OAAO,KAAK,EAAE,cAAc,EAAmC,MAAM,SAAS,CAAA;AAE9E,OAAO,EACH,KAAK,cAAc,EACnB,KAAK,gBAAgB,EAExB,MAAM,sBAAsB,CAAA;AAE7B,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,CAAA;AAiBhD,MAAM,WAAW,WAAW;IACxB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5B,SAAS,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAC1D;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;CAClB;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,WAAW,CAM1D;AAsBD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,cAAc,GAAG,cAAc,CAStE;AAGD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAkB3F;AASD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAY1D;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CASjE;AAUD,oEAAoE;AACpE,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,GAAG,OAAO,CAgBnF;AAED,wBAAgB,qBAAqB,CAAC,EAClC,IAAI,EACJ,YAAY,EACZ,MAAM,EACN,KAAK,EACL,MAAM,EACN,QAAQ,EACR,SAAS,GACZ,EAAE,gBAAgB,sCAoElB"}
1
+ {"version":3,"file":"action-modal-dispatcher.d.ts","sourceRoot":"","sources":["../src/action-modal-dispatcher.tsx"],"names":[],"mappings":"AAsEA,OAAO,KAAK,EAAE,cAAc,EAAmC,MAAM,SAAS,CAAA;AAE9E,OAAO,EACH,KAAK,cAAc,EACnB,KAAK,gBAAgB,EAExB,MAAM,sBAAsB,CAAA;AAE7B,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,CAAA;AAiBhD,MAAM,WAAW,WAAW;IACxB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5B,SAAS,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAC1D;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;CAClB;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,WAAW,CAM1D;AAsBD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,cAAc,GAAG,cAAc,CAStE;AAGD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAkB3F;AASD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAY1D;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CASjE;AAUD,oEAAoE;AACpE,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,GAAG,OAAO,CAgBnF;AAED,wBAAgB,qBAAqB,CAAC,EAClC,IAAI,EACJ,YAAY,EACZ,MAAM,EACN,KAAK,EACL,MAAM,EACN,QAAQ,EACR,SAAS,GACZ,EAAE,gBAAgB,sCAkFlB"}
@@ -1,11 +1,13 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  // ActionModalDispatcher — renders the right modal for a custom action:
3
3
  // 1) Custom component from the SDK registry → use it
4
- // 2) action.fields[] / action.steps[] → GenericActionModal / WizardActionModal
5
- // 3) action.confirm OR action.confirmMessage → ConfirmActionDialog
6
- // 4) action.executable (host opened the modal; federated UI missing) →
4
+ // 2) action.modal set but no registered component → MissingCustomActionModal
5
+ // (NEVER fall back to confirm/fields — that hides a broken federated UI)
6
+ // 3) action.fields[] / action.steps[] → GenericActionModal / WizardActionModal
7
+ // 4) action.confirm OR action.confirmMessage → ConfirmActionDialog
8
+ // 5) action.executable (host opened the modal; no modal/fields/confirm) →
7
9
  // ConfirmActionDialog so a click never silently no-ops
8
- // 5) otherwise → null (caller should execute immediately without opening us)
10
+ // 6) otherwise → null (caller should execute immediately without opening us)
9
11
  //
10
12
  // The host injects its axios-like client via <ApiProvider>; we no longer
11
13
  // depend on a bundler alias to `@/lib/api`.
@@ -158,6 +160,11 @@ export function ActionModalDispatcher({ open, onOpenChange, action, model, recor
158
160
  if (CustomComponent) {
159
161
  return (_jsx(CustomComponent, { open: open, onOpenChange: onOpenChange, action: action, model: model, record: record, endpoint: endpoint, onSuccess: onSuccess }));
160
162
  }
163
+ // Declarative custom slot (`modal: "addon.action"`). Prefer a hard error
164
+ // over confirm/fields generics — those look "fine" and hide a missing remote.
165
+ if (action.modal) {
166
+ return (_jsx(MissingCustomActionModal, { open: open, onOpenChange: onOpenChange, action: action, model: model }));
167
+ }
161
168
  if (action.steps && action.steps.length > 0) {
162
169
  return (_jsx(WizardActionModal, { open: open, onOpenChange: onOpenChange, action: action, model: model, record: record, endpoint: endpoint, onSuccess: onSuccess }));
163
170
  }
@@ -168,12 +175,28 @@ export function ActionModalDispatcher({ open, onOpenChange, action, model, recor
168
175
  // boolean). Hosts also mark every wasm action `executable` and open THIS
169
176
  // dispatcher; without a confirm/fields/custom UI we used to return null and
170
177
  // the row click did nothing. Treat message-only + executable-open as confirm.
178
+ // Never reached when action.modal is set (handled above).
171
179
  const wantsConfirm = !!(action.confirm || action.confirmMessage);
172
180
  if (wantsConfirm || (open && action.executable)) {
173
181
  return (_jsx(ConfirmActionDialog, { open: open, onOpenChange: onOpenChange, action: action, model: model, record: record, endpoint: endpoint, onSuccess: onSuccess }));
174
182
  }
175
183
  return null;
176
184
  }
185
+ /** Shown when the action declares `modal` but no federated component registered. */
186
+ function MissingCustomActionModal({ open, onOpenChange, action, model, }) {
187
+ const { t } = useTranslation();
188
+ const slug = action.modal || '';
189
+ const title = t('dynamic.action_modal_missing_title', {
190
+ defaultValue: 'No se pudo cargar el formulario',
191
+ });
192
+ const description = t('dynamic.action_modal_missing_description', {
193
+ defaultValue: 'Esta acción requiere una interfaz personalizada ({{slug}}) que no está registrada. Recarga la página o reinstala el módulo; no se abre un confirmatorio genérico para no confundir el flujo.',
194
+ slug: slug || `${model}.${action.key}`,
195
+ action: action.key,
196
+ model,
197
+ });
198
+ return (_jsx(AlertDialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: title }), _jsx(AlertDialogDescription, { children: description })] }), _jsx(AlertDialogFooter, { children: _jsx(AlertDialogAction, { onClick: () => onOpenChange(false), children: t('common.close', { defaultValue: 'Cerrar' }) }) })] }) }));
199
+ }
177
200
  function buildActionUrl(endpoint, model, recordId, actionKey) {
178
201
  // A create-placement (collection) action has no record yet, so `recordId`
179
202
  // is undefined. Omit the `/{id}` segment so the request hits the collection
@@ -1 +1 @@
1
- {"version":3,"file":"dynamic-table.d.ts","sourceRoot":"","sources":["../src/dynamic-table.tsx"],"names":[],"mappings":"AAgBA,OAAO,EAKH,KAAK,SAAS,EAajB,MAAM,uBAAuB,CAAA;AAgC9B,OAAO,KAAK,EAAsB,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAkGnF,MAAM,WAAW,iBAAiB;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,6DAA6D;IAC7D,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB;;;;;;;;;OASG;IACH,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC5B,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,IAAI,CAAA;IAC7C;;;;;OAKG;IACH,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,CAAA;IAC/B,cAAc,CAAC,EAAE,GAAG,CAAA;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACpC,YAAY,CAAC,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,CAAA;IAC/B;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;IACrC;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;;;;;;;;;;OAaG;IACH,UAAU,CAAC,EAAE,OAAO,GAAG,UAAU,CAAA;IACjC;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;CAC3B;AAED,wBAAgB,YAAY,CAAC,EACzB,KAAK,EACL,QAAQ,EACR,gBAAgB,EAChB,aAAoB,EACpB,UAAU,EACV,UAAU,EACV,aAAkB,EAClB,iBAAiB,EACjB,QAAQ,EACR,UAAU,EACV,cAAc,EACd,cAAc,EACd,YAAiB,EACjB,iBAA4C,EAC5C,QAAQ,EACR,QAAQ,EACR,UAAU,EAAE,cAAc,EAC1B,cAAc,EAAE,kBAA0B,GAC7C,EAAE,iBAAiB,+BA02CnB"}
1
+ {"version":3,"file":"dynamic-table.d.ts","sourceRoot":"","sources":["../src/dynamic-table.tsx"],"names":[],"mappings":"AAgBA,OAAO,EAKH,KAAK,SAAS,EAajB,MAAM,uBAAuB,CAAA;AAgC9B,OAAO,KAAK,EAAsB,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAkGnF,MAAM,WAAW,iBAAiB;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,6DAA6D;IAC7D,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB;;;;;;;;;OASG;IACH,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC5B,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,IAAI,CAAA;IAC7C;;;;;OAKG;IACH,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,CAAA;IAC/B,cAAc,CAAC,EAAE,GAAG,CAAA;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACpC,YAAY,CAAC,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,CAAA;IAC/B;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;IACrC;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;;;;;;;;;;OAaG;IACH,UAAU,CAAC,EAAE,OAAO,GAAG,UAAU,CAAA;IACjC;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;CAC3B;AAED,wBAAgB,YAAY,CAAC,EACzB,KAAK,EACL,QAAQ,EACR,gBAAgB,EAChB,aAAoB,EACpB,UAAU,EACV,UAAU,EACV,aAAkB,EAClB,iBAAiB,EACjB,QAAQ,EACR,UAAU,EACV,cAAc,EACd,cAAc,EACd,YAAiB,EACjB,iBAA4C,EAC5C,QAAQ,EACR,QAAQ,EACR,UAAU,EAAE,cAAc,EAC1B,cAAc,EAAE,kBAA0B,GAC7C,EAAE,iBAAiB,+BA62CnB"}
@@ -340,7 +340,10 @@ export function DynamicTable({ model, endpoint, mutationEndpoint, enableUrlSync
340
340
  // the URL goes bare, and the sidebar falls back to the model default
341
341
  // (the "click twice to move the active to the right entry" bug).
342
342
  const current = new URLSearchParams(window.location.search);
343
- for (const key of ['view', 'group_by']) {
343
+ // `action` is host-owned (?action=stamp_fiscal deep-links from
344
+ // notifications). Without carrying it, the first URL rewrite drops it
345
+ // before the list route can open ActionModalDispatcher.
346
+ for (const key of ['view', 'group_by', 'action']) {
344
347
  const v = current.get(key);
345
348
  if (v)
346
349
  params.set(key, v);
package/dist/index.d.ts CHANGED
@@ -14,6 +14,7 @@ export { useStageOverrides, type StageOverridePatch, type UseStageOverridesResul
14
14
  export { DynamicView, resolveViewRenderer, readViewFromSearch, resolveActiveView, type DynamicViewProps, } from './dynamic-view';
15
15
  export { useDynamicFilters, type UseDynamicFiltersOptions, type UseDynamicFiltersResult, } from './use-dynamic-filters';
16
16
  export { useDebouncedValue, SEARCH_DEBOUNCE_MS } from './use-debounced-value';
17
+ export { useResource, useMutation, type ResourceState, type MutationState, } from './use-resource';
17
18
  export * from './dynamic-form';
18
19
  export { FilePickButton, type FilePickButtonProps, } from './file-pick-button';
19
20
  export * from './barcode-scanner';
@@ -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
@@ -19,6 +19,7 @@ export { useStageOverrides, } from './stage-overrides';
19
19
  export { DynamicView, resolveViewRenderer, readViewFromSearch, resolveActiveView, } from './dynamic-view';
20
20
  export { useDynamicFilters, } from './use-dynamic-filters';
21
21
  export { useDebouncedValue, SEARCH_DEBOUNCE_MS } from './use-debounced-value';
22
+ export { useResource, useMutation, } from './use-resource';
22
23
  export * from './dynamic-form';
23
24
  export { FilePickButton, } from './file-pick-button';
24
25
  export * from './barcode-scanner';
@@ -0,0 +1,28 @@
1
+ export interface ResourceState<T> {
2
+ data: T | undefined;
3
+ isLoading: boolean;
4
+ error: Error | null;
5
+ refetch: () => void;
6
+ /**
7
+ * True once `isLoading` has stayed on for `stuckAfterMs` without landing.
8
+ * Whatever the exact timing race (an aborted fetch racing a remount, a
9
+ * slow/hung request), a caller needs a way OUT — a spinner with no escape
10
+ * hatch is a dead end. This never claims to know WHY the fetch is stuck;
11
+ * it only tells the caller long enough has passed to offer a retry
12
+ * instead of an indefinite loading state.
13
+ */
14
+ stuck: boolean;
15
+ }
16
+ export declare function useResource<T>(fetcher: (signal: AbortSignal) => Promise<T>, deps: ReadonlyArray<unknown>, options?: {
17
+ refetchInterval?: number;
18
+ enabled?: boolean;
19
+ /** Threshold in ms before `stuck` flips true. Default 8000. */
20
+ stuckAfterMs?: number;
21
+ }): ResourceState<T>;
22
+ export interface MutationState<TArgs, TResult> {
23
+ mutateAsync: (args: TArgs) => Promise<TResult>;
24
+ isPending: boolean;
25
+ error: Error | null;
26
+ }
27
+ export declare function useMutation<TArgs, TResult>(fn: (args: TArgs) => Promise<TResult>): MutationState<TArgs, TResult>;
28
+ //# sourceMappingURL=use-resource.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-resource.d.ts","sourceRoot":"","sources":["../src/use-resource.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,aAAa,CAAC,CAAC;IAC9B,IAAI,EAAE,CAAC,GAAG,SAAS,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB;;;;;;;OAOG;IACH,KAAK,EAAE,OAAO,CAAC;CAChB;AAID,wBAAgB,WAAW,CAAC,CAAC,EAC3B,OAAO,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,EAC5C,IAAI,EAAE,aAAa,CAAC,OAAO,CAAC,EAC5B,OAAO,CAAC,EAAE;IACR,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,+DAA+D;IAC/D,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,GACA,aAAa,CAAC,CAAC,CAAC,CAuElB;AAED,MAAM,WAAW,aAAa,CAAC,KAAK,EAAE,OAAO;IAC3C,WAAW,EAAE,CAAC,IAAI,EAAE,KAAK,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/C,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,EACxC,EAAE,EAAE,CAAC,IAAI,EAAE,KAAK,KAAK,OAAO,CAAC,OAAO,CAAC,GACpC,aAAa,CAAC,KAAK,EAAE,OAAO,CAAC,CAqB/B"}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Tiny data-fetching hooks for federated addons.
3
+ *
4
+ * Addon-safe replacement for `@tanstack/react-query`: pulling tanstack into a
5
+ * federated remote risks a duplicate query-client / version clash with the
6
+ * host and bloats the MF bundle (see @asteby/metacore-runtime-react's
7
+ * singleton react-query for HOST-side code, which does not apply inside a
8
+ * federated addon bundle). These hooks cover exactly what an addon panel
9
+ * needs — a polling GET resource and an imperative mutation — over any
10
+ * fetch-like client.
11
+ */
12
+ import { useCallback, useEffect, useRef, useState } from "react";
13
+ const DEFAULT_STUCK_AFTER_MS = 8_000;
14
+ export function useResource(fetcher, deps, options) {
15
+ const enabled = options?.enabled ?? true;
16
+ const stuckAfterMs = options?.stuckAfterMs ?? DEFAULT_STUCK_AFTER_MS;
17
+ const [data, setData] = useState(undefined);
18
+ const [isLoading, setIsLoading] = useState(enabled);
19
+ const [error, setError] = useState(null);
20
+ const fetcherRef = useRef(fetcher);
21
+ fetcherRef.current = fetcher;
22
+ const hasDataRef = useRef(false);
23
+ const [tick, setTick] = useState(0);
24
+ const [stuck, setStuck] = useState(false);
25
+ const refetch = useCallback(() => {
26
+ setStuck(false);
27
+ setTick((t) => t + 1);
28
+ }, []);
29
+ useEffect(() => {
30
+ if (!enabled) {
31
+ setIsLoading(false);
32
+ return;
33
+ }
34
+ const controller = new AbortController();
35
+ let active = true;
36
+ // Stale-while-revalidate: once we have data, refetches/polls revalidate in
37
+ // the background without flipping isLoading — the UI keeps showing the last
38
+ // values instead of collapsing to a "loading" blank every poll.
39
+ if (!hasDataRef.current)
40
+ setIsLoading(true);
41
+ setStuck(false);
42
+ const stuckTimer = !hasDataRef.current
43
+ ? setTimeout(() => {
44
+ if (active)
45
+ setStuck(true);
46
+ }, stuckAfterMs)
47
+ : undefined;
48
+ fetcherRef
49
+ .current(controller.signal)
50
+ .then((res) => {
51
+ if (!active)
52
+ return;
53
+ hasDataRef.current = true;
54
+ setData(res);
55
+ setError(null);
56
+ })
57
+ .catch((e) => {
58
+ if (controller.signal.aborted)
59
+ return;
60
+ if (!active)
61
+ return;
62
+ setError(e instanceof Error ? e : new Error(String(e)));
63
+ })
64
+ .finally(() => {
65
+ clearTimeout(stuckTimer);
66
+ if (active && !controller.signal.aborted) {
67
+ setIsLoading(false);
68
+ setStuck(false);
69
+ }
70
+ });
71
+ return () => {
72
+ active = false;
73
+ clearTimeout(stuckTimer);
74
+ controller.abort();
75
+ };
76
+ // eslint-disable-next-line react-hooks/exhaustive-deps
77
+ }, [enabled, tick, stuckAfterMs, ...deps]);
78
+ // Polling
79
+ const interval = options?.refetchInterval;
80
+ useEffect(() => {
81
+ if (!enabled || !interval)
82
+ return;
83
+ const id = setInterval(() => setTick((t) => t + 1), interval);
84
+ return () => clearInterval(id);
85
+ }, [enabled, interval]);
86
+ return { data, isLoading, error, refetch, stuck: stuck && isLoading };
87
+ }
88
+ export function useMutation(fn) {
89
+ const [isPending, setIsPending] = useState(false);
90
+ const [error, setError] = useState(null);
91
+ const fnRef = useRef(fn);
92
+ fnRef.current = fn;
93
+ const mutateAsync = useCallback(async (args) => {
94
+ setIsPending(true);
95
+ setError(null);
96
+ try {
97
+ return await fnRef.current(args);
98
+ }
99
+ catch (e) {
100
+ const err = e instanceof Error ? e : new Error(String(e));
101
+ setError(err);
102
+ throw err;
103
+ }
104
+ finally {
105
+ setIsPending(false);
106
+ }
107
+ }, []);
108
+ return { mutateAsync, isPending, error };
109
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@asteby/metacore-runtime-react",
3
- "version": "37.1.1",
3
+ "version": "37.2.0",
4
4
  "description": "React runtime for metacore hosts — renders addon contributions dynamically",
5
5
  "repository": {
6
6
  "type": "git",
@@ -0,0 +1,76 @@
1
+ // @vitest-environment happy-dom
2
+ //
3
+ // When an action declares `modal: "<slug>"` and no federated component is
4
+ // registered, ActionModalDispatcher must surface an error — never the generic
5
+ // ConfirmActionDialog (even if confirm/confirmMessage are also set).
6
+ import { afterEach, describe, expect, it, vi } from 'vitest'
7
+ import { cleanup, render, screen } from '@testing-library/react'
8
+
9
+ vi.mock('react-i18next', () => ({
10
+ useTranslation: () => ({
11
+ t: (_k: string, opts?: { defaultValue?: string }) => opts?.defaultValue ?? _k,
12
+ }),
13
+ }))
14
+
15
+ import { ActionModalDispatcher } from '../action-modal-dispatcher'
16
+ import { ApiProvider, type ApiClient } from '../api-context'
17
+ import {
18
+ registerActionComponent,
19
+ unregisterActionComponent,
20
+ type ActionMetadata,
21
+ } from '@asteby/metacore-sdk'
22
+
23
+ afterEach(() => {
24
+ cleanup()
25
+ unregisterActionComponent('SalesOrder', 'authorize_credit')
26
+ })
27
+
28
+ const noopApi: ApiClient = {
29
+ get: vi.fn().mockResolvedValue({ data: {} }),
30
+ post: vi.fn().mockResolvedValue({ data: { success: true } }),
31
+ } as unknown as ApiClient
32
+
33
+ const creditAction: ActionMetadata = {
34
+ key: 'authorize_credit',
35
+ label: 'Autorizar a crédito',
36
+ icon: 'Landmark',
37
+ modal: 'credit_approval.authorize_credit',
38
+ confirm: true,
39
+ confirmMessage: 'customers.action.authorize_credit_confirm_message',
40
+ executable: true,
41
+ }
42
+
43
+ function renderDispatcher(action: ActionMetadata = creditAction) {
44
+ return render(
45
+ <ApiProvider client={noopApi}>
46
+ <ActionModalDispatcher
47
+ open
48
+ onOpenChange={() => {}}
49
+ action={action}
50
+ model="SalesOrder"
51
+ record={{ id: '1', folio: 'SO-1' } as any}
52
+ endpoint="/data/SalesOrder"
53
+ onSuccess={() => {}}
54
+ />
55
+ </ApiProvider>,
56
+ )
57
+ }
58
+
59
+ describe('ActionModalDispatcher custom modal contract', () => {
60
+ it('errors when modal is declared but no component is registered (no generic confirm)', () => {
61
+ renderDispatcher()
62
+ expect(screen.getByText('No se pudo cargar el formulario')).toBeTruthy()
63
+ expect(screen.queryByText('customers.action.authorize_credit_confirm_message')).toBeNull()
64
+ expect(screen.queryByRole('button', { name: /autorizar a crédito/i })).toBeNull()
65
+ })
66
+
67
+ it('renders the registered custom component when present', () => {
68
+ function CustomModal() {
69
+ return <div data-testid="credit-dossier">dossier</div>
70
+ }
71
+ registerActionComponent('SalesOrder', 'authorize_credit', CustomModal as never)
72
+ renderDispatcher()
73
+ expect(screen.getByTestId('credit-dossier')).toBeTruthy()
74
+ expect(screen.queryByText('No se pudo cargar el formulario')).toBeNull()
75
+ })
76
+ })
@@ -0,0 +1,100 @@
1
+ // @vitest-environment happy-dom
2
+ import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest'
3
+ import { renderHook, act } from '@testing-library/react'
4
+ import { useResource } from '../use-resource'
5
+
6
+ describe('useResource stuck flag', () => {
7
+ beforeEach(() => {
8
+ vi.useFakeTimers()
9
+ })
10
+ afterEach(() => {
11
+ vi.useRealTimers()
12
+ })
13
+
14
+ it('is not stuck before the threshold elapses', () => {
15
+ const fetcher = vi.fn(() => new Promise<string>(() => {}))
16
+ const { result } = renderHook(() =>
17
+ useResource(fetcher, [], { stuckAfterMs: 8_000 }),
18
+ )
19
+
20
+ expect(result.current.isLoading).toBe(true)
21
+ expect(result.current.stuck).toBe(false)
22
+
23
+ act(() => {
24
+ vi.advanceTimersByTime(7_999)
25
+ })
26
+ expect(result.current.stuck).toBe(false)
27
+ })
28
+
29
+ it('flips stuck once loading is sustained past the threshold', () => {
30
+ const fetcher = vi.fn(() => new Promise<string>(() => {}))
31
+ const { result } = renderHook(() =>
32
+ useResource(fetcher, [], { stuckAfterMs: 8_000 }),
33
+ )
34
+
35
+ act(() => {
36
+ vi.advanceTimersByTime(8_000)
37
+ })
38
+ expect(result.current.isLoading).toBe(true)
39
+ expect(result.current.stuck).toBe(true)
40
+ })
41
+
42
+ it('never gets stuck when the fetch resolves before the threshold', async () => {
43
+ let resolveFetch: (value: string) => void = () => {}
44
+ const fetcher = vi.fn(
45
+ () =>
46
+ new Promise<string>((resolve) => {
47
+ resolveFetch = resolve
48
+ }),
49
+ )
50
+ const { result } = renderHook(() =>
51
+ useResource(fetcher, [], { stuckAfterMs: 8_000 }),
52
+ )
53
+
54
+ act(() => {
55
+ vi.advanceTimersByTime(1_000)
56
+ })
57
+ expect(result.current.stuck).toBe(false)
58
+
59
+ await act(async () => {
60
+ resolveFetch('data')
61
+ await Promise.resolve()
62
+ await Promise.resolve()
63
+ })
64
+ expect(result.current.isLoading).toBe(false)
65
+ expect(result.current.data).toBe('data')
66
+ expect(result.current.stuck).toBe(false)
67
+
68
+ act(() => {
69
+ vi.advanceTimersByTime(8_000)
70
+ })
71
+ expect(result.current.stuck).toBe(false)
72
+ })
73
+
74
+ it('clears stuck and restarts the timer on refetch', () => {
75
+ const fetcher = vi.fn(() => new Promise<string>(() => {}))
76
+ const { result } = renderHook(() =>
77
+ useResource(fetcher, [], { stuckAfterMs: 8_000 }),
78
+ )
79
+
80
+ act(() => {
81
+ vi.advanceTimersByTime(8_000)
82
+ })
83
+ expect(result.current.stuck).toBe(true)
84
+
85
+ act(() => {
86
+ result.current.refetch()
87
+ })
88
+ expect(result.current.stuck).toBe(false)
89
+
90
+ act(() => {
91
+ vi.advanceTimersByTime(7_999)
92
+ })
93
+ expect(result.current.stuck).toBe(false)
94
+
95
+ act(() => {
96
+ vi.advanceTimersByTime(1)
97
+ })
98
+ expect(result.current.stuck).toBe(true)
99
+ })
100
+ })
@@ -1,10 +1,12 @@
1
1
  // ActionModalDispatcher — renders the right modal for a custom action:
2
2
  // 1) Custom component from the SDK registry → use it
3
- // 2) action.fields[] / action.steps[] → GenericActionModal / WizardActionModal
4
- // 3) action.confirm OR action.confirmMessage → ConfirmActionDialog
5
- // 4) action.executable (host opened the modal; federated UI missing) →
3
+ // 2) action.modal set but no registered component → MissingCustomActionModal
4
+ // (NEVER fall back to confirm/fields — that hides a broken federated UI)
5
+ // 3) action.fields[] / action.steps[] → GenericActionModal / WizardActionModal
6
+ // 4) action.confirm OR action.confirmMessage → ConfirmActionDialog
7
+ // 5) action.executable (host opened the modal; no modal/fields/confirm) →
6
8
  // ConfirmActionDialog so a click never silently no-ops
7
- // 5) otherwise → null (caller should execute immediately without opening us)
9
+ // 6) otherwise → null (caller should execute immediately without opening us)
8
10
  //
9
11
  // The host injects its axios-like client via <ApiProvider>; we no longer
10
12
  // depend on a bundler alias to `@/lib/api`.
@@ -251,6 +253,19 @@ export function ActionModalDispatcher({
251
253
  )
252
254
  }
253
255
 
256
+ // Declarative custom slot (`modal: "addon.action"`). Prefer a hard error
257
+ // over confirm/fields generics — those look "fine" and hide a missing remote.
258
+ if (action.modal) {
259
+ return (
260
+ <MissingCustomActionModal
261
+ open={open}
262
+ onOpenChange={onOpenChange}
263
+ action={action}
264
+ model={model}
265
+ />
266
+ )
267
+ }
268
+
254
269
  if (action.steps && action.steps.length > 0) {
255
270
  return (
256
271
  <WizardActionModal
@@ -283,6 +298,7 @@ export function ActionModalDispatcher({
283
298
  // boolean). Hosts also mark every wasm action `executable` and open THIS
284
299
  // dispatcher; without a confirm/fields/custom UI we used to return null and
285
300
  // the row click did nothing. Treat message-only + executable-open as confirm.
301
+ // Never reached when action.modal is set (handled above).
286
302
  const wantsConfirm = !!(action.confirm || action.confirmMessage)
287
303
  if (wantsConfirm || (open && action.executable)) {
288
304
  return (
@@ -301,6 +317,47 @@ export function ActionModalDispatcher({
301
317
  return null
302
318
  }
303
319
 
320
+ /** Shown when the action declares `modal` but no federated component registered. */
321
+ function MissingCustomActionModal({
322
+ open,
323
+ onOpenChange,
324
+ action,
325
+ model,
326
+ }: {
327
+ open: boolean
328
+ onOpenChange: (open: boolean) => void
329
+ action: ActionMetadata
330
+ model: string
331
+ }) {
332
+ const { t } = useTranslation()
333
+ const slug = action.modal || ''
334
+ const title = t('dynamic.action_modal_missing_title', {
335
+ defaultValue: 'No se pudo cargar el formulario',
336
+ })
337
+ const description = t('dynamic.action_modal_missing_description', {
338
+ defaultValue:
339
+ 'Esta acción requiere una interfaz personalizada ({{slug}}) que no está registrada. Recarga la página o reinstala el módulo; no se abre un confirmatorio genérico para no confundir el flujo.',
340
+ slug: slug || `${model}.${action.key}`,
341
+ action: action.key,
342
+ model,
343
+ })
344
+ return (
345
+ <AlertDialog open={open} onOpenChange={onOpenChange}>
346
+ <AlertDialogContent>
347
+ <AlertDialogHeader>
348
+ <AlertDialogTitle>{title}</AlertDialogTitle>
349
+ <AlertDialogDescription>{description}</AlertDialogDescription>
350
+ </AlertDialogHeader>
351
+ <AlertDialogFooter>
352
+ <AlertDialogAction onClick={() => onOpenChange(false)}>
353
+ {t('common.close', { defaultValue: 'Cerrar' })}
354
+ </AlertDialogAction>
355
+ </AlertDialogFooter>
356
+ </AlertDialogContent>
357
+ </AlertDialog>
358
+ )
359
+ }
360
+
304
361
  function buildActionUrl(endpoint: string | undefined, model: string, recordId: string | undefined, actionKey: string) {
305
362
  // A create-placement (collection) action has no record yet, so `recordId`
306
363
  // is undefined. Omit the `/{id}` segment so the request hits the collection
@@ -511,7 +511,10 @@ export function DynamicTable({
511
511
  // the URL goes bare, and the sidebar falls back to the model default
512
512
  // (the "click twice to move the active to the right entry" bug).
513
513
  const current = new URLSearchParams(window.location.search)
514
- for (const key of ['view', 'group_by']) {
514
+ // `action` is host-owned (?action=stamp_fiscal deep-links from
515
+ // notifications). Without carrying it, the first URL rewrite drops it
516
+ // before the list route can open ActionModalDispatcher.
517
+ for (const key of ['view', 'group_by', 'action']) {
515
518
  const v = current.get(key)
516
519
  if (v) params.set(key, v)
517
520
  }
package/src/index.ts CHANGED
@@ -127,6 +127,12 @@ export {
127
127
  type UseDynamicFiltersResult,
128
128
  } from './use-dynamic-filters'
129
129
  export { useDebouncedValue, SEARCH_DEBOUNCE_MS } from './use-debounced-value'
130
+ export {
131
+ useResource,
132
+ useMutation,
133
+ type ResourceState,
134
+ type MutationState,
135
+ } from './use-resource'
130
136
  export * from './dynamic-form'
131
137
  export {
132
138
  FilePickButton,
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Tiny data-fetching hooks for federated addons.
3
+ *
4
+ * Addon-safe replacement for `@tanstack/react-query`: pulling tanstack into a
5
+ * federated remote risks a duplicate query-client / version clash with the
6
+ * host and bloats the MF bundle (see @asteby/metacore-runtime-react's
7
+ * singleton react-query for HOST-side code, which does not apply inside a
8
+ * federated addon bundle). These hooks cover exactly what an addon panel
9
+ * needs — a polling GET resource and an imperative mutation — over any
10
+ * fetch-like client.
11
+ */
12
+ import { useCallback, useEffect, useRef, useState } from "react";
13
+
14
+ export interface ResourceState<T> {
15
+ data: T | undefined;
16
+ isLoading: boolean;
17
+ error: Error | null;
18
+ refetch: () => void;
19
+ /**
20
+ * True once `isLoading` has stayed on for `stuckAfterMs` without landing.
21
+ * Whatever the exact timing race (an aborted fetch racing a remount, a
22
+ * slow/hung request), a caller needs a way OUT — a spinner with no escape
23
+ * hatch is a dead end. This never claims to know WHY the fetch is stuck;
24
+ * it only tells the caller long enough has passed to offer a retry
25
+ * instead of an indefinite loading state.
26
+ */
27
+ stuck: boolean;
28
+ }
29
+
30
+ const DEFAULT_STUCK_AFTER_MS = 8_000;
31
+
32
+ export function useResource<T>(
33
+ fetcher: (signal: AbortSignal) => Promise<T>,
34
+ deps: ReadonlyArray<unknown>,
35
+ options?: {
36
+ refetchInterval?: number;
37
+ enabled?: boolean;
38
+ /** Threshold in ms before `stuck` flips true. Default 8000. */
39
+ stuckAfterMs?: number;
40
+ },
41
+ ): ResourceState<T> {
42
+ const enabled = options?.enabled ?? true;
43
+ const stuckAfterMs = options?.stuckAfterMs ?? DEFAULT_STUCK_AFTER_MS;
44
+ const [data, setData] = useState<T | undefined>(undefined);
45
+ const [isLoading, setIsLoading] = useState<boolean>(enabled);
46
+ const [error, setError] = useState<Error | null>(null);
47
+ const fetcherRef = useRef(fetcher);
48
+ fetcherRef.current = fetcher;
49
+ const hasDataRef = useRef(false);
50
+ const [tick, setTick] = useState(0);
51
+ const [stuck, setStuck] = useState(false);
52
+
53
+ const refetch = useCallback(() => {
54
+ setStuck(false);
55
+ setTick((t) => t + 1);
56
+ }, []);
57
+
58
+ useEffect(() => {
59
+ if (!enabled) {
60
+ setIsLoading(false);
61
+ return;
62
+ }
63
+ const controller = new AbortController();
64
+ let active = true;
65
+ // Stale-while-revalidate: once we have data, refetches/polls revalidate in
66
+ // the background without flipping isLoading — the UI keeps showing the last
67
+ // values instead of collapsing to a "loading" blank every poll.
68
+ if (!hasDataRef.current) setIsLoading(true);
69
+ setStuck(false);
70
+ const stuckTimer = !hasDataRef.current
71
+ ? setTimeout(() => {
72
+ if (active) setStuck(true);
73
+ }, stuckAfterMs)
74
+ : undefined;
75
+ fetcherRef
76
+ .current(controller.signal)
77
+ .then((res) => {
78
+ if (!active) return;
79
+ hasDataRef.current = true;
80
+ setData(res);
81
+ setError(null);
82
+ })
83
+ .catch((e: unknown) => {
84
+ if (controller.signal.aborted) return;
85
+ if (!active) return;
86
+ setError(e instanceof Error ? e : new Error(String(e)));
87
+ })
88
+ .finally(() => {
89
+ clearTimeout(stuckTimer);
90
+ if (active && !controller.signal.aborted) {
91
+ setIsLoading(false);
92
+ setStuck(false);
93
+ }
94
+ });
95
+ return () => {
96
+ active = false;
97
+ clearTimeout(stuckTimer);
98
+ controller.abort();
99
+ };
100
+ // eslint-disable-next-line react-hooks/exhaustive-deps
101
+ }, [enabled, tick, stuckAfterMs, ...deps]);
102
+
103
+ // Polling
104
+ const interval = options?.refetchInterval;
105
+ useEffect(() => {
106
+ if (!enabled || !interval) return;
107
+ const id = setInterval(() => setTick((t) => t + 1), interval);
108
+ return () => clearInterval(id);
109
+ }, [enabled, interval]);
110
+
111
+ return { data, isLoading, error, refetch, stuck: stuck && isLoading };
112
+ }
113
+
114
+ export interface MutationState<TArgs, TResult> {
115
+ mutateAsync: (args: TArgs) => Promise<TResult>;
116
+ isPending: boolean;
117
+ error: Error | null;
118
+ }
119
+
120
+ export function useMutation<TArgs, TResult>(
121
+ fn: (args: TArgs) => Promise<TResult>,
122
+ ): MutationState<TArgs, TResult> {
123
+ const [isPending, setIsPending] = useState(false);
124
+ const [error, setError] = useState<Error | null>(null);
125
+ const fnRef = useRef(fn);
126
+ fnRef.current = fn;
127
+
128
+ const mutateAsync = useCallback(async (args: TArgs) => {
129
+ setIsPending(true);
130
+ setError(null);
131
+ try {
132
+ return await fnRef.current(args);
133
+ } catch (e) {
134
+ const err = e instanceof Error ? e : new Error(String(e));
135
+ setError(err);
136
+ throw err;
137
+ } finally {
138
+ setIsPending(false);
139
+ }
140
+ }, []);
141
+
142
+ return { mutateAsync, isPending, error };
143
+ }