@eintrek/erp-shell 0.1.21 → 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/dist/index.js CHANGED
@@ -711,8 +711,15 @@ function getActiveSubModule(pathname, modules) {
711
711
  }
712
712
  return null;
713
713
  }
714
- /** Prepend `code` (org code) to a relative URL so `/sales` → `/O25.../sales`. */
714
+ /** Prepend `code` (org code) to a relative URL so `/sales` → `/O25.../sales`.
715
+ * Absolute `http(s)://` URLs are left intact; optional `{code}` placeholder is replaced.
716
+ */
715
717
  function buildUrlWithCode(code, url) {
718
+ if (/^https?:\/\//i.test(url)) {
719
+ if (!code)
720
+ return url.split("{code}").join("");
721
+ return url.split("{code}").join(code);
722
+ }
716
723
  if (!code)
717
724
  return url;
718
725
  const cleanUrl = url.startsWith("/") ? url.slice(1) : url;
@@ -859,7 +866,7 @@ function NavUser({ organization, avatarSrc, profileHref, selectOrganizationHref
859
866
  { code: "zh", label: "中文" },
860
867
  { code: "ja", label: "日本語" },
861
868
  ].map(({ code, label }) => (jsxRuntime.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: [jsxRuntime.jsx("span", { className: "flex-1 text-left", children: label }), organization?.language === code && (jsxRuntime.jsx("div", { className: "h-2 w-2 rounded-full bg-primary" }))] }, code))) }))] }), profileHref && (jsxRuntime.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: [jsxRuntime.jsx(lucideReact.User, { className: "h-4 w-4" }), jsxRuntime.jsx("span", { className: "flex-1 text-left", children: "\u0E02\u0E49\u0E2D\u0E21\u0E39\u0E25\u0E2A\u0E48\u0E27\u0E19\u0E15\u0E31\u0E27" })] })), jsxRuntime.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: [jsxRuntime.jsx(lucideReact.Building2, { className: "h-4 w-4" }), jsxRuntime.jsx("span", { className: "flex-1 text-left", children: "\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E18\u0E38\u0E23\u0E01\u0E34\u0E08" })] }), jsxRuntime.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 ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 animate-spin" }), jsxRuntime.jsx("span", { className: "flex-1 text-left", children: "\u0E01\u0E33\u0E25\u0E31\u0E07\u0E2D\u0E2D\u0E01\u0E08\u0E32\u0E01\u0E23\u0E30\u0E1A\u0E1A..." })] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(lucideReact.LogOut, { className: "h-4 w-4" }), jsxRuntime.jsx("span", { className: "flex-1 text-left", children: "\u0E2D\u0E2D\u0E01\u0E08\u0E32\u0E01\u0E23\u0E30\u0E1A\u0E1A" })] })) })] })] })) : null;
862
- return (jsxRuntime.jsx("div", { className: "p-2", children: jsxRuntime.jsxs("div", { className: "relative", children: [jsxRuntime.jsxs("button", { ref: buttonRef, onClick: () => setIsOpen(!isOpen), className: "flex w-full items-center gap-2 p-2 rounded-md transition-colors", children: [jsxRuntime.jsxs(erpTheme.Avatar, { className: "h-8 w-8 rounded-lg", children: [jsxRuntime.jsx(erpTheme.AvatarImage, { src: user.avatar, alt: user.name }), jsxRuntime.jsx(erpTheme.AvatarFallback, { className: "rounded-lg bg-[#E4525F] text-sidebar-primary-foreground w-8 h-8", children: getInitials(user.name) })] }), jsxRuntime.jsxs("div", { className: "grid flex-1 text-left text-sm leading-tight", children: [jsxRuntime.jsx("span", { className: "truncate font-medium", title: user.name, children: user.name }), jsxRuntime.jsx("span", { className: "truncate text-xs text-sidebar-foreground/70", title: user.email, children: user.email })] }), jsxRuntime.jsx(lucideReact.ChevronUp, { className: `ml-auto size-4 transition-transform ${isOpen ? "rotate-180" : ""}` })] }), typeof window !== "undefined" &&
869
+ return (jsxRuntime.jsx("div", { className: "p-2", children: jsxRuntime.jsxs("div", { className: "relative", children: [jsxRuntime.jsxs("button", { ref: buttonRef, onClick: () => setIsOpen(!isOpen), className: "flex w-full items-center gap-2 p-2 rounded-md transition-colors", children: [jsxRuntime.jsxs(erpTheme.Avatar, { className: "h-8 w-8 rounded-lg", children: [user.avatar ? (jsxRuntime.jsx(erpTheme.AvatarImage, { src: user.avatar, alt: user.name })) : null, jsxRuntime.jsx(erpTheme.AvatarFallback, { className: "rounded-lg bg-[#E4525F] text-sidebar-primary-foreground w-8 h-8", children: getInitials(user.name) })] }), jsxRuntime.jsxs("div", { className: "grid flex-1 text-left text-sm leading-tight", children: [jsxRuntime.jsx("span", { className: "truncate font-medium", title: user.name, children: user.name }), jsxRuntime.jsx("span", { className: "truncate text-xs text-sidebar-foreground/70", title: user.email, children: user.email })] }), jsxRuntime.jsx(lucideReact.ChevronUp, { className: `ml-auto size-4 transition-transform ${isOpen ? "rotate-180" : ""}` })] }), typeof window !== "undefined" &&
863
870
  reactDom.createPortal(dropdownContent, document.body)] }) }));
864
871
  }
865
872
 
@@ -938,6 +945,17 @@ function NavMain({ items, orgCode, closeOnNavigation = false, approvalCounts, })
938
945
  }) }) }));
