mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-27 00:52:50 +00:00
feat(portal): Overhaul authentication flow and application routing
This commit is contained in:
@@ -4,13 +4,13 @@ import {
|
||||
Routes,
|
||||
Route,
|
||||
Navigate,
|
||||
Outlet,
|
||||
} from "react-router-dom";
|
||||
import { DashboardLayout, type SidebarItem } from "@edr/ui-common";
|
||||
import {
|
||||
CalendarCheck,
|
||||
MapPin,
|
||||
Receipt,
|
||||
FileText,
|
||||
Home,
|
||||
Loader2,
|
||||
User,
|
||||
@@ -36,7 +36,7 @@ import CustomerOnBoarding from "./pages/customers/on_boarding/TransportrOnBoardi
|
||||
import CustomerOnboardingPage from "./pages/customers/on_boarding/CustomerOnboardingPage";
|
||||
|
||||
const sidebarItems: SidebarItem[] = [
|
||||
{ label: "Home", href: "/", icon: <Home /> },
|
||||
{ label: "Home", href: "/portal", icon: <Home /> },
|
||||
{ label: "My Bookings", href: "/bookings", icon: <CalendarCheck /> },
|
||||
{ label: "Tracking", href: "/tracking", icon: <MapPin /> },
|
||||
{ label: "Billing", href: "/billing", icon: <Receipt /> },
|
||||
@@ -46,13 +46,20 @@ const sidebarItems: SidebarItem[] = [
|
||||
const App = () => {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const { user, isPending, logout, customer, customerQuery } = useAuth();
|
||||
|
||||
console.log({ customer, isPending, user });
|
||||
const { user, isPending, logout, customer } = useAuth();
|
||||
useEffect(() => {
|
||||
if (!user) return;
|
||||
// if (!user.hasSetPassword) navigate("/set-password");
|
||||
}, [user]);
|
||||
if (isPending) return;
|
||||
const isInProtectedRoutes = sidebarItems.find((item) =>
|
||||
location.pathname.startsWith(item.href),
|
||||
);
|
||||
if (!user) {
|
||||
if (isInProtectedRoutes) return navigate("/login");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!customer && !!isInProtectedRoutes) navigate("/onboarding");
|
||||
else if (customer && !isInProtectedRoutes) return navigate("/portal");
|
||||
}, [user, location, customer]);
|
||||
|
||||
if (isPending) {
|
||||
return (
|
||||
@@ -62,50 +69,45 @@ const App = () => {
|
||||
);
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
return (
|
||||
<Routes>
|
||||
<Route path="/" element={<EDRFreightLandingPage />} />
|
||||
<Route path="/login" element={<LoginPage />} />
|
||||
<Route path="/signup" element={<SignupPage />} />
|
||||
<Route path="/otp" element={<VerificationOtpPage />} />
|
||||
<Route path="/set-password" element={<SetPasswordPage />} />
|
||||
<Route path="*" element={<Navigate to="/" replace />} />
|
||||
</Routes>
|
||||
);
|
||||
}
|
||||
|
||||
if (user && !customer && !customerQuery.isPending) {
|
||||
return <OnboardingPage />;
|
||||
}
|
||||
|
||||
return <CustomerOnboardingPage />
|
||||
|
||||
const displayName = user?.name?.en || user?.username || user?.email || "User";
|
||||
const userEmail = user?.email;
|
||||
|
||||
return (
|
||||
<DashboardLayout
|
||||
title="EDR Freight"
|
||||
sidebarItems={sidebarItems}
|
||||
activeHref={location.pathname}
|
||||
onNavigate={navigate}
|
||||
enableThemeToggle
|
||||
userName={displayName}
|
||||
userEmail={userEmail}
|
||||
onLogout={logout}
|
||||
>
|
||||
<Routes>
|
||||
<Route path="/" element={<MyPortalPage />} />
|
||||
<Routes>
|
||||
<Route>
|
||||
<Route index element={<EDRFreightLandingPage />} />
|
||||
<Route path="/login" element={<LoginPage />} />
|
||||
<Route path="/signup" element={<SignupPage />} />
|
||||
<Route path="/otp" element={<VerificationOtpPage />} />
|
||||
<Route path="/set-password" element={<SetPasswordPage />} />
|
||||
<Route path="/onboarding" element={<OnboardingPage />} />
|
||||
</Route>
|
||||
<Route
|
||||
element={
|
||||
<DashboardLayout
|
||||
title="EDR Freight"
|
||||
sidebarItems={sidebarItems}
|
||||
activeHref={location.pathname}
|
||||
onNavigate={navigate}
|
||||
enableThemeToggle
|
||||
userName={displayName}
|
||||
userEmail={userEmail}
|
||||
onLogout={logout}
|
||||
>
|
||||
<Outlet />
|
||||
</DashboardLayout>
|
||||
}
|
||||
>
|
||||
<Route path="/portal" element={<MyPortalPage />} />
|
||||
<Route path="/bookings" element={<MyBookings />} />
|
||||
<Route path="/bookings/new" element={<NewBookingPage />} />
|
||||
<Route path="/bookings/:id" element={<BookingDetailPage />} />
|
||||
<Route path="/tracking" element={<TrackingPage />} />
|
||||
<Route path="/billing" element={<BillingPage />} />
|
||||
<Route path="/profile" element={<ProfilePage />} />
|
||||
<Route path="*" element={<Navigate to="/" replace />} />
|
||||
</Routes>
|
||||
</DashboardLayout>
|
||||
</Route>
|
||||
<Route path="*" element={<Navigate to="/" replace />} />
|
||||
</Routes>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import type {
|
||||
} from "@/types/auth";
|
||||
import type { Result } from "@/utils/result";
|
||||
import { extractApiError } from "@/utils/result";
|
||||
import { useEffect } from "react";
|
||||
|
||||
function setCookie(name: string, value: string, days: number) {
|
||||
const expires = new Date();
|
||||
@@ -44,6 +45,16 @@ const useAuth = () => {
|
||||
}),
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
console.log({
|
||||
user: authQuery.data,
|
||||
customer: customerQuery.data,
|
||||
isCustomer: !!customerQuery.data,
|
||||
isUserPending: authQuery.isPending,
|
||||
isCustomerPending: customerQuery.isPending,
|
||||
});
|
||||
}, [authQuery, customerQuery]);
|
||||
|
||||
const hasToken = !!getCookie("auth-token");
|
||||
const isPending = authQuery.isPending && hasToken;
|
||||
|
||||
|
||||
@@ -26,7 +26,11 @@ const userSchema = z.object({
|
||||
.min(9, "Phone number is too short")
|
||||
.max(9, "Phone number is too long"),
|
||||
userType: z.string(),
|
||||
name: z.object({
|
||||
firstName: z.object({
|
||||
en: z.string().min(2, "Name is required"),
|
||||
am: z.string().nullable(),
|
||||
}),
|
||||
lastName: z.object({
|
||||
en: z.string().min(2, "Name is required"),
|
||||
am: z.string().nullable(),
|
||||
}),
|
||||
@@ -51,7 +55,8 @@ export default function SignupPage() {
|
||||
countryCode: "+251",
|
||||
phone: "",
|
||||
userType: userType.individual,
|
||||
name: { en: "", am: "" },
|
||||
firstName: { en: "", am: "" },
|
||||
lastName: { en: "", am: "" },
|
||||
},
|
||||
});
|
||||
|
||||
@@ -67,11 +72,12 @@ export default function SignupPage() {
|
||||
username: data.email,
|
||||
phoneNumber: `${data.countryCode}${normalizedPhone}`,
|
||||
userType: data.userType,
|
||||
name: { en: data.name.en, am: data.name.am ?? "" },
|
||||
name: { en: `${data.firstName.en} ${data.lastName.en}`, am: "" },
|
||||
};
|
||||
const result = await signup(payload);
|
||||
if (result.success) {
|
||||
navigate("/otp");
|
||||
navigate("/portal");
|
||||
// navigate("/otp");
|
||||
} else {
|
||||
setError(result.error.message);
|
||||
}
|
||||
@@ -121,18 +127,31 @@ export default function SignupPage() {
|
||||
|
||||
<form onSubmit={handleSubmit(onSubmit)} className="flex flex-col gap-4">
|
||||
<FieldGroup className="gap-4">
|
||||
<Field data-invalid={Boolean(errors.name?.en)}>
|
||||
<FieldLabel>Full Name</FieldLabel>
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="John Doe"
|
||||
disabled={loading}
|
||||
aria-invalid={Boolean(errors.name?.en)}
|
||||
{...register("name.en")}
|
||||
/>
|
||||
<FieldError errors={[errors.name?.en]} />
|
||||
</Field>
|
||||
<div className="flex gap-4">
|
||||
<Field data-invalid={Boolean(errors.firstName?.en)}>
|
||||
<FieldLabel>First Name</FieldLabel>
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="John"
|
||||
disabled={loading}
|
||||
aria-invalid={Boolean(errors.firstName?.en)}
|
||||
{...register("firstName.en")}
|
||||
/>
|
||||
<FieldError errors={[errors.firstName?.en]} />
|
||||
</Field>
|
||||
|
||||
<Field data-invalid={Boolean(errors.lastName?.en)}>
|
||||
<FieldLabel>Last Name</FieldLabel>
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="Doe"
|
||||
disabled={loading}
|
||||
aria-invalid={Boolean(errors.lastName?.en)}
|
||||
{...register("lastName.en")}
|
||||
/>
|
||||
<FieldError errors={[errors.lastName?.en]} />
|
||||
</Field>
|
||||
</div>
|
||||
<Field data-invalid={Boolean(errors.email)}>
|
||||
<FieldLabel>Email Address</FieldLabel>
|
||||
<Input
|
||||
|
||||
Reference in New Issue
Block a user