- {/* ------------------------------------------------------------------ */}
- {/* Left Side */}
- {/* ------------------------------------------------------------------ */}
-
-
-
-
-
- {/* Logo */}
-
-
-
-
-
-
-
- EDR Freight
-
-
-
- Railway Logistics
- Platform
-
-
-
-
- {/* Hero */}
-
-
- Account Security
-
-
-
- Set your secure
- password
-
-
-
- Create a strong
- password to secure
- your EDR Freight
- account and protect
- railway logistics
- operations and shipment
- data.
-
-
-
- {/* Features */}
-
- {[
- "Enterprise-grade security",
- "Protected account access",
- "Secure freight operations",
- "Advanced authentication system",
- ].map((item) => (
-
- ))}
-
-
-
- {/* Stats */}
-
-
-
-
- Security Protection
-
-
-
- 256-bit
-
-
-
-
- Encrypted
-
-
-
-
-
-
-
- {/* ------------------------------------------------------------------ */}
- {/* Right Side */}
- {/* ------------------------------------------------------------------ */}
-
-
-
- {/* Mobile Logo */}
-
-
-
-
-
-
-
- EDR Freight
-
-
-
- Railway Logistics
- Platform
-
-
-
-
- {/* Card */}
-
- {/* Header */}
-
-
-
-
-
-
- Set Password
-
-
-
- Create a secure
- password for your
- EDR Freight account.
-
-
-
- {/* Success */}
- {setPasswordMutation.isSuccess && (
-
- Password updated
- successfully.
-
- )}
-
- {/* Error */}
- {setPasswordMutation.isError && (
-
- Failed to set
- password. Please try
- again.
-
- )}
-
- {/* Form */}
-
-
-
+
+
+
+
+
Set Password
+
+ Create a secure password for your EDR Freight account.
+
-
+
+ {error && (
+
+ {error}
+
+ )}
+
+
+
);
-}
\ No newline at end of file
+}
diff --git a/apps/edr-freight-web/portal/src/pages/accounts/SignupPage.tsx b/apps/edr-freight-web/portal/src/pages/accounts/SignupPage.tsx
index 2b9b7f135..05affbbe8 100644
--- a/apps/edr-freight-web/portal/src/pages/accounts/SignupPage.tsx
+++ b/apps/edr-freight-web/portal/src/pages/accounts/SignupPage.tsx
@@ -1,534 +1,194 @@
-import { userType } from "@/enums/userType";
-import { createOTP, createUser } from "@/services/account";
-
-import { CreateUserPayload } from "@/types/createUser";
-
-import { zodResolver } from "@hookform/resolvers/zod";
-
-import { useMutation } from "@tanstack/react-query";
-
-import {
- ArrowRight,
- ShieldCheck,
- Train,
- UserPlus,
-} from "lucide-react";
-
-import { useForm } from "react-hook-form";
-
+import { useState } from "react";
import { useNavigate } from "react-router-dom";
-
+import { useForm } from "react-hook-form";
+import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
-
-// -----------------------------------------------------------------------------
-// Schema
-// -----------------------------------------------------------------------------
+import { ArrowRight, UserPlus } from "lucide-react";
+import { userType } from "@/enums/userType";
+import useAuth from "@/hooks/useAuth";
+import type { SignupPayload } from "@/types/auth";
+import AuthLayout from "@/components/auth/AuthLayout";
const userSchema = z.object({
- email: z
- .string()
- .email("Invalid email address"),
-
- username: z
- .string()
- .min(
- 3,
- "Username must be at least 3 characters"
- ),
-
- countryCode: z
- .string()
- .min(
- 1,
- "Country code is required"
- ),
-
- phone: z
- .string()
- .min(
- 9,
- "Phone number is too short"
- )
- .max(
- 9,
- "Phone number is too long"
- ),
-
+ email: z.string().email("Invalid email address"),
+ username: z.string().min(3, "Username must be at least 3 characters"),
+ countryCode: z.string().min(1, "Country code is required"),
+ phone: z.string().min(9, "Phone number is too short").max(9, "Phone number is too long"),
userType: z.string(),
-
name: z.object({
- en: z
- .string()
- .min(2, "Name is required"),
-
+ en: z.string().min(2, "Name is required"),
am: z.string().nullable(),
}),
});
-type FormData = z.infer<
- typeof userSchema
->;
-
-// -----------------------------------------------------------------------------
-// Component
-// -----------------------------------------------------------------------------
+type FormData = z.infer
;
export default function SignupPage() {
const navigate = useNavigate();
+ const { signup } = useAuth();
+ const [error, setError] = useState(null);
+ const [loading, setLoading] = useState(false);
const {
register,
handleSubmit,
formState: { errors },
- reset,
} = useForm({
- resolver:
- zodResolver(userSchema),
-
+ resolver: zodResolver(userSchema),
defaultValues: {
email: "",
username: "",
countryCode: "+251",
phone: "",
- userType:
- userType.individual,
-
- name: {
- en: "",
- am: "",
- },
+ userType: userType.individual,
+ name: { en: "", am: "" },
},
});
- // ---------------------------------------------------------------------------
- // Create User Mutation
- // ---------------------------------------------------------------------------
-
- const createUserMutation =
- useMutation({
- mutationFn: (
- user: CreateUserPayload
- ) => createUser(user),
-
- onSuccess: () => {
- reset();
- },
- });
-
- // ---------------------------------------------------------------------------
- // Submit
- // ---------------------------------------------------------------------------
-
- const onSubmit = async (
- data: FormData
- ) => {
+ const onSubmit = async (data: FormData) => {
+ setError(null);
+ setLoading(true);
try {
- const normalizedPhone =
- data.phone.startsWith(
- "0"
- )
- ? data.phone.slice(1)
- : data.phone;
-
- const fullPhoneNumber = `${data.countryCode
- }${normalizedPhone}`;
-
- const payload: CreateUserPayload =
- {
+ const normalizedPhone = data.phone.startsWith("0") ? data.phone.slice(1) : data.phone;
+ const payload: SignupPayload = {
email: data.email,
-
- username:
- data.username,
-
- phoneNumber:
- fullPhoneNumber,
-
- userType:
- data.userType,
-
- name: {
- en: data.name.en,
- am:
- data.name.am ||
- "",
- },
+ username: data.username,
+ phoneNumber: `${data.countryCode}${normalizedPhone}`,
+ userType: data.userType,
+ name: { en: data.name.en, am: data.name.am ?? "" },
};
-
- const res =
- await createUserMutation.mutateAsync(
- payload
- );
-
- if (res?.success) {
- // save auth token
- // document.cookie = `auth-token=${res.data?.token}; path=/`;
- localStorage.setItem(
- "auth-token",
- `auth-token=${res.data?.token}; path=/`
- );
- localStorage.setItem(
- "userId",res.data?.userId
- );
- localStorage.setItem(
- "otp",res.data?.otp?.split(" ")?.[6]
- );
- createOTP({ phone: payload.phoneNumber, otp:res.data?.otp?.split(" ")?.[6] })
- // save phone for otp page
- localStorage.setItem(
- "otp-phone",
- payload.phoneNumber
- );
- // save phone for set password page
-
- localStorage.setItem(
- "otp-email",
- payload.email
- );
- // navigate otp page
+ const result = await signup(payload);
+ if (result.success) {
navigate("/otp");
+ } else {
+ setError(result.error.message);
}
- } catch (err) {
- console.error(err);
+ } catch {
+ setError("An unexpected error occurred");
+ } finally {
+ setLoading(false);
}
};
- // ---------------------------------------------------------------------------
- // UI
- // ---------------------------------------------------------------------------
-
return (
-
-
- {/* ------------------------------------------------------------------ */}
- {/* Left Side */}
- {/* ------------------------------------------------------------------ */}
-
-
-
-
-
- {/* Logo */}
-
-
-
-
-
-
-
- EDR Freight
-
-
-
- Railway Logistics
- Platform
-
-
-
-
- {/* Hero */}
-
-
- Smart Freight
- Operations
-
-
-
- Create your freight
- operations account
-
-
-
- Join EDR Freight to
- manage shipments,
- monitor railway
- operations, track
- consignments, and
- streamline logistics
- workflows across
- Ethiopia and
- Djibouti.
-
-
-
- {/* Features */}
-
- {[
- "Real-time shipment tracking",
- "Secure logistics management",
- "Enterprise-grade operations",
- "Multi-corridor freight monitoring",
- ].map((item) => (
-
- ))}
-
-
-
- {/* Stats */}
-
-
-
-
- Active Corridors
-
-
-
- 24+
-
-
-
-
- Operational
-
-
-
-
-
-
-
- {/* ------------------------------------------------------------------ */}
- {/* Right Side */}
- {/* ------------------------------------------------------------------ */}
-
-
-
- {/* Mobile Logo */}
-
-
-
-
-
-
-
- EDR Freight
-
-
-
- Railway Logistics
- Platform
-
-
-
-
- {/* Form Card */}
-
- {/* Header */}
-
-
-
-
-
-
- Create Account
-
-
-
- Register to access
- EDR Freight
- services and railway
- logistics operations.
-
-
-
- {/* Success */}
- {createUserMutation.isSuccess && (
-
- Account created
- successfully.
-
- )}
-
- {/* Error */}
- {createUserMutation.isError && (
-
- Failed to create
- account. Please try
- again.
-
- )}
-
- {/* Form */}
-
-
-
+
+
+
+
+
Create Account
+
+ Register to access EDR Freight services and railway logistics operations.
+
-
+
+ {error && (
+
+ {error}
+
+ )}
+
+
+
);
-}
\ No newline at end of file
+}
diff --git a/apps/edr-freight-web/portal/src/pages/accounts/VerificationOtpPage.tsx b/apps/edr-freight-web/portal/src/pages/accounts/VerificationOtpPage.tsx
index 091406df3..183980e4f 100644
--- a/apps/edr-freight-web/portal/src/pages/accounts/VerificationOtpPage.tsx
+++ b/apps/edr-freight-web/portal/src/pages/accounts/VerificationOtpPage.tsx
@@ -1,70 +1,28 @@
-import { verificationCodeType } from "@/enums/verificationCodeType";
-
-import {
- generateVerificationCode,
- verifyOTP,
-} from "@/services/account";
-
-import { zodResolver } from "@hookform/resolvers/zod";
-
-import { useMutation } from "@tanstack/react-query";
-
+import { useState } from "react";
import { useNavigate } from "react-router-dom";
-
-import {
- ArrowRight,
- ShieldCheck,
- Train,
- MailCheck,
- RotateCw,
-} from "lucide-react";
-
import { useForm } from "react-hook-form";
-
+import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
-
-// -----------------------------------------------------------------------------
-// Schema
-// -----------------------------------------------------------------------------
+import { ArrowRight, MailCheck, RotateCw } from "lucide-react";
+import { verificationCodeType } from "@/enums/verificationCodeType";
+import useAuth from "@/hooks/useAuth";
+import AuthLayout from "@/components/auth/AuthLayout";
const otpSchema = z.object({
- code: z
- .string()
- .regex(
- /^\d{6}$/,
- "OTP must be exactly 6 digits"
- ),
+ code: z.string().regex(/^\d{6}$/, "OTP must be exactly 6 digits"),
});
-type FormData = z.infer<
- typeof otpSchema
->;
-
-// -----------------------------------------------------------------------------
-// Component
-// -----------------------------------------------------------------------------
+type FormData = z.infer
;
export default function VerificationOtpPage() {
- const navigate =
- useNavigate();
+ const navigate = useNavigate();
+ const { verifyOTP, generateVerificationCode } = useAuth();
+ const [verifying, setVerifying] = useState(false);
+ const [resending, setResending] = useState(false);
+ const [error, setError] = useState(null);
+ const [resentMessage, setResentMessage] = useState(null);
- // ---------------------------------------------------------------------------
- // Local Storage Data
- // ---------------------------------------------------------------------------
-
- const phone =
- localStorage.getItem(
- "otp-phone"
- ) || "";
-
- const email =
- localStorage.getItem(
- "otp-email"
- ) || "";
-
- // ---------------------------------------------------------------------------
- // Form
- // ---------------------------------------------------------------------------
+ const phone = localStorage.getItem("otp-phone") || "";
const {
register,
@@ -72,383 +30,152 @@ export default function VerificationOtpPage() {
formState: { errors },
watch,
} = useForm({
- resolver:
- zodResolver(otpSchema),
-
- defaultValues: {
- code: "",
- },
+ resolver: zodResolver(otpSchema),
+ defaultValues: { code: "" },
});
- const otpValue =
- watch("code");
+ const otpValue = watch("code");
- // ---------------------------------------------------------------------------
- // Verify Mutation
- // ---------------------------------------------------------------------------
-
- const verifyMutation =
- useMutation({
- mutationFn: async (
- data: {
- phone: string;
- otp: string;
- }
- ) => verifyOTP(data),
-
- onSuccess: () => {
- navigate(
- "/set-password"
- );
- },
- });
-
- // ---------------------------------------------------------------------------
- // Resend Mutation
- // ---------------------------------------------------------------------------
-
- const resendMutation =
- useMutation({
- mutationFn: async () => {
- return generateVerificationCode(
- {
- email,
- phoneNumber:
- phone,
-
- type:
- verificationCodeType.setPassword,
- }
- );
- },
- });
-
- // ---------------------------------------------------------------------------
- // Submit
- // ---------------------------------------------------------------------------
-
- const onSubmit = async (
- data: FormData
- ) => {
+ const onSubmit = async (data: FormData) => {
+ setError(null);
+ setVerifying(true);
try {
- await verifyMutation.mutateAsync(
- {
- phone,
- otp: data.code,
- }
- );
- } catch (err) {
- console.error(err);
+ const result = await verifyOTP(data.code);
+ if (result.success) {
+ navigate("/set-password");
+ } else {
+ setError(result.error.message);
+ }
+ } catch {
+ setError("An unexpected error occurred");
+ } finally {
+ setVerifying(false);
}
};
- // ---------------------------------------------------------------------------
- // Helpers
- // ---------------------------------------------------------------------------
+ const handleResend = async () => {
+ setResentMessage(null);
+ setResending(true);
+ try {
+ const result = await generateVerificationCode(verificationCodeType.setPassword);
+ if (result.success) {
+ setResentMessage("New OTP code sent successfully.");
+ } else {
+ setError(result.error.message);
+ }
+ } catch {
+ setError("An unexpected error occurred");
+ } finally {
+ setResending(false);
+ }
+ };
- const maskedPhone =
- phone.length > 4
- ? `${phone.slice(
- 0,
- 7
- )}******`
- : phone;
-
- // ---------------------------------------------------------------------------
- // UI
- // ---------------------------------------------------------------------------
+ const maskedPhone = phone.length > 4 ? `${phone.slice(0, 7)}******` : phone;
return (
-
-
- {/* ------------------------------------------------------------------ */}
- {/* Left Side */}
- {/* ------------------------------------------------------------------ */}
-
-
-
-
-
- {/* Logo */}
-
-
-
-
-
-
-
- EDR Freight
-
-
-
- Railway Logistics
- Platform
-
-
-
-
- {/* Hero */}
-
-
- Secure
- Verification
-
-
-
- Verify your
- account securely
-
-
-
- Enter the
- verification code
- sent to your phone
- number to continue
- using EDR Freight
- logistics services.
-
-
-
- {/* Features */}
-
- {[
- "Secure OTP verification",
- "Protected account access",
- "Fast identity confirmation",
- "Enterprise-grade security",
- ].map((item) => (
-
- ))}
-
-
-
- {/* Footer Stats */}
-
-
-
-
- Verification
- Security
-
-
-
- 99.9%
-
-
-
-
- Protected
-
-
-
-
-
+
+
+
+
-
- {/* ------------------------------------------------------------------ */}
- {/* Right Side */}
- {/* ------------------------------------------------------------------ */}
-
-
-
- {/* Mobile Logo */}
-
-
-
-
-
-
-
- EDR Freight
-
-
-
- Railway Logistics
- Platform
-
-
-
-
- {/* OTP Card */}
-
- {/* Header */}
-
-
-
-
-
-
- OTP Verification
-
-
-
- Enter the
- 6-digit code sent
- to:
-
-
-
-
-
- {/* Success */}
- {verifyMutation.isSuccess && (
-
- Verification
- successful.
-
- )}
-
- {/* Error */}
- {verifyMutation.isError && (
-
- Invalid OTP
- code. Please try
- again.
-
- )}
-
- {/* Resend Success */}
- {resendMutation.isSuccess && (
-
- New OTP code sent
- successfully.
-
- )}
-
- {/* Form */}
-
-
-
+
OTP Verification
+
Enter the 6-digit code sent to:
+
-
+
+ {error && (
+
+ {error}
+
+ )}
+
+ {resentMessage && (
+
+ {resentMessage}
+
+ )}
+
+
+
);
-}
\ No newline at end of file
+}