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,