@asteby/metacore-runtime-react 23.11.2 → 23.12.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/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/use-addon-settings.d.ts +56 -0
- package/dist/use-addon-settings.d.ts.map +1 -0
- package/dist/use-addon-settings.js +109 -0
- package/package.json +1 -1
- package/src/__tests__/use-addon-settings.test.ts +74 -0
- package/src/index.ts +1 -0
- package/src/use-addon-settings.ts +152 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @asteby/metacore-runtime-react
|
|
2
2
|
|
|
3
|
+
## 23.12.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 9376722: Add `useAddonSettings` / `useUpdateAddonSettings` — the standard primitive for reading an addon's per-organization configuration from a federated addon, backed by react-query and the host-injected api client. Merges caller-provided defaults (mirroring the manifest's `settings[].default`) under the saved org values, so a never-saved setting falls back to its default. Also exports the pure `mergeAddonSettings` helper and the stable `addonSettingsKey` query key.
|
|
8
|
+
|
|
3
9
|
## 23.11.2
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
package/dist/index.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ export * from './org-runtime-provider';
|
|
|
25
25
|
export * from './navigation-builder';
|
|
26
26
|
export * from './i18n-provider';
|
|
27
27
|
export * from './api-context';
|
|
28
|
+
export * from './use-addon-settings';
|
|
28
29
|
export * from './metadata-cache';
|
|
29
30
|
export { ADDON_MANIFEST_CHANGED_TYPE, wireHotSwapInvalidation, useManifestHotSwapSubscriber, type AddonManifestChangedMessage, type ManifestHotSwapClient, type WireHotSwapInvalidationOptions, } from './manifest-hotswap-subscriber';
|
|
30
31
|
export { useHotSwapReload, applyHotSwapReload, withVersionParam, clearFederationContainer, shortenHash, type HotSwapReloadStrategy, type HotSwapReloadConfig, type HotSwapReloadAction, type HotSwapReloadDeps, type UseHotSwapReloadResult, } from './hotswap-reload-policy';
|
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
|
@@ -30,6 +30,7 @@ export * from './org-runtime-provider';
|
|
|
30
30
|
export * from './navigation-builder';
|
|
31
31
|
export * from './i18n-provider';
|
|
32
32
|
export * from './api-context';
|
|
33
|
+
export * from './use-addon-settings';
|
|
33
34
|
export * from './metadata-cache';
|
|
34
35
|
export { ADDON_MANIFEST_CHANGED_TYPE, wireHotSwapInvalidation, useManifestHotSwapSubscriber, } from './manifest-hotswap-subscriber';
|
|
35
36
|
export { useHotSwapReload, applyHotSwapReload, withVersionParam, clearFederationContainer, shortenHash, } from './hotswap-reload-policy';
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/** Stable react-query key for an addon's per-org settings. */
|
|
2
|
+
export declare function addonSettingsKey(addonKey: string): readonly ["addon-settings", string];
|
|
3
|
+
/**
|
|
4
|
+
* Merge saved org values over the caller's defaults so a never-saved setting
|
|
5
|
+
* falls back to its default. Pure + exported for unit testing. Only keys that
|
|
6
|
+
* are actually present in `stored` win — an explicit `undefined` in the stored
|
|
7
|
+
* payload is treated as "not saved" and yields the default (the host omits
|
|
8
|
+
* unsaved keys, but this keeps a sparse payload well-behaved).
|
|
9
|
+
*/
|
|
10
|
+
export declare function mergeAddonSettings<T extends Record<string, unknown>>(defaults: Partial<T> | undefined, stored: Partial<T> | null | undefined): T;
|
|
11
|
+
export interface UseAddonSettingsOptions<T> {
|
|
12
|
+
/**
|
|
13
|
+
* Fallback values (typically the addon manifest's `settings[].default`)
|
|
14
|
+
* merged UNDER the saved org values. A setting the org hasn't saved
|
|
15
|
+
* resolves to its default here.
|
|
16
|
+
*/
|
|
17
|
+
defaults?: Partial<T>;
|
|
18
|
+
/** Override staleTime (default 30s — org config changes rarely). */
|
|
19
|
+
staleTime?: number;
|
|
20
|
+
/** Defer fetching (e.g. until the addon key is known). */
|
|
21
|
+
enabled?: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface UseAddonSettingsResult<T> {
|
|
24
|
+
/** Saved org values merged over `defaults`. Never null. */
|
|
25
|
+
settings: T;
|
|
26
|
+
isLoading: boolean;
|
|
27
|
+
error: unknown;
|
|
28
|
+
refetch: () => void;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Read an addon's per-organization settings.
|
|
32
|
+
*
|
|
33
|
+
* This is the ecosystem-standard way for a federated addon to read its own
|
|
34
|
+
* config; prefer it over a hand-rolled fetch so every addon caches, merges
|
|
35
|
+
* defaults, and invalidates identically.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* type PosSettings = { allowNegativeStock: boolean; roundingMode: string }
|
|
39
|
+
* const { settings, isLoading } = useAddonSettings<PosSettings>('pos', {
|
|
40
|
+
* defaults: { allowNegativeStock: false, roundingMode: 'nearest' },
|
|
41
|
+
* })
|
|
42
|
+
* if (!isLoading && settings.allowNegativeStock) { ... }
|
|
43
|
+
*/
|
|
44
|
+
export declare function useAddonSettings<T extends Record<string, unknown> = Record<string, unknown>>(addonKey: string, opts?: UseAddonSettingsOptions<T>): UseAddonSettingsResult<T>;
|
|
45
|
+
/**
|
|
46
|
+
* Optional write companion — PUTs a (partial) settings object and invalidates
|
|
47
|
+
* the read query so `useAddonSettings` re-resolves. The host's generic config
|
|
48
|
+
* modal does its own PUT; this exists for addons that ship a custom config
|
|
49
|
+
* surface. Backward-compatible: purely additive.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* const update = useUpdateAddonSettings('pos')
|
|
53
|
+
* update.mutate({ allowNegativeStock: true })
|
|
54
|
+
*/
|
|
55
|
+
export declare function useUpdateAddonSettings<T extends Record<string, unknown> = Record<string, unknown>>(addonKey: string): import("@tanstack/react-query").UseMutationResult<Partial<T>, Error, Partial<T>, unknown>;
|
|
56
|
+
//# sourceMappingURL=use-addon-settings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-addon-settings.d.ts","sourceRoot":"","sources":["../src/use-addon-settings.ts"],"names":[],"mappings":"AA6BA,8DAA8D;AAC9D,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,uCAEhD;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChE,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,SAAS,EAChC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,GACtC,CAAC,CAQH;AAED,MAAM,WAAW,uBAAuB,CAAC,CAAC;IACtC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;IACrB,oEAAoE;IACpE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,0DAA0D;IAC1D,OAAO,CAAC,EAAE,OAAO,CAAA;CACpB;AAED,MAAM,WAAW,sBAAsB,CAAC,CAAC;IACrC,2DAA2D;IAC3D,QAAQ,EAAE,CAAC,CAAA;IACX,SAAS,EAAE,OAAO,CAAA;IAClB,KAAK,EAAE,OAAO,CAAA;IACd,OAAO,EAAE,MAAM,IAAI,CAAA;CACtB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxF,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE,uBAAuB,CAAC,CAAC,CAAM,GACtC,sBAAsB,CAAC,CAAC,CAAC,CA8B3B;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9F,QAAQ,EAAE,MAAM,6FAgBnB"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// useAddonSettings — THE standard primitive for reading an addon's
|
|
2
|
+
// per-organization configuration from a federated addon (or any host
|
|
3
|
+
// surface) without every addon re-implementing its own fetch.
|
|
4
|
+
//
|
|
5
|
+
// The host (ops) persists settings per org on `AddonInstallation.settings`
|
|
6
|
+
// (jsonb) and exposes:
|
|
7
|
+
// GET /api/addons/:key/settings → { success, data: { <key>: <value> } }
|
|
8
|
+
// PUT /api/addons/:key/settings → body = object of values
|
|
9
|
+
// A setting the org has never saved simply isn't present in `data`; the
|
|
10
|
+
// caller supplies the fallback via `opts.defaults` (which mirror the
|
|
11
|
+
// manifest's `settings[].default`). runtime-react has no access to the
|
|
12
|
+
// addon manifest at runtime, so defaults are a parameter, not automatic.
|
|
13
|
+
//
|
|
14
|
+
// react-query is a PEER dependency of runtime-react: `useQuery`/`useMutation`
|
|
15
|
+
// resolve to the HOST's singleton QueryClient (the MF shared singleton), so
|
|
16
|
+
// this hook never constructs its own client and stays cache-coherent with
|
|
17
|
+
// the rest of the app. Do not add a QueryClientProvider here.
|
|
18
|
+
//
|
|
19
|
+
// Server-side gating (wasm/Go backends) is out of scope here: a backend that
|
|
20
|
+
// must gate behaviour on org config reads the same `AddonInstallation.settings`
|
|
21
|
+
// jsonb directly — this hook is the client-read half of that contract.
|
|
22
|
+
import { useMemo } from 'react';
|
|
23
|
+
import { useMutation, useQuery, useQueryClient, } from '@tanstack/react-query';
|
|
24
|
+
import { useApi } from './api-context';
|
|
25
|
+
/** Stable react-query key for an addon's per-org settings. */
|
|
26
|
+
export function addonSettingsKey(addonKey) {
|
|
27
|
+
return ['addon-settings', addonKey];
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Merge saved org values over the caller's defaults so a never-saved setting
|
|
31
|
+
* falls back to its default. Pure + exported for unit testing. Only keys that
|
|
32
|
+
* are actually present in `stored` win — an explicit `undefined` in the stored
|
|
33
|
+
* payload is treated as "not saved" and yields the default (the host omits
|
|
34
|
+
* unsaved keys, but this keeps a sparse payload well-behaved).
|
|
35
|
+
*/
|
|
36
|
+
export function mergeAddonSettings(defaults, stored) {
|
|
37
|
+
const out = { ...(defaults ?? {}) };
|
|
38
|
+
if (stored) {
|
|
39
|
+
for (const [k, v] of Object.entries(stored)) {
|
|
40
|
+
if (v !== undefined)
|
|
41
|
+
out[k] = v;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return out;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Read an addon's per-organization settings.
|
|
48
|
+
*
|
|
49
|
+
* This is the ecosystem-standard way for a federated addon to read its own
|
|
50
|
+
* config; prefer it over a hand-rolled fetch so every addon caches, merges
|
|
51
|
+
* defaults, and invalidates identically.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* type PosSettings = { allowNegativeStock: boolean; roundingMode: string }
|
|
55
|
+
* const { settings, isLoading } = useAddonSettings<PosSettings>('pos', {
|
|
56
|
+
* defaults: { allowNegativeStock: false, roundingMode: 'nearest' },
|
|
57
|
+
* })
|
|
58
|
+
* if (!isLoading && settings.allowNegativeStock) { ... }
|
|
59
|
+
*/
|
|
60
|
+
export function useAddonSettings(addonKey, opts = {}) {
|
|
61
|
+
const api = useApi();
|
|
62
|
+
const { defaults, staleTime, enabled } = opts;
|
|
63
|
+
const query = useQuery({
|
|
64
|
+
queryKey: addonSettingsKey(addonKey),
|
|
65
|
+
queryFn: async () => {
|
|
66
|
+
const res = await api.get(`/api/addons/${addonKey}/settings`);
|
|
67
|
+
const body = res.data;
|
|
68
|
+
// Host envelope: { success, data: { <key>: <value> } }.
|
|
69
|
+
const values = body?.success ? body.data : body;
|
|
70
|
+
return (values ?? {});
|
|
71
|
+
},
|
|
72
|
+
staleTime: staleTime ?? 30_000,
|
|
73
|
+
enabled: (enabled ?? true) && !!addonKey,
|
|
74
|
+
});
|
|
75
|
+
const settings = useMemo(() => mergeAddonSettings(defaults, query.data), [defaults, query.data]);
|
|
76
|
+
return {
|
|
77
|
+
settings,
|
|
78
|
+
isLoading: query.isLoading,
|
|
79
|
+
error: query.error,
|
|
80
|
+
refetch: () => {
|
|
81
|
+
void query.refetch();
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Optional write companion — PUTs a (partial) settings object and invalidates
|
|
87
|
+
* the read query so `useAddonSettings` re-resolves. The host's generic config
|
|
88
|
+
* modal does its own PUT; this exists for addons that ship a custom config
|
|
89
|
+
* surface. Backward-compatible: purely additive.
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* const update = useUpdateAddonSettings('pos')
|
|
93
|
+
* update.mutate({ allowNegativeStock: true })
|
|
94
|
+
*/
|
|
95
|
+
export function useUpdateAddonSettings(addonKey) {
|
|
96
|
+
const api = useApi();
|
|
97
|
+
const qc = useQueryClient();
|
|
98
|
+
return useMutation({
|
|
99
|
+
mutationFn: async (values) => {
|
|
100
|
+
const res = await api.put(`/api/addons/${addonKey}/settings`, values);
|
|
101
|
+
const body = res.data;
|
|
102
|
+
const saved = body?.success ? body.data : body;
|
|
103
|
+
return (saved ?? values);
|
|
104
|
+
},
|
|
105
|
+
onSuccess: () => {
|
|
106
|
+
void qc.invalidateQueries({ queryKey: addonSettingsKey(addonKey) });
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// Locks the default-merge semantics of the useAddonSettings primitive: saved
|
|
2
|
+
// org values win over caller defaults, never-saved keys fall back to their
|
|
3
|
+
// default, and a sparse/undefined stored payload stays well-behaved. The hook
|
|
4
|
+
// itself is a thin react-query wrapper over `useApi`; the load-bearing logic
|
|
5
|
+
// is this pure merge, so that's what we test directly.
|
|
6
|
+
import { describe, it, expect } from 'vitest'
|
|
7
|
+
import { mergeAddonSettings, addonSettingsKey } from '../use-addon-settings'
|
|
8
|
+
|
|
9
|
+
interface PosSettings extends Record<string, unknown> {
|
|
10
|
+
allowNegativeStock: boolean
|
|
11
|
+
roundingMode: string
|
|
12
|
+
taxRate: number
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
describe('mergeAddonSettings', () => {
|
|
16
|
+
const defaults: Partial<PosSettings> = {
|
|
17
|
+
allowNegativeStock: false,
|
|
18
|
+
roundingMode: 'nearest',
|
|
19
|
+
taxRate: 0,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
it('falls back to defaults when nothing is stored', () => {
|
|
23
|
+
expect(mergeAddonSettings<PosSettings>(defaults, null)).toEqual({
|
|
24
|
+
allowNegativeStock: false,
|
|
25
|
+
roundingMode: 'nearest',
|
|
26
|
+
taxRate: 0,
|
|
27
|
+
})
|
|
28
|
+
expect(mergeAddonSettings<PosSettings>(defaults, undefined)).toEqual(defaults)
|
|
29
|
+
expect(mergeAddonSettings<PosSettings>(defaults, {})).toEqual(defaults)
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('lets saved org values override defaults', () => {
|
|
33
|
+
expect(
|
|
34
|
+
mergeAddonSettings<PosSettings>(defaults, { allowNegativeStock: true, taxRate: 16 }),
|
|
35
|
+
).toEqual({
|
|
36
|
+
allowNegativeStock: true,
|
|
37
|
+
roundingMode: 'nearest',
|
|
38
|
+
taxRate: 16,
|
|
39
|
+
})
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('keeps falsy saved values (0, false, "") instead of treating them as unset', () => {
|
|
43
|
+
expect(
|
|
44
|
+
mergeAddonSettings<PosSettings>(
|
|
45
|
+
{ allowNegativeStock: true, roundingMode: 'up', taxRate: 5 },
|
|
46
|
+
{ allowNegativeStock: false, roundingMode: '', taxRate: 0 },
|
|
47
|
+
),
|
|
48
|
+
).toEqual({ allowNegativeStock: false, roundingMode: '', taxRate: 0 })
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('treats an explicit undefined in the stored payload as unset', () => {
|
|
52
|
+
expect(
|
|
53
|
+
mergeAddonSettings<PosSettings>(defaults, { roundingMode: undefined }),
|
|
54
|
+
).toEqual(defaults)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('returns an object even with no defaults', () => {
|
|
58
|
+
expect(mergeAddonSettings(undefined, { a: 1 })).toEqual({ a: 1 })
|
|
59
|
+
expect(mergeAddonSettings(undefined, null)).toEqual({})
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('does not mutate the provided defaults object', () => {
|
|
63
|
+
const d = { ...defaults }
|
|
64
|
+
mergeAddonSettings<PosSettings>(d, { taxRate: 99 })
|
|
65
|
+
expect(d).toEqual(defaults)
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
describe('addonSettingsKey', () => {
|
|
70
|
+
it('is stable and namespaced per addon', () => {
|
|
71
|
+
expect(addonSettingsKey('pos')).toEqual(['addon-settings', 'pos'])
|
|
72
|
+
expect(addonSettingsKey('inventory')).toEqual(['addon-settings', 'inventory'])
|
|
73
|
+
})
|
|
74
|
+
})
|
package/src/index.ts
CHANGED
|
@@ -165,6 +165,7 @@ export * from './org-runtime-provider'
|
|
|
165
165
|
export * from './navigation-builder'
|
|
166
166
|
export * from './i18n-provider'
|
|
167
167
|
export * from './api-context'
|
|
168
|
+
export * from './use-addon-settings'
|
|
168
169
|
export * from './metadata-cache'
|
|
169
170
|
export {
|
|
170
171
|
ADDON_MANIFEST_CHANGED_TYPE,
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
// useAddonSettings — THE standard primitive for reading an addon's
|
|
2
|
+
// per-organization configuration from a federated addon (or any host
|
|
3
|
+
// surface) without every addon re-implementing its own fetch.
|
|
4
|
+
//
|
|
5
|
+
// The host (ops) persists settings per org on `AddonInstallation.settings`
|
|
6
|
+
// (jsonb) and exposes:
|
|
7
|
+
// GET /api/addons/:key/settings → { success, data: { <key>: <value> } }
|
|
8
|
+
// PUT /api/addons/:key/settings → body = object of values
|
|
9
|
+
// A setting the org has never saved simply isn't present in `data`; the
|
|
10
|
+
// caller supplies the fallback via `opts.defaults` (which mirror the
|
|
11
|
+
// manifest's `settings[].default`). runtime-react has no access to the
|
|
12
|
+
// addon manifest at runtime, so defaults are a parameter, not automatic.
|
|
13
|
+
//
|
|
14
|
+
// react-query is a PEER dependency of runtime-react: `useQuery`/`useMutation`
|
|
15
|
+
// resolve to the HOST's singleton QueryClient (the MF shared singleton), so
|
|
16
|
+
// this hook never constructs its own client and stays cache-coherent with
|
|
17
|
+
// the rest of the app. Do not add a QueryClientProvider here.
|
|
18
|
+
//
|
|
19
|
+
// Server-side gating (wasm/Go backends) is out of scope here: a backend that
|
|
20
|
+
// must gate behaviour on org config reads the same `AddonInstallation.settings`
|
|
21
|
+
// jsonb directly — this hook is the client-read half of that contract.
|
|
22
|
+
import { useMemo } from 'react'
|
|
23
|
+
import {
|
|
24
|
+
useMutation,
|
|
25
|
+
useQuery,
|
|
26
|
+
useQueryClient,
|
|
27
|
+
} from '@tanstack/react-query'
|
|
28
|
+
import { useApi } from './api-context'
|
|
29
|
+
|
|
30
|
+
/** Stable react-query key for an addon's per-org settings. */
|
|
31
|
+
export function addonSettingsKey(addonKey: string) {
|
|
32
|
+
return ['addon-settings', addonKey] as const
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Merge saved org values over the caller's defaults so a never-saved setting
|
|
37
|
+
* falls back to its default. Pure + exported for unit testing. Only keys that
|
|
38
|
+
* are actually present in `stored` win — an explicit `undefined` in the stored
|
|
39
|
+
* payload is treated as "not saved" and yields the default (the host omits
|
|
40
|
+
* unsaved keys, but this keeps a sparse payload well-behaved).
|
|
41
|
+
*/
|
|
42
|
+
export function mergeAddonSettings<T extends Record<string, unknown>>(
|
|
43
|
+
defaults: Partial<T> | undefined,
|
|
44
|
+
stored: Partial<T> | null | undefined,
|
|
45
|
+
): T {
|
|
46
|
+
const out: Record<string, unknown> = { ...(defaults ?? {}) }
|
|
47
|
+
if (stored) {
|
|
48
|
+
for (const [k, v] of Object.entries(stored)) {
|
|
49
|
+
if (v !== undefined) out[k] = v
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return out as T
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface UseAddonSettingsOptions<T> {
|
|
56
|
+
/**
|
|
57
|
+
* Fallback values (typically the addon manifest's `settings[].default`)
|
|
58
|
+
* merged UNDER the saved org values. A setting the org hasn't saved
|
|
59
|
+
* resolves to its default here.
|
|
60
|
+
*/
|
|
61
|
+
defaults?: Partial<T>
|
|
62
|
+
/** Override staleTime (default 30s — org config changes rarely). */
|
|
63
|
+
staleTime?: number
|
|
64
|
+
/** Defer fetching (e.g. until the addon key is known). */
|
|
65
|
+
enabled?: boolean
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface UseAddonSettingsResult<T> {
|
|
69
|
+
/** Saved org values merged over `defaults`. Never null. */
|
|
70
|
+
settings: T
|
|
71
|
+
isLoading: boolean
|
|
72
|
+
error: unknown
|
|
73
|
+
refetch: () => void
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Read an addon's per-organization settings.
|
|
78
|
+
*
|
|
79
|
+
* This is the ecosystem-standard way for a federated addon to read its own
|
|
80
|
+
* config; prefer it over a hand-rolled fetch so every addon caches, merges
|
|
81
|
+
* defaults, and invalidates identically.
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* type PosSettings = { allowNegativeStock: boolean; roundingMode: string }
|
|
85
|
+
* const { settings, isLoading } = useAddonSettings<PosSettings>('pos', {
|
|
86
|
+
* defaults: { allowNegativeStock: false, roundingMode: 'nearest' },
|
|
87
|
+
* })
|
|
88
|
+
* if (!isLoading && settings.allowNegativeStock) { ... }
|
|
89
|
+
*/
|
|
90
|
+
export function useAddonSettings<T extends Record<string, unknown> = Record<string, unknown>>(
|
|
91
|
+
addonKey: string,
|
|
92
|
+
opts: UseAddonSettingsOptions<T> = {},
|
|
93
|
+
): UseAddonSettingsResult<T> {
|
|
94
|
+
const api = useApi()
|
|
95
|
+
const { defaults, staleTime, enabled } = opts
|
|
96
|
+
|
|
97
|
+
const query = useQuery<Partial<T>>({
|
|
98
|
+
queryKey: addonSettingsKey(addonKey),
|
|
99
|
+
queryFn: async () => {
|
|
100
|
+
const res = await api.get(`/api/addons/${addonKey}/settings`)
|
|
101
|
+
const body = (res as { data: any }).data
|
|
102
|
+
// Host envelope: { success, data: { <key>: <value> } }.
|
|
103
|
+
const values = body?.success ? body.data : body
|
|
104
|
+
return (values ?? {}) as Partial<T>
|
|
105
|
+
},
|
|
106
|
+
staleTime: staleTime ?? 30_000,
|
|
107
|
+
enabled: (enabled ?? true) && !!addonKey,
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
const settings = useMemo(
|
|
111
|
+
() => mergeAddonSettings<T>(defaults, query.data),
|
|
112
|
+
[defaults, query.data],
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
settings,
|
|
117
|
+
isLoading: query.isLoading,
|
|
118
|
+
error: query.error,
|
|
119
|
+
refetch: () => {
|
|
120
|
+
void query.refetch()
|
|
121
|
+
},
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Optional write companion — PUTs a (partial) settings object and invalidates
|
|
127
|
+
* the read query so `useAddonSettings` re-resolves. The host's generic config
|
|
128
|
+
* modal does its own PUT; this exists for addons that ship a custom config
|
|
129
|
+
* surface. Backward-compatible: purely additive.
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* const update = useUpdateAddonSettings('pos')
|
|
133
|
+
* update.mutate({ allowNegativeStock: true })
|
|
134
|
+
*/
|
|
135
|
+
export function useUpdateAddonSettings<T extends Record<string, unknown> = Record<string, unknown>>(
|
|
136
|
+
addonKey: string,
|
|
137
|
+
) {
|
|
138
|
+
const api = useApi()
|
|
139
|
+
const qc = useQueryClient()
|
|
140
|
+
|
|
141
|
+
return useMutation<Partial<T>, Error, Partial<T>>({
|
|
142
|
+
mutationFn: async (values) => {
|
|
143
|
+
const res = await api.put(`/api/addons/${addonKey}/settings`, values)
|
|
144
|
+
const body = (res as { data: any }).data
|
|
145
|
+
const saved = body?.success ? body.data : body
|
|
146
|
+
return (saved ?? values) as Partial<T>
|
|
147
|
+
},
|
|
148
|
+
onSuccess: () => {
|
|
149
|
+
void qc.invalidateQueries({ queryKey: addonSettingsKey(addonKey) })
|
|
150
|
+
},
|
|
151
|
+
})
|
|
152
|
+
}
|