diff --git a/apps/edr-freight-web/portal/src/App.tsx b/apps/edr-freight-web/portal/src/App.tsx index fc2fca1e4..f9746e99b 100644 --- a/apps/edr-freight-web/portal/src/App.tsx +++ b/apps/edr-freight-web/portal/src/App.tsx @@ -28,7 +28,12 @@ import TrackingPage from "./pages/tracking/TrackingPage"; import BillingPage from "./pages/billing/BillingPage"; import TrainsPage from "./pages/trains/TrainsPage"; import DashboardPage from "./pages/dashboard/DashboardPage"; -import { IamLoginPage, LoadingScreen, useAuth } from "@tria-plc/iamui-common"; +import { + IamLoginPage, + LoadingScreen, + useAuth, + useAuthUser, +} from "@tria-plc/iamui-common"; import CustomersPage from "./pages/customers/CustomersPage"; import CustomerDetailPage from "./pages/customers/CustomerDetailPage"; import NewCustomerPage from "./pages/customers/NewCustomerPage"; @@ -53,6 +58,7 @@ const App = () => { const navigate = useNavigate(); const location = useLocation(); const { user, loading } = useAuth(); + const { logout } = useAuthUser(); if (loading) { return ; @@ -67,9 +73,19 @@ const App = () => { ); } - if (location.pathname === "/auth") { - return ; - } + const displayName = user?.name?.en || user?.username || user?.email || "User"; + const userEmail = user?.email; + + const handleLogout = () => { + // Best-effort server-side session invalidation; local cleanup always runs + // so a failed API call (e.g. expired token) never leaves the user stuck. + logout(); + ["auth-token", "refresh-token"].forEach((name) => { + document.cookie = `${name}=; Max-Age=0; path=/`; + }); + localStorage.clear(); + window.location.replace("/auth"); + }; return ( { activeHref={location.pathname} onNavigate={navigate} enableThemeToggle + userName={displayName} + userEmail={userEmail} + onLogout={handleLogout} > } /> diff --git a/apps/edr-freight-web/portal/src/main.tsx b/apps/edr-freight-web/portal/src/main.tsx index 665a9fc81..b5c8c3a6f 100644 --- a/apps/edr-freight-web/portal/src/main.tsx +++ b/apps/edr-freight-web/portal/src/main.tsx @@ -13,6 +13,19 @@ import { axiosInstance, } from "@tria-plc/iamui-common"; +// Purge cookies that were stored as the literal string "undefined" before the +// envelope interceptor fix. Without this, stale sessions would keep sending +// "Authorization: Bearer undefined" and get 401 JsonWebTokenError forever. +["auth-token", "refresh-token"].forEach((name) => { + const entry = document.cookie + .split("; ") + .find((c) => c.startsWith(`${name}=`)); + const value = entry?.split("=").slice(1).join("="); + if (value === "undefined" || value === "null" || value === "") { + document.cookie = `${name}=; Max-Age=0; path=/`; + } +}); + const queryClient = new QueryClient(); window.__IAM_CONFIG__ = { apiUrl: `${import.meta.env.VITE_BASE_API_URL}/api`, diff --git a/packages/ui-common/src/components/Layout/DashboardLayout.tsx b/packages/ui-common/src/components/Layout/DashboardLayout.tsx index 184448b41..eb4bdb1bd 100644 --- a/packages/ui-common/src/components/Layout/DashboardLayout.tsx +++ b/packages/ui-common/src/components/Layout/DashboardLayout.tsx @@ -17,6 +17,10 @@ export interface DashboardLayoutProps { onNavigate?: (href: string) => void; headerRight?: ReactNode; enableThemeToggle?: boolean; + userName?: string; + userEmail?: string; + userInitials?: string; + onLogout?: () => void; children: ReactNode; } @@ -42,8 +46,20 @@ const DashboardLayout = ({ onNavigate, headerRight, enableThemeToggle = false, + userName = "User", + userEmail, + userInitials, + onLogout, children, }: DashboardLayoutProps) => { + const initials = + userInitials ?? + userName + .split(" ") + .filter(Boolean) + .slice(0, 2) + .map((n) => n[0].toUpperCase()) + .join(""); const [theme, setTheme] = useState(() => enableThemeToggle ? getInitialTheme() : "light", ); @@ -148,10 +164,10 @@ const DashboardLayout = ({ className="flex items-center gap-2 rounded-xl border border-transparent px-2 py-1.5 transition hover:border-[#33578D]/20 hover:bg-[#33578D]/5 aria-expanded:border-[#33578D]/30 aria-expanded:bg-[#33578D]/10 dark:hover:border-[#33578D]/30 dark:hover:bg-[#33578D]/10 dark:aria-expanded:border-[#33578D]/40 dark:aria-expanded:bg-[#33578D]/20" >
- JD + {initials}
- John Doe + {userName}

- John Doe -

-

- john.doe@edr.com + {userName}

+ {userEmail ? ( +

+ {userEmail} +

+ ) : null}
Profile - setIsUserMenuOpen(false)} - className="flex items-center gap-2 px-4 py-2 text-sm text-red-600 transition hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-950/30" + onClick={() => { + setIsUserMenuOpen(false); + onLogout?.(); + }} + className="flex w-full items-center gap-2 px-4 py-2 text-sm text-red-600 transition hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-950/30" > Logout - + ) : null}