@eintrek/erp-shell 0.1.20 → 0.1.22
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/README.md +3 -2
- package/dist/hooks/use-org-side-effects.d.ts +14 -3
- package/dist/index.esm.js +68 -15
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +68 -15
- package/dist/index.js.map +1 -1
- package/dist/navigation/navigation-data-helpers.d.ts +3 -1
- package/dist/navigation/organization-switcher.d.ts +5 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @eintrek/erp-shell
|
|
2
2
|
|
|
3
|
-
Shared frontend platform for ERP apps (erp-accounting, erp-hr, …).
|
|
3
|
+
Shared frontend platform for ERP apps (erp-accounting, erp-hr, erp-platform, …).
|
|
4
4
|
|
|
5
5
|
Holds the cross-app glue that was previously duplicated in every consumer:
|
|
6
6
|
HTTP client, auth/token plumbing, organization-id context, app chrome
|
|
@@ -81,7 +81,8 @@ Shell now owns the **shapes + helpers** for navigation; apps own the data.
|
|
|
81
81
|
slot for app narrowing), `Tool`, `Company`, `NavigationData`.
|
|
82
82
|
- Helpers: `canAccessNavPermissionRoute`, `filterNavigationToolsByPermission`,
|
|
83
83
|
`filterNavigationModulesByPermission`, `getActiveModule(pathname, modules)`,
|
|
84
|
-
`getActiveSubModule(pathname, modules)`, `buildUrlWithCode(code, url)
|
|
84
|
+
`getActiveSubModule(pathname, modules)`, `buildUrlWithCode(code, url)`
|
|
85
|
+
(absolute `http(s)://` URLs pass through; `{code}` placeholder is replaced).
|
|
85
86
|
- Components: `NavTools` (reads Tool[] + buildUrlWithCode internally),
|
|
86
87
|
`AppBreadcrumb` (accepts `labels` prop so per-app Thai overrides stay local).
|
|
87
88
|
|
|
@@ -11,8 +11,19 @@
|
|
|
11
11
|
*/
|
|
12
12
|
export declare function useInvalidateQueriesOnOrgChange(organizationId: number | string | null | undefined): void;
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
14
|
+
* Clear the selected organization when the signed-in user actually changes.
|
|
15
|
+
*
|
|
16
|
+
* Only a transition between two KNOWN users counts. An empty or absent id
|
|
17
|
+
* means "not resolved yet" — it is not a different person, and treating it as
|
|
18
|
+
* one is what made this fire on every cold load: hosts derive the id as
|
|
19
|
+
* `session?.user?.id ?? ""`, so the first effect run saw `""`, and the moment
|
|
20
|
+
* NextAuth resolved, `"" !== "abc"` looked like a user switch and wiped the
|
|
21
|
+
* org. Downstream that nulls organizationId, which re-keys every org-scoped
|
|
22
|
+
* query and remounts the page — visible as settings snapping back to their
|
|
23
|
+
* defaults.
|
|
24
|
+
*
|
|
25
|
+
* Remembering the last known id (rather than the last value seen) keeps the
|
|
26
|
+
* real case working: user A signs out — id goes empty, ignored — user B signs
|
|
27
|
+
* in, A !== B, and the org is cleared.
|
|
17
28
|
*/
|
|
18
29
|
export declare function useClearOrgOnUserChange(userId: string | null | undefined, clearOrg: (id: null) => Promise<void> | void): void;
|
package/dist/index.esm.js
CHANGED
|
@@ -367,18 +367,32 @@ function useInvalidateQueriesOnOrgChange(organizationId) {
|
|
|
367
367
|
}, [organizationId, queryClient]);
|
|
368
368
|
}
|
|
369
369
|
/**
|
|
370
|
-
*
|
|
371
|
-
*
|
|
372
|
-
*
|
|
370
|
+
* Clear the selected organization when the signed-in user actually changes.
|
|
371
|
+
*
|
|
372
|
+
* Only a transition between two KNOWN users counts. An empty or absent id
|
|
373
|
+
* means "not resolved yet" — it is not a different person, and treating it as
|
|
374
|
+
* one is what made this fire on every cold load: hosts derive the id as
|
|
375
|
+
* `session?.user?.id ?? ""`, so the first effect run saw `""`, and the moment
|
|
376
|
+
* NextAuth resolved, `"" !== "abc"` looked like a user switch and wiped the
|
|
377
|
+
* org. Downstream that nulls organizationId, which re-keys every org-scoped
|
|
378
|
+
* query and remounts the page — visible as settings snapping back to their
|
|
379
|
+
* defaults.
|
|
380
|
+
*
|
|
381
|
+
* Remembering the last known id (rather than the last value seen) keeps the
|
|
382
|
+
* real case working: user A signs out — id goes empty, ignored — user B signs
|
|
383
|
+
* in, A !== B, and the org is cleared.
|
|
373
384
|
*/
|
|
374
385
|
function useClearOrgOnUserChange(userId, clearOrg) {
|
|
375
|
-
const
|
|
386
|
+
const lastKnownUserIdRef = useRef(null);
|
|
376
387
|
useEffect(() => {
|
|
377
|
-
|
|
378
|
-
|
|
388
|
+
const next = userId ?? "";
|
|
389
|
+
if (next === "")
|
|
390
|
+
return;
|
|
391
|
+
const previous = lastKnownUserIdRef.current;
|
|
392
|
+
lastKnownUserIdRef.current = next;
|
|
393
|
+
if (previous !== null && previous !== next) {
|
|
379
394
|
Promise.resolve(clearOrg(null)).catch(console.error);
|
|
380
395
|
}
|
|
381
|
-
previousUserIdRef.current = userId ?? null;
|
|
382
396
|
}, [userId, clearOrg]);
|
|
383
397
|
}
|
|
384
398
|
|
|
@@ -678,8 +692,15 @@ function getActiveSubModule(pathname, modules) {
|
|
|
678
692
|
}
|
|
679
693
|
return null;
|
|
680
694
|
}
|
|
681
|
-
/** Prepend `code` (org code) to a relative URL so `/sales` → `/O25.../sales`.
|
|
695
|
+
/** Prepend `code` (org code) to a relative URL so `/sales` → `/O25.../sales`.
|
|
696
|
+
* Absolute `http(s)://` URLs are left intact; optional `{code}` placeholder is replaced.
|
|
697
|
+
*/
|
|
682
698
|
function buildUrlWithCode(code, url) {
|
|
699
|
+
if (/^https?:\/\//i.test(url)) {
|
|
700
|
+
if (!code)
|
|
701
|
+
return url.split("{code}").join("");
|
|
702
|
+
return url.split("{code}").join(code);
|
|
703
|
+
}
|
|
683
704
|
if (!code)
|
|
684
705
|
return url;
|
|
685
706
|
const cleanUrl = url.startsWith("/") ? url.slice(1) : url;
|
|
@@ -826,7 +847,7 @@ function NavUser({ organization, avatarSrc, profileHref, selectOrganizationHref
|
|
|
826
847
|
{ code: "zh", label: "中文" },
|
|
827
848
|
{ code: "ja", label: "日本語" },
|
|
828
849
|
].map(({ code, label }) => (jsxs("button", { onClick: () => handleLanguageChange(code), className: "flex w-full items-center gap-3 px-3 py-2 text-sm text-sidebar-foreground hover:bg-sidebar-accent rounded-md transition-colors", children: [jsx("span", { className: "flex-1 text-left", children: label }), organization?.language === code && (jsx("div", { className: "h-2 w-2 rounded-full bg-primary" }))] }, code))) }))] }), profileHref && (jsxs("button", { onClick: handleProfile, className: "flex w-full items-center gap-3 px-3 py-2.5 text-sm text-sidebar-foreground hover:bg-sidebar-accent rounded-md transition-colors", children: [jsx(User, { className: "h-4 w-4" }), jsx("span", { className: "flex-1 text-left", children: "\u0E02\u0E49\u0E2D\u0E21\u0E39\u0E25\u0E2A\u0E48\u0E27\u0E19\u0E15\u0E31\u0E27" })] })), jsxs("button", { onClick: handleSelectOrganization, className: "flex w-full items-center gap-3 px-3 py-2.5 text-sm text-sidebar-foreground hover:bg-sidebar-accent rounded-md transition-colors", children: [jsx(Building2, { className: "h-4 w-4" }), jsx("span", { className: "flex-1 text-left", children: "\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E18\u0E38\u0E23\u0E01\u0E34\u0E08" })] }), jsx("button", { onClick: handleSignOut, disabled: isSigningOut, className: "flex w-full items-center gap-3 px-3 py-2.5 text-sm text-sidebar-foreground hover:bg-sidebar-accent rounded-md transition-colors disabled:opacity-50 disabled:cursor-not-allowed mt-1", children: isSigningOut ? (jsxs(Fragment$1, { children: [jsx(Loader2, { className: "h-4 w-4 animate-spin" }), jsx("span", { className: "flex-1 text-left", children: "\u0E01\u0E33\u0E25\u0E31\u0E07\u0E2D\u0E2D\u0E01\u0E08\u0E32\u0E01\u0E23\u0E30\u0E1A\u0E1A..." })] })) : (jsxs(Fragment$1, { children: [jsx(LogOut, { className: "h-4 w-4" }), jsx("span", { className: "flex-1 text-left", children: "\u0E2D\u0E2D\u0E01\u0E08\u0E32\u0E01\u0E23\u0E30\u0E1A\u0E1A" })] })) })] })] })) : null;
|
|
829
|
-
return (jsx("div", { className: "p-2", children: jsxs("div", { className: "relative", children: [jsxs("button", { ref: buttonRef, onClick: () => setIsOpen(!isOpen), className: "flex w-full items-center gap-2 p-2 rounded-md transition-colors", children: [jsxs(Avatar, { className: "h-8 w-8 rounded-lg", children: [jsx(AvatarImage, { src: user.avatar, alt: user.name }), jsx(AvatarFallback, { className: "rounded-lg bg-[#E4525F] text-sidebar-primary-foreground w-8 h-8", children: getInitials(user.name) })] }), jsxs("div", { className: "grid flex-1 text-left text-sm leading-tight", children: [jsx("span", { className: "truncate font-medium", title: user.name, children: user.name }), jsx("span", { className: "truncate text-xs text-sidebar-foreground/70", title: user.email, children: user.email })] }), jsx(ChevronUp, { className: `ml-auto size-4 transition-transform ${isOpen ? "rotate-180" : ""}` })] }), typeof window !== "undefined" &&
|
|
850
|
+
return (jsx("div", { className: "p-2", children: jsxs("div", { className: "relative", children: [jsxs("button", { ref: buttonRef, onClick: () => setIsOpen(!isOpen), className: "flex w-full items-center gap-2 p-2 rounded-md transition-colors", children: [jsxs(Avatar, { className: "h-8 w-8 rounded-lg", children: [user.avatar ? (jsx(AvatarImage, { src: user.avatar, alt: user.name })) : null, jsx(AvatarFallback, { className: "rounded-lg bg-[#E4525F] text-sidebar-primary-foreground w-8 h-8", children: getInitials(user.name) })] }), jsxs("div", { className: "grid flex-1 text-left text-sm leading-tight", children: [jsx("span", { className: "truncate font-medium", title: user.name, children: user.name }), jsx("span", { className: "truncate text-xs text-sidebar-foreground/70", title: user.email, children: user.email })] }), jsx(ChevronUp, { className: `ml-auto size-4 transition-transform ${isOpen ? "rotate-180" : ""}` })] }), typeof window !== "undefined" &&
|
|
830
851
|
createPortal(dropdownContent, document.body)] }) }));
|
|
831
852
|
}
|
|
832
853
|
|
|
@@ -905,6 +926,17 @@ function NavMain({ items, orgCode, closeOnNavigation = false, approvalCounts, })
|
|
|
905
926
|
}) }) }));
|
|
906
927
|
}
|
|
907
928
|
|
|
929
|
+
function OrgMark({ logoUrl, size = "md", }) {
|
|
930
|
+
const [failed, setFailed] = useState(false);
|
|
931
|
+
const box = size === "sm"
|
|
932
|
+
? "flex size-6 items-center justify-center rounded-md border overflow-hidden bg-background"
|
|
933
|
+
: "flex aspect-square size-8 shrink-0 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground overflow-hidden";
|
|
934
|
+
const icon = size === "sm" ? "size-3.5 shrink-0" : "size-4";
|
|
935
|
+
if (logoUrl && !failed) {
|
|
936
|
+
return (jsx("div", { className: cn(box, size === "md" && "bg-white"), children: jsx("img", { src: logoUrl, alt: "", className: "size-full object-contain p-0.5", onError: () => setFailed(true) }) }));
|
|
937
|
+
}
|
|
938
|
+
return (jsx("div", { className: box, children: jsx(Building2, { className: icon }) }));
|
|
939
|
+
}
|
|
908
940
|
function OrganizationSwitcher({ activeOrganization, isContextLoading = false, organizations, isLoadingOrgs = false, setOrganizationCookie, registerOrganizationHref = "/register-organization", className, }) {
|
|
909
941
|
const { isMobile } = useSidebarToggle();
|
|
910
942
|
const router = useRouter();
|
|
@@ -950,10 +982,10 @@ function OrganizationSwitcher({ activeOrganization, isContextLoading = false, or
|
|
|
950
982
|
return (jsx(SidebarMenu, { className: className, children: jsx(SidebarMenuItem, { children: jsxs("div", { className: "flex items-center gap-2 p-2", children: [jsx(Skeleton, { className: "size-8 rounded-lg" }), jsx(Skeleton, { className: "h-4 w-24" })] }) }) }));
|
|
951
983
|
}
|
|
952
984
|
if (organizations.length <= 1) {
|
|
953
|
-
return (jsx(SidebarMenu, { className: className, children: jsx(SidebarMenuItem, { children: jsxs("div", { className: "flex items-center gap-2 p-2", children: [jsx(
|
|
985
|
+
return (jsx(SidebarMenu, { className: className, children: jsx(SidebarMenuItem, { children: jsxs("div", { className: "flex items-center gap-2 p-2", children: [jsx(OrgMark, { logoUrl: activeOrganization?.logoUrl }), jsx("span", { className: "truncate font-semibold text-sm min-w-0 flex-1", children: activeOrganization?.name || "ไม่มีองค์กร" })] }) }) }));
|
|
954
986
|
}
|
|
955
|
-
return (jsx(SidebarMenu, { className: className, children: jsx(SidebarMenuItem, { children: jsxs(DropdownMenu, { children: [jsx(DropdownMenuTrigger, { asChild: true, children: jsxs(SidebarMenuButton, { size: "lg", className: "data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground", children: [jsx(
|
|
956
|
-
"bg-accent"), children: [jsx(
|
|
987
|
+
return (jsx(SidebarMenu, { className: className, children: jsx(SidebarMenuItem, { children: jsxs(DropdownMenu, { children: [jsx(DropdownMenuTrigger, { asChild: true, children: jsxs(SidebarMenuButton, { size: "lg", className: "data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground", children: [jsx(OrgMark, { logoUrl: activeOrganization?.logoUrl }), jsx("span", { className: "truncate font-semibold text-sm flex-1 min-w-0 text-left", children: activeOrganization?.name || "เลือกองค์กร" }), isPending ? (jsx(Loader2, { className: "ml-auto size-4 animate-spin" })) : (jsx(ChevronsUpDown, { className: "ml-auto size-4" }))] }) }), jsxs(DropdownMenuContent, { className: "w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg", align: "start", side: isMobile ? "bottom" : "right", sideOffset: 4, children: [jsx(DropdownMenuLabel, { className: "text-xs text-muted-foreground", children: "\u0E2D\u0E07\u0E04\u0E4C\u0E01\u0E23" }), organizations.map(org => (jsxs(DropdownMenuItem, { onClick: () => handleOrganizationChange(org), disabled: isPending, className: cn("gap-2 p-2 cursor-pointer", String(activeOrganization?.id) === String(org.id) &&
|
|
988
|
+
"bg-accent"), children: [jsx(OrgMark, { logoUrl: org.logoUrl, size: "sm" }), jsx("span", { className: "truncate", children: org.name })] }, org.id))), jsx(DropdownMenuSeparator, {}), jsxs(DropdownMenuItem, { className: "gap-2 p-2 cursor-pointer", onClick: () => router.push(registerOrganizationHref), children: [jsx("div", { className: "flex size-6 items-center justify-center rounded-md border bg-transparent", children: jsx(Plus, { className: "size-4" }) }), jsx("span", { className: "text-muted-foreground font-medium", children: "\u0E40\u0E1E\u0E34\u0E48\u0E21\u0E2D\u0E07\u0E04\u0E4C\u0E01\u0E23" })] })] })] }) }) }));
|
|
957
989
|
}
|
|
958
990
|
|
|
959
991
|
function SidebarHeader({ children, className }) {
|
|
@@ -1027,6 +1059,25 @@ function AppLayoutShell({ children, sidebar, loadingComponent, breadcrumb, permi
|
|
|
1027
1059
|
code,
|
|
1028
1060
|
organizations.length,
|
|
1029
1061
|
]);
|
|
1062
|
+
// Key for the content subtree. It exists so switching organizations throws
|
|
1063
|
+
// away the previous org's component state — but it must react ONLY to a real
|
|
1064
|
+
// switch. organizationId legitimately passes through null while the context
|
|
1065
|
+
// re-settles (session resolving, cookie write in flight), and keying on that
|
|
1066
|
+
// unmounted and remounted the entire page: forms lost their loaded values,
|
|
1067
|
+
// in-flight edits vanished, and settings pickers snapped back to defaults.
|
|
1068
|
+
//
|
|
1069
|
+
// Holding the last known id makes a transient null a no-op while a genuine
|
|
1070
|
+
// org-to-org switch still changes the key.
|
|
1071
|
+
const lastOrgKeyRef = React.useRef("no-org");
|
|
1072
|
+
const currentOrgKey = organizationId === null ||
|
|
1073
|
+
organizationId === undefined ||
|
|
1074
|
+
String(organizationId).trim() === ""
|
|
1075
|
+
? null
|
|
1076
|
+
: String(organizationId);
|
|
1077
|
+
if (currentOrgKey !== null) {
|
|
1078
|
+
lastOrgKeyRef.current = currentOrgKey;
|
|
1079
|
+
}
|
|
1080
|
+
const contentOrgKey = lastOrgKeyRef.current;
|
|
1030
1081
|
// ≤1024px: sidebar renders as a modal overlay (backdrop dims content). >1024px:
|
|
1031
1082
|
// sidebar is docked and the content gets a left margin to clear it.
|
|
1032
1083
|
const [isSidebarModalLayout, setIsSidebarModalLayout] = React.useState(false);
|
|
@@ -1073,7 +1124,7 @@ function AppLayoutShell({ children, sidebar, loadingComponent, breadcrumb, permi
|
|
|
1073
1124
|
const contentMargin = open && !isSidebarModalLayout ? "ml-[21.5rem]" : "";
|
|
1074
1125
|
const needsLeftGutter = !(open && !isSidebarModalLayout);
|
|
1075
1126
|
const wrap = permissionProvider ?? ((c) => c);
|
|
1076
|
-
return (jsxs("div", { className: "flex min-h-screen w-full bg-background", children: [open && (jsx("div", { className: "fixed left-0 top-0 z-50 h-screen", children: sidebar })), open && isSidebarModalLayout && (jsx("button", { type: "button", "aria-label": "\u0E1B\u0E34\u0E14\u0E40\u0E21\u0E19\u0E39", className: "fixed inset-0 z-40 bg-black/50", onClick: () => setOpen(false) })), jsx("div", { className: cx("relative flex min-w-0 flex-1 flex-col transition-all", contentMargin), children: jsxs("main", { className: "flex flex-1 flex-col min-h-screen gap-4", children: [jsxs("div", { className: "sticky top-0 z-30 shrink-0 w-full", children: [jsx(HeaderBackground, {}), jsxs("header", { className: "relative shrink-0 gap-2 py-4 h-auto min-h-[123.5px] w-full z-10", children: [jsxs("div", { className: "flex w-full items-center gap-2 px-3 sm:px-4 lg:px-6", children: [jsx(SidebarTrigger, { className: "shrink-0 text-white hover:bg-white/10" }), jsx("div", { className: "flex-1 min-w-0", children: breadcrumb })] }), jsx("div", { id: "header-title" })] })] }), jsx(Suspense, { fallback: jsx("div", { className: "relative z-10 min-h-[calc(100vh-8rem)]", children: jsx("div", { className: "flex h-full items-center justify-center", children: jsxs("div", { className: "space-y-4 text-center", children: [jsx("div", { className: "mx-auto h-8 w-8 animate-spin rounded-full border-b-2 border-primary" }), jsx("p", { className: "text-sm text-muted-foreground", children: "\u0E01\u0E33\u0E25\u0E31\u0E07\u0E42\u0E2B\u0E25\u0E14..." })] }) }) }), children: jsx("div", { className: cx("relative z-10 pr-6", needsLeftGutter && "pl-6"), children: wrap(children) }, `org-${
|
|
1127
|
+
return (jsxs("div", { className: "flex min-h-screen w-full bg-background", children: [open && (jsx("div", { className: "fixed left-0 top-0 z-50 h-screen", children: sidebar })), open && isSidebarModalLayout && (jsx("button", { type: "button", "aria-label": "\u0E1B\u0E34\u0E14\u0E40\u0E21\u0E19\u0E39", className: "fixed inset-0 z-40 bg-black/50", onClick: () => setOpen(false) })), jsx("div", { className: cx("relative flex min-w-0 flex-1 flex-col transition-all", contentMargin), children: jsxs("main", { className: "flex flex-1 flex-col min-h-screen gap-4", children: [jsxs("div", { className: "sticky top-0 z-30 shrink-0 w-full", children: [jsx(HeaderBackground, {}), jsxs("header", { className: "relative shrink-0 gap-2 py-4 h-auto min-h-[123.5px] w-full z-10", children: [jsxs("div", { className: "flex w-full items-center gap-2 px-3 sm:px-4 lg:px-6", children: [jsx(SidebarTrigger, { className: "shrink-0 text-white hover:bg-white/10" }), jsx("div", { className: "flex-1 min-w-0", children: breadcrumb })] }), jsx("div", { id: "header-title" })] })] }), jsx(Suspense, { fallback: jsx("div", { className: "relative z-10 min-h-[calc(100vh-8rem)]", children: jsx("div", { className: "flex h-full items-center justify-center", children: jsxs("div", { className: "space-y-4 text-center", children: [jsx("div", { className: "mx-auto h-8 w-8 animate-spin rounded-full border-b-2 border-primary" }), jsx("p", { className: "text-sm text-muted-foreground", children: "\u0E01\u0E33\u0E25\u0E31\u0E07\u0E42\u0E2B\u0E25\u0E14..." })] }) }) }), children: jsx("div", { className: cx("relative z-10 pr-6", needsLeftGutter && "pl-6"), children: wrap(children) }, `org-${contentOrgKey}`) })] }) })] }));
|
|
1077
1128
|
}
|
|
1078
1129
|
/** Local tiny classnames helper — avoids pulling clsx/tailwind-merge into
|
|
1079
1130
|
* the shell just for the layout file. Falsy values are skipped. */
|
|
@@ -1386,7 +1437,9 @@ function SessionBackedProfileCacheSeeder({ queryKey, mapper, }) {
|
|
|
1386
1437
|
if (mapped) {
|
|
1387
1438
|
const existing = queryClient.getQueryData(queryKey);
|
|
1388
1439
|
if (!existing) {
|
|
1389
|
-
|
|
1440
|
+
// updatedAt: 0 → immediately stale so observers still GET /myprofile
|
|
1441
|
+
// for fields the JWT seed omits (avatar/signature paths).
|
|
1442
|
+
queryClient.setQueryData(queryKey, mapped, { updatedAt: 0 });
|
|
1390
1443
|
}
|
|
1391
1444
|
else {
|
|
1392
1445
|
// Only overwrite when the mapped id changed — we don't know T's shape
|
|
@@ -1394,7 +1447,7 @@ function SessionBackedProfileCacheSeeder({ queryKey, mapper, }) {
|
|
|
1394
1447
|
const existingId = existing?.id;
|
|
1395
1448
|
const mappedId = mapped?.id;
|
|
1396
1449
|
if (existingId !== mappedId) {
|
|
1397
|
-
queryClient.setQueryData(queryKey, mapped);
|
|
1450
|
+
queryClient.setQueryData(queryKey, mapped, { updatedAt: 0 });
|
|
1398
1451
|
}
|
|
1399
1452
|
}
|
|
1400
1453
|
}
|