diff --git a/apps/edr-freight-web/portal/src/App.tsx b/apps/edr-freight-web/portal/src/App.tsx index c7f532154..ddccd2f4c 100644 --- a/apps/edr-freight-web/portal/src/App.tsx +++ b/apps/edr-freight-web/portal/src/App.tsx @@ -8,8 +8,9 @@ import { Settings, User, } from "lucide-react"; -import { useEffect } from "react"; +import { useEffect, useRef } from "react"; import { + Navigate, Outlet, Route, Routes, @@ -18,17 +19,6 @@ import { } from "react-router-dom"; import useAuth from "./hooks/useAuth"; - -function LogoutHandler() { - const { logout } = useAuth(); - const navigate = useNavigate(); - - useEffect(() => { - logout().then(() => navigate("/login", { replace: true })); - }, []); - - return null; -} import EDRFreightLandingPage from "./pages/EDRFreightLandingPage"; import MyPortalPage from "./pages/MyPortalPage"; import ProfilePage from "./pages/ProfilePage"; @@ -47,8 +37,82 @@ import NewBookingPage from "./pages/bookings/NewBookingPage"; import CheckPaymentPage from "./pages/payments/CheckPaymentPage"; import TrackingPage from "./pages/tracking/TrackingPage"; +function FullScreenSpinner() { + return ( +
+ +
+ ); +} + +function LogoutHandler() { + const { logout } = useAuth(); + const navigate = useNavigate(); + const hasRun = useRef(false); + + useEffect(() => { + if (hasRun.current) return; + hasRun.current = true; + logout().then(() => navigate("/login", { replace: true })); + }, []); + + return ; +} + +/** Blocks unauthenticated users; renders children only with a valid session. */ +function RequireAuth() { + const { isPending, isAuthenticated } = useAuth(); + const location = useLocation(); + + if (isPending) return ; + if (!isAuthenticated) + return ; + return ; +} + +/** + * Sends authenticated users without a company to onboarding. + * Only redirects on a confirmed "no company" response — never on a + * transient query error. + */ +function RequireCompany() { + const { customerQuery } = useAuth(); + + if (customerQuery.isPending) return ; + if (customerQuery.isSuccess && !customerQuery.data) + return ; + return ; +} + +/** Keeps already-onboarded users out of the onboarding flow. */ +function RequireNoCompany() { + const { customerQuery } = useAuth(); + + if (customerQuery.isPending) return ; + if (customerQuery.data) return ; + return ; +} + +/** Keeps authenticated users off the login/signup pages. */ +function RedirectIfAuthed() { + const { isPending, isAuthenticated } = useAuth(); + + if (isPending) return ; + if (isAuthenticated) return ; + return ; +} + +/** Landing page for visitors; authenticated users go straight to the portal. */ +function LandingRoute() { + const { isPending, isAuthenticated } = useAuth(); + + if (isPending) return ; + if (isAuthenticated) return ; + return ; +} + const sidebarItems: SidebarItem[] = [ - { label: "Home", href: "/portal", icon: }, + { label: "Home", href: "/portal", icon: }, { label: "My Bookings", href: "/bookings", @@ -81,79 +145,70 @@ const sidebarItems: SidebarItem[] = [ const App = () => { const navigate = useNavigate(); const location = useLocation(); - const { user, isPending, customer, customerQuery } = useAuth(); - - useEffect(() => { - if (isPending || customerQuery.isPending) return; - const isInProtectedRoutes = sidebarItems.find((item) => - location.pathname.startsWith(item.href), - ); - console.log({ isInProtectedRoutes, location }); - if (!user) { - if (isInProtectedRoutes) return navigate("/login"); - return; - } - - if (user && location.pathname === "/") navigate("/portal"); - else if (!customer && !!isInProtectedRoutes) navigate("/onboarding"); - }, [user, location, customer, customerQuery.isPending]); - - if (isPending) { - return ( -
- -
- ); - } + const { user } = useAuth(); const displayName = user?.name?.en || user?.username || user?.email || "User"; const userEmail = user?.email; return ( - - } /> - } /> + {/* Public routes */} + } /> + } /> + } + /> + + {/* Auth pages — inaccessible once logged in */} + }> } /> } /> - } /> - } /> - } /> - } - /> - } /> + } /> + + }> + }> + } /> + + + }> + + + + } > - - - } - > - } /> - } /> - } /> - } /> - } /> - } - /> - } /> - } /> - } /> - } /> + } /> + } /> + } /> + } /> + } /> + } + /> + } /> + } /> + } /> + } /> + + - {/* } /> */} + + } /> ); }; diff --git a/apps/edr-freight-web/portal/src/hooks/useAuth.ts b/apps/edr-freight-web/portal/src/hooks/useAuth.ts index 67e4385f3..637ff04fd 100644 --- a/apps/edr-freight-web/portal/src/hooks/useAuth.ts +++ b/apps/edr-freight-web/portal/src/hooks/useAuth.ts @@ -47,21 +47,14 @@ const useAuth = () => { ); useEffect(() => { - console.log({ - user: authQuery.data, - company: companyQuery.data, - isCompany: !!companyQuery.data, - isUserPending: authQuery.isPending, - isCompanyPending: companyQuery.isPending, - }); - }, [ - authQuery.data, - companyQuery.data, - authQuery.isPending, - companyQuery.isPending, - ]); + if (authQuery.isError) { + queryClient.clear(); + localStorage.clear(); + } + }, [authQuery.isError, queryClient]); const isPending = authQuery.isPending && hasToken; + const isAuthenticated = hasToken && !!authQuery.data && !authQuery.isError; const login = async ( payload: LoginPayload, @@ -183,9 +176,10 @@ const useAuth = () => { return { isPending, - user: authQuery.data ?? null, - company: companyQuery.data ?? null, - customer: companyQuery.data ?? null, + isAuthenticated, + user: isAuthenticated ? (authQuery.data ?? null) : null, + company: isAuthenticated ? (companyQuery.data ?? null) : null, + customer: isAuthenticated ? (companyQuery.data ?? null) : null, login, signup, setPassword,