@eintrek/erp-shell 0.1.18 → 0.1.20

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.esm.js CHANGED
@@ -719,7 +719,7 @@ function AppBreadcrumb({ labels = {}, homeLabel = "แดชบอร์ด", ro
719
719
  })] }) }));
720
720
  }
721
721
 
722
- function NavUser({ organization, profileHref, selectOrganizationHref = "/select-organization", signInRedirectHref = "/auth/signin", } = {}) {
722
+ function NavUser({ organization, avatarSrc, profileHref, selectOrganizationHref = "/select-organization", signInRedirectHref = "/auth/signin", } = {}) {
723
723
  const { data: session, status } = useSession();
724
724
  const router = useRouter();
725
725
  const { theme, setTheme } = useTheme();
@@ -733,7 +733,10 @@ function NavUser({ organization, profileHref, selectOrganizationHref = "/select-
733
733
  ? {
734
734
  name: session.user.name || "ผู้ใช้",
735
735
  email: session.user.email || "",
736
- avatar: session.user.image || undefined,
736
+ // avatarSrc first: the NextAuth session carries no picture unless the
737
+ // host app deliberately puts one in the JWT, so relying on
738
+ // session.user.image alone left every user on their initials forever.
739
+ avatar: avatarSrc || session.user.image || undefined,
737
740
  }
738
741
  : null;
739
742
  const getInitials = (name) => {
@@ -800,7 +803,14 @@ function NavUser({ organization, profileHref, selectOrganizationHref = "/select-
800
803
  }
801
804
  if (!user)
802
805
  return null;
803
- const dropdownContent = isOpen ? (jsxs("div", { "data-dropdown": "nav-user", className: "fixed w-52 sm:w-60 md:w-80 rounded-lg bg-sidebar border border-sidebar-border shadow-xl z-50 overflow-hidden\n -translate-x-[65%] -translate-y-[110%] sm:translate-x-0 sm:-translate-y-full", style: { top: `${position.top}px`, left: `${position.left}px` }, children: [organization && (jsx("div", { className: "p-4 border-b border-sidebar-border bg-sidebar-accent/30", children: jsxs("div", { className: "flex items-center gap-3", children: [jsx(Building2, { className: "h-5 w-5 text-sidebar-foreground/70 flex-shrink-0" }), jsxs("div", { className: "flex-1 min-w-0", children: [jsx("div", { className: "font-medium text-sm sm:truncate", children: organization.name }), organization.description && (jsx("div", { className: "text-xs text-sidebar-foreground/70 truncate mt-0.5", children: organization.description }))] })] }) })), jsxs("div", { className: "p-2", children: [jsxs("div", { className: "relative", children: [jsxs("button", { onClick: () => {
806
+ const dropdownContent = isOpen ? (jsxs("div", { "data-dropdown": "nav-user", className: "fixed w-52 sm:w-60 md:w-80 rounded-lg bg-sidebar border border-sidebar-border shadow-xl z-50 overflow-hidden\n -translate-x-[65%] -translate-y-[110%] sm:translate-x-0 sm:-translate-y-full", style: { top: `${position.top}px`, left: `${position.left}px` }, children: [organization && (jsx("div", { className: "p-4 border-b border-sidebar-border bg-sidebar-accent/30", children: jsxs("div", { className: "flex items-center gap-3", children: [organization.logoUrl ? (
807
+ // eslint-disable-next-line @next/next/no-img-element
808
+ jsx("img", { src: organization.logoUrl, alt: organization.name ?? "โลโก้บริษัท", className: "h-8 w-8 flex-shrink-0 rounded object-contain bg-white",
809
+ // A dead logo URL must not leave a broken-image glyph in the
810
+ // sidebar; drop back to the generic icon's empty slot.
811
+ onError: e => {
812
+ e.currentTarget.style.display = "none";
813
+ } })) : (jsx(Building2, { className: "h-5 w-5 text-sidebar-foreground/70 flex-shrink-0" })), jsxs("div", { className: "flex-1 min-w-0", children: [jsx("div", { className: "font-medium text-sm sm:truncate", children: organization.name }), organization.description && (jsx("div", { className: "text-xs text-sidebar-foreground/70 truncate mt-0.5", children: organization.description }))] })] }) })), jsxs("div", { className: "p-2", children: [jsxs("div", { className: "relative", children: [jsxs("button", { onClick: () => {
804
814
  setShowThemeMenu(!showThemeMenu);
805
815
  setShowLanguageMenu(false);
806
816
  }, 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(Palette, { className: "h-4 w-4" }), jsx("span", { className: "flex-1 text-left", children: "\u0E40\u0E1B\u0E25\u0E35\u0E48\u0E22\u0E19\u0E18\u0E35\u0E21" }), jsx(ChevronUp, { className: `h-4 w-4 transition-transform ${showThemeMenu ? "rotate-180" : ""}` })] }), showThemeMenu && (jsx("div", { className: "mt-1 ml-4 space-y-1", children: [
@@ -1332,8 +1342,19 @@ const getQueryClientConfig = () => ({
1332
1342
  refetchOnWindowFocus: true,
1333
1343
  },
1334
1344
  mutations: {
1335
- retry: 1,
1336
- retryDelay: attemptIndex => Math.min(1000 * 2 ** attemptIndex, 30000),
1345
+ // NEVER auto-retry a mutation. Unlike a query, a mutation is a side
1346
+ // effect on a non-idempotent endpoint (submit, create, approve), and
1347
+ // React Query cannot tell "the network dropped" from "the server
1348
+ // rejected this on business grounds".
1349
+ //
1350
+ // With retry: 1 every failed mutation fired twice. Two visible
1351
+ // consequences: a rejected submit showed up as two failed POSTs in the
1352
+ // network tab, and — far worse — a create whose response was lost in
1353
+ // flight would be re-sent and duplicate the record server-side.
1354
+ //
1355
+ // Retrying is the caller's decision, not a global default: the user
1356
+ // sees the error and presses the button again.
1357
+ retry: 0,
1337
1358
  },
1338
1359
  },
1339
1360
  queryCache: new QueryCache({