diff --git a/apps/edr-freight-web/backoffice/src/pages/auth/LoginPage.tsx b/apps/edr-freight-web/backoffice/src/pages/auth/LoginPage.tsx index e689ef9f8..89e8557c3 100644 --- a/apps/edr-freight-web/backoffice/src/pages/auth/LoginPage.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/auth/LoginPage.tsx @@ -1,11 +1,7 @@ import { type FormEvent, useState } from "react"; -import { parsePhoneNumberFromString } from "libphonenumber-js"; import { Eye, EyeOff, - Mail, - Smartphone, - UserRound, ArrowUpRight, Globe, ChevronDown, @@ -14,65 +10,15 @@ import { useNavigate } from "react-router-dom"; import { useAuth } from "@/auth/useAuth"; -type LoginMode = "email" | "phone" | "username"; - -const loginModes: Array<{ - value: LoginMode; - label: string; - icon: typeof Mail; - placeholder: string; -}> = [ - { - value: "email", - label: "Email", - icon: Mail, - placeholder: "name@company.com", - }, - { - value: "phone", - label: "Phone", - icon: Smartphone, - placeholder: "09XXXXXXXX", - }, - { - value: "username", - label: "Username", - icon: UserRound, - placeholder: "username", - }, - ]; - -const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; -const usernamePattern = /^[a-zA-Z0-9._-]{3,32}$/; - -const normalizeIdentifier = (mode: LoginMode, value: string) => { - const trimmed = value.trim(); - - if (mode === "email") { - if (!emailPattern.test(trimmed.toLowerCase())) { - throw new Error("Enter a valid email address."); - } - - return trimmed.toLowerCase(); +/** Normalise Ethiopian local phone (09…/07…) to E.164; pass email through unchanged. */ +const normaliseIdentifier = (raw: string): string => { + const v = raw.trim(); + const digits = v.replace(/\D/g, ""); + if (digits.length >= 9 && (v.startsWith("0") || v.startsWith("+251"))) { + const local = digits.startsWith("251") ? digits.slice(3) : digits.replace(/^0/, ""); + return `+251${local}`; } - - if (mode === "phone") { - const parsed = parsePhoneNumberFromString(trimmed, "ET"); - - if (!parsed?.isValid()) { - throw new Error("Enter a valid Ethiopian phone number."); - } - - return parsed.number; - } - - if (!usernamePattern.test(trimmed)) { - throw new Error( - "Username must be 3-32 characters and use letters, numbers, ., _, or -.", - ); - } - - return trimmed; + return v.toLowerCase(); }; const LOGIN_IMAGE = "/assets/login.png"; @@ -214,7 +160,6 @@ const FormFooter = () => ( const LoginPage = () => { const navigate = useNavigate(); const { login, verifyMfa } = useAuth(); - const [mode, setMode] = useState("email"); const [identifier, setIdentifier] = useState(""); const [password, setPassword] = useState(""); const [otp, setOtp] = useState(""); @@ -224,15 +169,13 @@ const LoginPage = () => { const [normalizedIdentifier, setNormalizedIdentifier] = useState(""); const [error, setError] = useState(null); - const currentMode = loginModes.find((item) => item.value === mode)!; - const handleSubmit = async (event: FormEvent) => { event.preventDefault(); setSubmitting(true); setError(null); try { - const normalized = normalizeIdentifier(mode, identifier); + const normalized = normaliseIdentifier(identifier); setNormalizedIdentifier(normalized); const result = await login({ email: normalized, password }); @@ -284,32 +227,14 @@ const LoginPage = () => {
-
- - -
-
- -
- setIdentifier(event.target.value)} - placeholder={currentMode.placeholder} + placeholder="name@company.com or 09XXXXXXXX" + autoComplete="username" className={fieldClass} />
diff --git a/apps/edr-freight-web/portal/src/components/onboarding/ETradeInfo.tsx b/apps/edr-freight-web/portal/src/components/onboarding/ETradeInfo.tsx index 41dc33886..98efc2613 100644 --- a/apps/edr-freight-web/portal/src/components/onboarding/ETradeInfo.tsx +++ b/apps/edr-freight-web/portal/src/components/onboarding/ETradeInfo.tsx @@ -50,8 +50,8 @@ export default function ETradeInfo({ TIN Number (10 digits) *} + placeholder="0012345678" maxLength={10} error={error} {...register} diff --git a/apps/edr-freight-web/portal/src/pages/accounts/CompanyProfileForm.tsx b/apps/edr-freight-web/portal/src/pages/accounts/CompanyProfileForm.tsx index 69aabae53..6822124fc 100644 --- a/apps/edr-freight-web/portal/src/pages/accounts/CompanyProfileForm.tsx +++ b/apps/edr-freight-web/portal/src/pages/accounts/CompanyProfileForm.tsx @@ -450,14 +450,15 @@ export default function CompanyProfileForm({ }); // The business owner/manager pulled from eTrade — powers "Use owner as - // manager" on the General Manager step. Null until a TIN lookup succeeds. + // manager" on the General Manager step. const [etradeOwner, setEtradeOwner] = useState<{ name: string; phone: string; + email?: string; } | null>(null); - // Mirror the two "copy from previous person" checkboxes so they can be - // re-toggled (re-checking re-pulls the latest values). + // Mirror the three "copy from previous person" checkboxes. + const [ownerIsGm, setOwnerIsGm] = useState(false); const [gmIsContact, setGmIsContact] = useState(false); const [contactIsPoa, setContactIsPoa] = useState(false); @@ -507,15 +508,20 @@ export default function CompanyProfileForm({ phone: toEthiopianE164( data.managerPhone || data.regularPhone || data.mobilePhone, ), + email: data.managerEmail || undefined, }); }; /** Fill the General Manager from the eTrade business owner. */ - const useOwnerAsManager = () => { - if (!etradeOwner) return; + const toggleOwnerAsGm = (checked: boolean) => { + setOwnerIsGm(checked); + if (!checked || !etradeOwner) return; const { first, last } = splitName(etradeOwner.name); setValue("generalManagerFirstName", first, { shouldValidate: true }); setValue("generalManagerLastName", last, { shouldValidate: true }); + setValue("generalManagerEmail", etradeOwner.email ?? "", { + shouldValidate: true, + }); setValue("generalManagerPhone", etradeOwner.phone ?? "", { shouldValidate: true, }); @@ -702,13 +708,13 @@ export default function CompanyProfileForm({ First Name *} placeholder="Global" error={errors.companyFirstName?.message} {...register("companyFirstName")} /> Last Name *} placeholder="Logistics Ltd" error={errors.companyLastName?.message} {...register("companyLastName")} @@ -716,7 +722,7 @@ export default function CompanyProfileForm({ Company Email *} type="email" placeholder="ops@company.com" error={errors.companyEmail?.message} @@ -730,21 +736,21 @@ export default function CompanyProfileForm({ /> Location *} placeholder="Addis Ababa, Ethiopia" error={errors.companyLocation?.message} {...register("companyLocation")} /> VAT Number *} placeholder="VAT-12345" maxLength={10} error={errors.vatNumber?.message} {...register("vatNumber")} /> FAN Number (16 digits) *} placeholder="1234567890123456" maxLength={16} error={errors.fanNumber?.message} @@ -757,16 +763,21 @@ export default function CompanyProfileForm({ Registration Details + + Auto-filled from eTrade — these fields cannot be edited. + @@ -774,13 +785,15 @@ export default function CompanyProfileForm({ @@ -788,13 +801,15 @@ export default function CompanyProfileForm({ @@ -806,13 +821,15 @@ export default function CompanyProfileForm({ @@ -820,13 +837,15 @@ export default function CompanyProfileForm({ @@ -834,7 +853,8 @@ export default function CompanyProfileForm({ @@ -850,31 +870,25 @@ export default function CompanyProfileForm({ {step === "personnel" && ( <> - - - General Manager - - {etradeOwner && ( - - )} - + + General Manager + + toggleOwnerAsGm(e.currentTarget.checked)} + /> First Name *} placeholder="Abebe" error={errors.generalManagerFirstName?.message} {...register("generalManagerFirstName")} /> Last Name *} placeholder="Bikila" error={errors.generalManagerLastName?.message} {...register("generalManagerLastName")} @@ -882,7 +896,7 @@ export default function CompanyProfileForm({ Email *} type="email" placeholder="gm@company.com" error={errors.generalManagerEmail?.message} @@ -911,13 +925,13 @@ export default function CompanyProfileForm({ /> First Name *} placeholder="Jane" error={errors.contactPersonFirstName?.message} {...register("contactPersonFirstName")} /> Last Name *} placeholder="Smith" error={errors.contactPersonLastName?.message} {...register("contactPersonLastName")} diff --git a/apps/edr-freight-web/portal/src/pages/accounts/LoginPage.tsx b/apps/edr-freight-web/portal/src/pages/accounts/LoginPage.tsx index ac13f4c59..bdd10871b 100644 --- a/apps/edr-freight-web/portal/src/pages/accounts/LoginPage.tsx +++ b/apps/edr-freight-web/portal/src/pages/accounts/LoginPage.tsx @@ -1,48 +1,39 @@ import { type FormEvent, useState } from "react"; -import { ChevronDown, Eye, EyeOff, Mail, Smartphone } from "lucide-react"; +import { Eye, EyeOff } from "lucide-react"; import { useLocation, useNavigate } from "react-router-dom"; -import RPNInput from "react-phone-number-input"; -import "react-phone-number-input/style.css"; import useAuth from "@/hooks/useAuth"; import AuthShell, { fieldClass, primaryButtonClass } from "@/components/auth/AuthShell"; -import "@/components/phone-field.css"; const EDR_LOGO = "/assets/edr-logo.png"; -type LoginMethod = "email" | "phone"; - -const loginMethods: Array<{ - value: LoginMethod; - label: string; - icon: typeof Mail; - placeholder: string; -}> = [ - { value: "email", label: "Email", icon: Mail, placeholder: "name@company.com" }, - { value: "phone", label: "Phone", icon: Smartphone, placeholder: "09XXXXXXXX" }, -]; +/** Normalise Ethiopian local phone (09…/07…) to E.164; pass email through unchanged. */ +function normaliseIdentifier(raw: string): string { + const v = raw.trim(); + const digits = v.replace(/\D/g, ""); + if (digits.length >= 9 && (v.startsWith("0") || v.startsWith("+251"))) { + const local = digits.startsWith("251") ? digits.slice(3) : digits.replace(/^0/, ""); + return `+251${local}`; + } + return v.toLowerCase(); +} export default function LoginPage() { const navigate = useNavigate(); const location = useLocation(); const { login } = useAuth(); - const [method, setMethod] = useState("email"); const [identifier, setIdentifier] = useState(""); const [password, setPassword] = useState(""); const [showPassword, setShowPassword] = useState(false); const [error, setError] = useState(null); const [loading, setLoading] = useState(false); - const currentMethod = loginMethods.find((item) => item.value === method)!; - const handleSubmit = async (event: FormEvent) => { event.preventDefault(); setError(null); setLoading(true); try { - // In phone mode the identifier is already a canonical E.164 string - // (e.g. +251912345678) from the phone field; email mode passes through. - const result = await login({ email: identifier, password }); + const result = await login({ email: normaliseIdentifier(identifier), password }); if (result.success) { const from = (location.state as { from?: { pathname: string } } | null)?.from ?.pathname; @@ -74,55 +65,19 @@ export default function LoginPage() {
-
- -
- - -
-
-
- {method === "phone" ? ( -
- setIdentifier(v ?? "")} - /> -
- ) : ( - setIdentifier(event.target.value)} - placeholder={currentMethod.placeholder} - disabled={loading} - className={fieldClass} - /> - )} + setIdentifier(event.target.value)} + placeholder="name@company.com or 09XXXXXXXX" + disabled={loading} + autoComplete="username" + className={fieldClass} + />