@viu/emporix-sdk-react 2.6.0 → 2.8.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 +81 -0
- package/dist/{chunk-RCI7242Y.js → chunk-GRGWUXNB.js} +75 -19
- package/dist/chunk-GRGWUXNB.js.map +1 -0
- package/dist/hooks.cjs +72 -15
- package/dist/hooks.cjs.map +1 -1
- package/dist/hooks.d.cts +1 -1
- package/dist/hooks.d.ts +1 -1
- package/dist/hooks.js +1 -1
- package/dist/index.cjs +72 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/{use-returns-BED8-FtN.d.ts → use-returns-96kuAgjF.d.ts} +16 -4
- package/dist/{use-returns-BbOXQG4L.d.cts → use-returns-D9g9zYu1.d.cts} +16 -4
- package/package.json +7 -7
- package/dist/chunk-RCI7242Y.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,86 @@
|
|
|
1
1
|
# @viu/emporix-sdk-react
|
|
2
2
|
|
|
3
|
+
## 2.8.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#98](https://github.com/viuteam/emporix-sdk/pull/98) [`108a724`](https://github.com/viuteam/emporix-sdk/commit/108a724f1d4342532ae8d575faa501d54d8c591f) Thanks [@amnael1](https://github.com/amnael1)! - Support partial cart-item updates. `client.carts.updateItem(cartId, itemId,
|
|
8
|
+
patch, auth, { partial: true })` now sends `?partial=true`, so a quantity-only
|
|
9
|
+
change can be `{ quantity }` instead of a full item replace (which otherwise
|
|
10
|
+
requires re-sending `itemYrn` + the `price` row). The React
|
|
11
|
+
`useCartMutations().updateItem` mutation accepts an optional `partial` flag in
|
|
12
|
+
its variables. Default behavior is unchanged.
|
|
13
|
+
|
|
14
|
+
- [#100](https://github.com/viuteam/emporix-sdk/pull/100) [`b4be158`](https://github.com/viuteam/emporix-sdk/commit/b4be1589b2fb0db44852233efe2a5d575a2e2795) Thanks [@amnael1](https://github.com/amnael1)! - `useCustomerSession()` now exposes the current `saasToken`. It was already
|
|
15
|
+
tracked internally (from `login` / `exchangeToken`) but not returned — so
|
|
16
|
+
consumers couldn't pass it to `useCheckout().placeOrder({ ..., saasToken })` for
|
|
17
|
+
customer checkout, or to saas-token-gated order reads.
|
|
18
|
+
|
|
19
|
+
### Patch Changes
|
|
20
|
+
|
|
21
|
+
- [#101](https://github.com/viuteam/emporix-sdk/pull/101) [`e010a5a`](https://github.com/viuteam/emporix-sdk/commit/e010a5ab8f35c92ed946522558db33b2febff5de) Thanks [@amnael1](https://github.com/amnael1)! - fix(react): refresh the cart after a 204-only mutation
|
|
22
|
+
|
|
23
|
+
`useCartMutations` assumed every cart write echoes the full updated cart.
|
|
24
|
+
A partial quantity update (`updateItem(..., { partial: true })`) returns
|
|
25
|
+
`204 No Content`, which the SDK resolves to `undefined` — and
|
|
26
|
+
`setQueryData(key, undefined)` is a no-op in React Query, so the cart cache
|
|
27
|
+
stayed stale and the UI did not reflect the change. The mutation now adopts
|
|
28
|
+
a real cart body when one is returned and otherwise invalidates the cart
|
|
29
|
+
query so it refetches. This also makes coupon/address/remove mutations
|
|
30
|
+
reconcile with the server when they return no body.
|
|
31
|
+
|
|
32
|
+
- [#103](https://github.com/viuteam/emporix-sdk/pull/103) [`2e5c767`](https://github.com/viuteam/emporix-sdk/commit/2e5c76715f5d08358dd9342ef65d7c4c0d8b9aef) Thanks [@amnael1](https://github.com/amnael1)! - fix(react): drop the cart on logout and react to cart-id clearing
|
|
33
|
+
|
|
34
|
+
Two related cleanup gaps caused follow-up errors after logout and checkout:
|
|
35
|
+
- `useCustomerSession().logout()` cleared the customer token but left the
|
|
36
|
+
stored `cartId`. The cart belonged to the customer and isn't accessible
|
|
37
|
+
anonymously, so the cart query immediately refetched it and got a `403`.
|
|
38
|
+
Logout now clears `cartId` too.
|
|
39
|
+
- `useActiveCart` cached the cart id in local state and never reacted to
|
|
40
|
+
external `storage.setCartId(null)` (logout, or the post-order cleanup that
|
|
41
|
+
closes the cart). It kept fetching the dead cart id — a `403` after logout,
|
|
42
|
+
a `404` after checkout. It now subscribes to storage cart-id changes and
|
|
43
|
+
syncs, so clearing the id stops the fetch (and a logged-out cart page
|
|
44
|
+
bootstraps a fresh anonymous cart on demand).
|
|
45
|
+
|
|
46
|
+
- [#102](https://github.com/viuteam/emporix-sdk/pull/102) [`020722b`](https://github.com/viuteam/emporix-sdk/commit/020722b2cb696bdc347538205ea4fad884451d88) Thanks [@amnael1](https://github.com/amnael1)! - fix(react): share the customer session across hook instances
|
|
47
|
+
|
|
48
|
+
`useCustomerSession` kept its session in a per-instance `useState`. The
|
|
49
|
+
`token` slot was mirrored from storage (so `isAuthenticated` was consistent),
|
|
50
|
+
but the in-memory `saasToken` and `refreshToken` lived only in the component
|
|
51
|
+
instance that called `login()`. A different consumer — e.g. the checkout page
|
|
52
|
+
reading `saasToken` for the `saas-token` header — saw `null`, so customer
|
|
53
|
+
checkout failed with `401 "Saas TOKEN is invalid"`.
|
|
54
|
+
|
|
55
|
+
The session now lives in a shared, per-storage store consumed via
|
|
56
|
+
`useSyncExternalStore`, so every `useCustomerSession()` reads the same
|
|
57
|
+
`{ token, refreshToken, saasToken }`. A login in one component is immediately
|
|
58
|
+
visible to all others. The tokens remain in-memory only (still cleared on a
|
|
59
|
+
full reload, by design).
|
|
60
|
+
|
|
61
|
+
## 2.7.0
|
|
62
|
+
|
|
63
|
+
### Minor Changes
|
|
64
|
+
|
|
65
|
+
- [#96](https://github.com/viuteam/emporix-sdk/pull/96) [`da1113a`](https://github.com/viuteam/emporix-sdk/commit/da1113a07f70dceb9f1cb732b28462ccb3671f4a) Thanks [@amnael1](https://github.com/amnael1)! - Fix and extend the Category service for catalogue + hierarchy browsing. Several
|
|
66
|
+
methods targeted routes that don't exist on the deployed category service
|
|
67
|
+
(verified against a live tenant):
|
|
68
|
+
- **`categories.productsIn(...)`** requested a non-existent
|
|
69
|
+
`/categories/{id}/products` route (always 404). It now resolves products via
|
|
70
|
+
category **assignments** (`/categories/{id}/assignments` → keep `PRODUCT`
|
|
71
|
+
refs → `/products/search`), preserving its `PaginatedItems<Product>` contract;
|
|
72
|
+
categories with no products return an empty page instead of throwing.
|
|
73
|
+
- **`categories.tree()`** pointed at a non-existent `/categories/{...}Tree`
|
|
74
|
+
route. It now reads `/category-trees` and returns the catalogue's **root
|
|
75
|
+
categories** (`Promise<Category[]>`) for top-level navigation. (Return type
|
|
76
|
+
changed from the previous nested-node shape; the `rootId` argument is removed.)
|
|
77
|
+
- **New `categories.subcategories(categoryId)`** (+ React `useSubcategories`):
|
|
78
|
+
a category's direct child categories, resolved from `CATEGORY` assignment refs
|
|
79
|
+
(mirrors `productsIn`). Returns `[]` when there are none.
|
|
80
|
+
|
|
81
|
+
React `useCategoryTree()` now returns `Category[]` (root categories) and takes no
|
|
82
|
+
`rootId`.
|
|
83
|
+
|
|
3
84
|
## 2.6.0
|
|
4
85
|
|
|
5
86
|
### Minor Changes
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useEmporix, EmporixSiteContext, useActiveCompany } from './chunk-SDRV73LG.js';
|
|
2
|
-
import { useContext,
|
|
2
|
+
import { useContext, useMemo, useSyncExternalStore, useEffect, useCallback, useState } from 'react';
|
|
3
3
|
import { useQueryClient, useQuery, useMutation, useInfiniteQuery } from '@tanstack/react-query';
|
|
4
4
|
import { auth, EmporixError } from '@viu/emporix-sdk';
|
|
5
5
|
|
|
@@ -29,6 +29,36 @@ async function bootstrapCart(opts) {
|
|
|
29
29
|
});
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
// src/hooks/internal/customer-session-store.ts
|
|
33
|
+
var stores = /* @__PURE__ */ new WeakMap();
|
|
34
|
+
function getCustomerSessionStore(storage) {
|
|
35
|
+
const existing = stores.get(storage);
|
|
36
|
+
if (existing) return existing;
|
|
37
|
+
let state = {
|
|
38
|
+
token: storage.getCustomerToken(),
|
|
39
|
+
refreshToken: null,
|
|
40
|
+
saasToken: null
|
|
41
|
+
};
|
|
42
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
43
|
+
const store = {
|
|
44
|
+
getSnapshot: () => state,
|
|
45
|
+
setState: (next) => {
|
|
46
|
+
const resolved = typeof next === "function" ? next(state) : next;
|
|
47
|
+
if (resolved === state) return;
|
|
48
|
+
state = resolved;
|
|
49
|
+
for (const listener of listeners) listener();
|
|
50
|
+
},
|
|
51
|
+
subscribe: (listener) => {
|
|
52
|
+
listeners.add(listener);
|
|
53
|
+
return () => {
|
|
54
|
+
listeners.delete(listener);
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
stores.set(storage, store);
|
|
59
|
+
return store;
|
|
60
|
+
}
|
|
61
|
+
|
|
32
62
|
// src/hooks/use-customer-session.ts
|
|
33
63
|
var EMPTY_SESSION = {
|
|
34
64
|
token: null,
|
|
@@ -39,14 +69,12 @@ function useCustomerSession() {
|
|
|
39
69
|
const { client, storage } = useEmporix();
|
|
40
70
|
const qc = useQueryClient();
|
|
41
71
|
const siteCtx = useContext(EmporixSiteContext);
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
saasToken: null
|
|
46
|
-
}));
|
|
72
|
+
const store = useMemo(() => getCustomerSessionStore(storage), [storage]);
|
|
73
|
+
const session = useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);
|
|
74
|
+
const setSession = store.setState;
|
|
47
75
|
useEffect(() => {
|
|
48
76
|
return storage.subscribe?.((t) => setSession((s) => ({ ...s, token: t })));
|
|
49
|
-
}, [storage]);
|
|
77
|
+
}, [storage, setSession]);
|
|
50
78
|
const meQuery = useQuery({
|
|
51
79
|
queryKey: ["emporix", "customer", "me", { tenant: client.tenant, hasToken: session.token !== null }],
|
|
52
80
|
enabled: session.token !== null,
|
|
@@ -80,7 +108,7 @@ function useCustomerSession() {
|
|
|
80
108
|
await qc.invalidateQueries({ queryKey: ["emporix", "customer"], refetchType: "none" });
|
|
81
109
|
await qc.invalidateQueries({ queryKey: ["emporix", "cart"], refetchType: "none" });
|
|
82
110
|
},
|
|
83
|
-
[client, storage, qc, siteCtx]
|
|
111
|
+
[client, storage, qc, siteCtx, setSession]
|
|
84
112
|
);
|
|
85
113
|
const signup = useCallback(
|
|
86
114
|
async (input) => {
|
|
@@ -112,7 +140,7 @@ function useCustomerSession() {
|
|
|
112
140
|
await qc.invalidateQueries({ queryKey: ["emporix", "customer"], refetchType: "none" });
|
|
113
141
|
await qc.invalidateQueries({ queryKey: ["emporix", "cart"], refetchType: "none" });
|
|
114
142
|
},
|
|
115
|
-
[client, storage, qc, siteCtx]
|
|
143
|
+
[client, storage, qc, siteCtx, setSession]
|
|
116
144
|
);
|
|
117
145
|
const socialLogin = useCallback(
|
|
118
146
|
async (input) => {
|
|
@@ -136,10 +164,11 @@ function useCustomerSession() {
|
|
|
136
164
|
storage.setCustomerToken(null);
|
|
137
165
|
storage.setRefreshToken(null);
|
|
138
166
|
storage.setActiveLegalEntityId(null);
|
|
167
|
+
storage.setCartId(null);
|
|
139
168
|
setSession(EMPTY_SESSION);
|
|
140
169
|
qc.removeQueries({ queryKey: ["emporix", "customer"] });
|
|
141
170
|
qc.removeQueries({ queryKey: ["emporix", "cart"] });
|
|
142
|
-
}, [client, session.token, storage, qc]);
|
|
171
|
+
}, [client, session.token, storage, qc, setSession]);
|
|
143
172
|
const refresh = useCallback(async () => {
|
|
144
173
|
await meQuery.refetch();
|
|
145
174
|
}, [meQuery]);
|
|
@@ -158,10 +187,11 @@ function useCustomerSession() {
|
|
|
158
187
|
}));
|
|
159
188
|
await qc.invalidateQueries({ queryKey: ["emporix", "customer"] });
|
|
160
189
|
await qc.invalidateQueries({ queryKey: ["emporix", "cart"] });
|
|
161
|
-
}, [client, storage, qc, session.refreshToken, session.saasToken]);
|
|
190
|
+
}, [client, storage, qc, session.refreshToken, session.saasToken, setSession]);
|
|
162
191
|
return {
|
|
163
192
|
customerToken: session.token,
|
|
164
193
|
refreshToken: session.refreshToken,
|
|
194
|
+
saasToken: session.saasToken,
|
|
165
195
|
customer: meQuery.data ?? null,
|
|
166
196
|
isAuthenticated: session.token !== null,
|
|
167
197
|
isLoading: meQuery.isLoading && session.token !== null,
|
|
@@ -425,6 +455,17 @@ function useCategory(categoryId, options = {}) {
|
|
|
425
455
|
staleTime: CATEGORIES_STALE_TIME
|
|
426
456
|
});
|
|
427
457
|
}
|
|
458
|
+
function useSubcategories(categoryId, params = {}, options = {}) {
|
|
459
|
+
const { client } = useEmporix();
|
|
460
|
+
const { ctx } = useReadAuth(options.auth);
|
|
461
|
+
const { siteCode } = useReadSite();
|
|
462
|
+
return useQuery({
|
|
463
|
+
queryKey: emporixKey("subcategories", [categoryId ?? null, params], { tenant: client.tenant, authKind: ctx.kind, siteCode }),
|
|
464
|
+
enabled: typeof categoryId === "string" && categoryId !== "",
|
|
465
|
+
queryFn: () => client.categories.subcategories(categoryId, params, ctx),
|
|
466
|
+
staleTime: CATEGORIES_STALE_TIME
|
|
467
|
+
});
|
|
468
|
+
}
|
|
428
469
|
function useCategories(params = {}, options = {}) {
|
|
429
470
|
const { client } = useEmporix();
|
|
430
471
|
const { ctx } = useReadAuth(options.auth);
|
|
@@ -448,13 +489,13 @@ function useCategoriesInfinite(params = {}, options = {}) {
|
|
|
448
489
|
staleTime: CATEGORIES_STALE_TIME
|
|
449
490
|
});
|
|
450
491
|
}
|
|
451
|
-
function useCategoryTree(
|
|
492
|
+
function useCategoryTree(options = {}) {
|
|
452
493
|
const { client } = useEmporix();
|
|
453
494
|
const { ctx } = useReadAuth(options.auth);
|
|
454
495
|
const { siteCode } = useReadSite();
|
|
455
496
|
return useQuery({
|
|
456
|
-
queryKey: emporixKey("category-tree", [
|
|
457
|
-
queryFn: () => client.categories.tree(
|
|
497
|
+
queryKey: emporixKey("category-tree", [], { tenant: client.tenant, authKind: ctx.kind, siteCode }),
|
|
498
|
+
queryFn: () => client.categories.tree(ctx),
|
|
458
499
|
staleTime: CATEGORIES_STALE_TIME
|
|
459
500
|
});
|
|
460
501
|
}
|
|
@@ -535,7 +576,12 @@ function useCartMutations(cartId) {
|
|
|
535
576
|
if (c) qc.setQueryData(c.key, c.previous);
|
|
536
577
|
},
|
|
537
578
|
onSuccess: (cart, _v, c) => {
|
|
538
|
-
if (c)
|
|
579
|
+
if (!c) return;
|
|
580
|
+
if (cart && Array.isArray(cart.items)) {
|
|
581
|
+
qc.setQueryData(c.key, cart);
|
|
582
|
+
} else {
|
|
583
|
+
void qc.invalidateQueries({ queryKey: c.key });
|
|
584
|
+
}
|
|
539
585
|
}
|
|
540
586
|
});
|
|
541
587
|
}
|
|
@@ -554,7 +600,9 @@ function useCartMutations(cartId) {
|
|
|
554
600
|
]
|
|
555
601
|
} : prev
|
|
556
602
|
),
|
|
557
|
-
updateItem: make(
|
|
603
|
+
updateItem: make(
|
|
604
|
+
(id, v) => client.carts.updateItem(id, v.itemId, v.patch, ctx, v.partial ? { partial: true } : {})
|
|
605
|
+
),
|
|
558
606
|
removeItem: make(
|
|
559
607
|
(id, v) => client.carts.removeItem(id, v.itemId, ctx),
|
|
560
608
|
(prev, v) => prev ? { ...prev, items: (prev.items ?? []).filter((i) => i.id !== v.itemId) } : prev
|
|
@@ -589,6 +637,14 @@ function useActiveCart(opts) {
|
|
|
589
637
|
const { activeCompany } = useActiveCompany();
|
|
590
638
|
const [cartId, setCartId] = useState(() => storage.getCartId());
|
|
591
639
|
const effectiveLegalEntityId = opts?.legalEntityId ?? activeCompany?.id;
|
|
640
|
+
useEffect(() => {
|
|
641
|
+
if (!storage.subscribeAll) return;
|
|
642
|
+
return storage.subscribeAll((key) => {
|
|
643
|
+
if (key !== "cartId") return;
|
|
644
|
+
const next = storage.getCartId();
|
|
645
|
+
setCartId((prev) => prev === next ? prev : next);
|
|
646
|
+
});
|
|
647
|
+
}, [storage]);
|
|
592
648
|
useEffect(() => {
|
|
593
649
|
if (cartId !== null) return;
|
|
594
650
|
if (!opts?.create) return;
|
|
@@ -1419,6 +1475,6 @@ function useUpdateApproval() {
|
|
|
1419
1475
|
});
|
|
1420
1476
|
}
|
|
1421
1477
|
|
|
1422
|
-
export { useActiveCart, useAddToShoppingList, useAddressMutations, useApproval, useApprovals, useAssignContact, useAvailabilities, useAvailability, useCancelOrder, useCart, useCartMutations, useCategories, useCategoriesInfinite, useCategory, useCategoryTree, useChangePassword, useCheckout, useCompany, useCompanyContacts, useCompanyGroups, useCompanyLocations, useCompanySwitcher, useCreateApproval, useCreateCart, useCreateCompany, useCreateLocation, useCreateReturn, useCreateShoppingList, useCustomerAddresses, useCustomerSession, useDefaultSite, useDeleteCompany, useDeleteLocation, useDeleteShoppingList, useMatchPrices, useMatchPricesChunked, useMyCompanies, useMyOrders, useMyOrdersInfinite, useMyReturns, useMyRewardPoints, useMyRewardPointsSummary, useMySegmentCategories, useMySegmentCategoriesInfinite, useMySegmentCategoryTree, useMySegmentItems, useMySegmentProducts, useMySegmentProductsInfinite, useMySegments, useOrder, useOrderTransition, usePasswordReset, usePaymentModes, useProduct, useProductByCode, useProductMedia, useProductSearch, useProducts, useProductsByCodes, useProductsInCategory, useProductsInCategoryInfinite, useProductsInfinite, useRedeemCoupon, useRedeemOptions, useRedeemRewardPoints, useRemoveFromShoppingList, useReorder, useReturn, useSalesOrder, useSetShoppingListItemQuantity, useShoppingLists, useSiteContext, useSites, useUnassignContact, useUpdateApproval, useUpdateCompany, useUpdateContactAssignment, useUpdateCustomer, useUpdateLocation, useUpdateSalesOrder, useValidateCoupon, useVariantChildren };
|
|
1423
|
-
//# sourceMappingURL=chunk-
|
|
1424
|
-
//# sourceMappingURL=chunk-
|
|
1478
|
+
export { useActiveCart, useAddToShoppingList, useAddressMutations, useApproval, useApprovals, useAssignContact, useAvailabilities, useAvailability, useCancelOrder, useCart, useCartMutations, useCategories, useCategoriesInfinite, useCategory, useCategoryTree, useChangePassword, useCheckout, useCompany, useCompanyContacts, useCompanyGroups, useCompanyLocations, useCompanySwitcher, useCreateApproval, useCreateCart, useCreateCompany, useCreateLocation, useCreateReturn, useCreateShoppingList, useCustomerAddresses, useCustomerSession, useDefaultSite, useDeleteCompany, useDeleteLocation, useDeleteShoppingList, useMatchPrices, useMatchPricesChunked, useMyCompanies, useMyOrders, useMyOrdersInfinite, useMyReturns, useMyRewardPoints, useMyRewardPointsSummary, useMySegmentCategories, useMySegmentCategoriesInfinite, useMySegmentCategoryTree, useMySegmentItems, useMySegmentProducts, useMySegmentProductsInfinite, useMySegments, useOrder, useOrderTransition, usePasswordReset, usePaymentModes, useProduct, useProductByCode, useProductMedia, useProductSearch, useProducts, useProductsByCodes, useProductsInCategory, useProductsInCategoryInfinite, useProductsInfinite, useRedeemCoupon, useRedeemOptions, useRedeemRewardPoints, useRemoveFromShoppingList, useReorder, useReturn, useSalesOrder, useSetShoppingListItemQuantity, useShoppingLists, useSiteContext, useSites, useSubcategories, useUnassignContact, useUpdateApproval, useUpdateCompany, useUpdateContactAssignment, useUpdateCustomer, useUpdateLocation, useUpdateSalesOrder, useValidateCoupon, useVariantChildren };
|
|
1479
|
+
//# sourceMappingURL=chunk-GRGWUXNB.js.map
|
|
1480
|
+
//# sourceMappingURL=chunk-GRGWUXNB.js.map
|