939
946
  }
940
947
 
948
+ function OrgMark({ logoUrl, size = "md", }) {
949
+ const [failed, setFailed] = React.useState(false);
950
+ const box = size === "sm"
951
+ ? "flex size-6 items-center justify-center rounded-md border overflow-hidden bg-background"
952
+ : "flex aspect-square size-8 shrink-0 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground overflow-hidden";
953
+ const icon = size === "sm" ? "size-3.5 shrink-0" : "size-4";
954
+ if (logoUrl && !failed) {
955
+ return (jsxRuntime.jsx("div", { className: erpTheme.cn(box, size === "md" && "bg-white"), children: jsxRuntime.jsx("img", { src: logoUrl, alt: "", className: "size-full object-contain p-0.5", onError: () => setFailed(true) }) }));
956
+ }
957
+ return (jsxRuntime.jsx("div", { className: box, children: jsxRuntime.jsx(lucideReact.Building2, { className: icon }) }));
958
+ }
941
959
  function OrganizationSwitcher({ activeOrganization, isContextLoading = false, organizations, isLoadingOrgs = false, setOrganizationCookie, registerOrganizationHref = "/register-organization", className, }) {
942
960
  const { isMobile } = useSidebarToggle();
943
961
  const router = navigation.useRouter();
@@ -983,10 +1001,10 @@ function OrganizationSwitcher({ activeOrganization, isContextLoading = false, or
983
1001
  return (jsxRuntime.jsx(SidebarMenu, { className: className, children: jsxRuntime.jsx(SidebarMenuItem, { children: jsxRuntime.jsxs("div", { className: "flex items-center gap-2 p-2", children: [jsxRuntime.jsx(erpTheme.Skeleton, { className: "size-8 rounded-lg" }), jsxRuntime.jsx(erpTheme.Skeleton, { className: "h-4 w-24" })] }) }) }));
984
1002
  }
985
1003
  if (organizations.length <= 1) {
986
- return (jsxRuntime.jsx(SidebarMenu, { className: className, children: jsxRuntime.jsx(SidebarMenuItem, { children: jsxRuntime.jsxs("div", { className: "flex items-center gap-2 p-2", children: [jsxRuntime.jsx("div", { className: "flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground", children: jsxRuntime.jsx(lucideReact.Building2, { className: "size-4" }) }), jsxRuntime.jsx("span", { className: "truncate font-semibold text-sm min-w-0 flex-1", children: activeOrganization?.name || "ไม่มีองค์กร" })] }) }) }));
1004
+ return (jsxRuntime.jsx(SidebarMenu, { className: className, children: jsxRuntime.jsx(SidebarMenuItem, { children: jsxRuntime.jsxs("div", { className: "flex items-center gap-2 p-2", children: [jsxRuntime.jsx(OrgMark, { logoUrl: activeOrganization?.logoUrl }), jsxRuntime.jsx("span", { className: "truncate font-semibold text-sm min-w-0 flex-1", children: activeOrganization?.name || "ไม่มีองค์กร" })] }) }) }));
987
1005
  }
988
- return (jsxRuntime.jsx(SidebarMenu, { className: className, children: jsxRuntime.jsx(SidebarMenuItem, { children: jsxRuntime.jsxs(erpTheme.DropdownMenu, { children: [jsxRuntime.jsx(erpTheme.DropdownMenuTrigger, { asChild: true, children: jsxRuntime.jsxs(SidebarMenuButton, { size: "lg", className: "data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground", children: [jsxRuntime.jsx("div", { className: "flex aspect-square size-8 shrink-0 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground", children: jsxRuntime.jsx(lucideReact.Building2, { className: "size-4" }) }), jsxRuntime.jsx("span", { className: "truncate font-semibold text-sm flex-1 min-w-0 text-left", children: activeOrganization?.name || "เลือกองค์กร" }), isPending ? (jsxRuntime.jsx(lucideReact.Loader2, { className: "ml-auto size-4 animate-spin" })) : (jsxRuntime.jsx(lucideReact.ChevronsUpDown, { className: "ml-auto size-4" }))] }) }), jsxRuntime.jsxs(erpTheme.DropdownMenuContent, { className: "w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg", align: "start", side: isMobile ? "bottom" : "right", sideOffset: 4, children: [jsxRuntime.jsx(erpTheme.DropdownMenuLabel, { className: "text-xs text-muted-foreground", children: "\u0E2D\u0E07\u0E04\u0E4C\u0E01\u0E23" }), organizations.map(org => (jsxRuntime.jsxs(erpTheme.DropdownMenuItem, { onClick: () => handleOrganizationChange(org), disabled: isPending, className: erpTheme.cn("gap-2 p-2 cursor-pointer", String(activeOrganization?.id) === String(org.id) &&
989
- "bg-accent"), children: [jsxRuntime.jsx("div", { className: "flex size-6 items-center justify-center rounded-md border", children: jsxRuntime.jsx(lucideReact.Building2, { className: "size-3.5 shrink-0" }) }), jsxRuntime.jsx("span", { className: "truncate", children: org.name })] }, org.id))), jsxRuntime.jsx(erpTheme.DropdownMenuSeparator, {}), jsxRuntime.jsxs(erpTheme.DropdownMenuItem, { className: "gap-2 p-2 cursor-pointer", onClick: () => router.push(registerOrganizationHref), children: [jsxRuntime.jsx("div", { className: "flex size-6 items-center justify-center rounded-md border bg-transparent", children: jsxRuntime.jsx(lucideReact.Plus, { className: "size-4" }) }), jsxRuntime.jsx("span", { className: "text-muted-foreground font-medium", children: "\u0E40\u0E1E\u0E34\u0E48\u0E21\u0E2D\u0E07\u0E04\u0E4C\u0E01\u0E23" })] })] })] }) }) }));
1006
+ return (jsxRuntime.jsx(SidebarMenu, { className: className, children: jsxRuntime.jsx(SidebarMenuItem, { children: jsxRuntime.jsxs(erpTheme.DropdownMenu, { children: [jsxRuntime.jsx(erpTheme.DropdownMenuTrigger, { asChild: true, children: jsxRuntime.jsxs(SidebarMenuButton, { size: "lg", className: "data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground", children: [jsxRuntime.jsx(OrgMark, { logoUrl: activeOrganization?.logoUrl }), jsxRuntime.jsx("span", { className: "truncate font-semibold text-sm flex-1 min-w-0 text-left", children: activeOrganization?.name || "เลือกองค์กร" }), isPending ? (jsxRuntime.jsx(lucideReact.Loader2, { className: "ml-auto size-4 animate-spin" })) : (jsxRuntime.jsx(lucideReact.ChevronsUpDown, { className: "ml-auto size-4" }))] }) }), jsxRuntime.jsxs(erpTheme.DropdownMenuContent, { className: "w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg", align: "start", side: isMobile ? "bottom" : "right", sideOffset: 4, children: [jsxRuntime.jsx(erpTheme.DropdownMenuLabel, { className: "text-xs text-muted-foreground", children: "\u0E2D\u0E07\u0E04\u0E4C\u0E01\u0E23" }), organizations.map(org => (jsxRuntime.jsxs(erpTheme.DropdownMenuItem, { onClick: () => handleOrganizationChange(org), disabled: isPending, className: erpTheme.cn("gap-2 p-2 cursor-pointer", String(activeOrganization?.id) === String(org.id) &&
1007
+ "bg-accent"), children: [jsxRuntime.jsx(OrgMark, { logoUrl: org.logoUrl, size: "sm" }), jsxRuntime.jsx("span", { className: "truncate", children: org.name })] }, org.id))), jsxRuntime.jsx(erpTheme.DropdownMenuSeparator, {}), jsxRuntime.jsxs(erpTheme.DropdownMenuItem, { className: "gap-2 p-2 cursor-pointer", onClick: () => router.push(registerOrganizationHref), children: [jsxRuntime.jsx("div", { className: "flex size-6 items-center justify-center rounded-md border bg-transparent", children: jsxRuntime.jsx(lucideReact.Plus, { className: "size-4" }) }), jsxRuntime.jsx("span", { className: "text-muted-foreground font-medium", children: "\u0E40\u0E1E\u0E34\u0E48\u0E21\u0E2D\u0E07\u0E04\u0E4C\u0E01\u0E23" })] })] })] }) }) }));
990
1008
  }
991
1009
 
992
1010
  function SidebarHeader({ children, className }) {
@@ -1438,7 +1456,9 @@ function SessionBackedProfileCacheSeeder({ queryKey, mapper, }) {
1438
1456
  if (mapped) {
1439
1457
  const existing = queryClient.getQueryData(queryKey);
1440
1458
  if (!existing) {
1441
- queryClient.setQueryData(queryKey, mapped);
1459
+ // updatedAt: 0 → immediately stale so observers still GET /myprofile
1460
+ // for fields the JWT seed omits (avatar/signature paths).
1461
+ queryClient.setQueryData(queryKey, mapped, { updatedAt: 0 });
1442
1462
  }
1443
1463
  else {
1444
1464
  // Only overwrite when the mapped id changed — we don't know T's shape
@@ -1446,7 +1466,7 @@ function SessionBackedProfileCacheSeeder({ queryKey, mapper, }) {
1446
1466
  const existingId = existing?.id;
1447
1467
  const mappedId = mapped?.id;
1448
1468
  if (existingId !== mappedId) {
1449
- queryClient.setQueryData(queryKey, mapped);
1469
+ queryClient.setQueryData(queryKey, mapped, { updatedAt: 0 });
1450
1470
  }
1451
1471
  }
1452
1472
  }