From 619ffa5419de62addd46a3f9c5b439a2c61787d1 Mon Sep 17 00:00:00 2001
From: Nathnael
Date: Sat, 4 Jul 2026 07:17:05 +0000
Subject: [PATCH] style(WIP): auth ui clean up.
---
.../src/components/auth/AuthShell.tsx | 139 +++++++
.../backoffice/src/pages/auth/LoginPage.tsx | 392 +++++-------------
.../portal/src/pages/accounts/LoginPage.tsx | 81 ++--
3 files changed, 274 insertions(+), 338 deletions(-)
create mode 100644 apps/edr-freight-web/backoffice/src/components/auth/AuthShell.tsx
diff --git a/apps/edr-freight-web/backoffice/src/components/auth/AuthShell.tsx b/apps/edr-freight-web/backoffice/src/components/auth/AuthShell.tsx
new file mode 100644
index 000000000..ecf06c7c9
--- /dev/null
+++ b/apps/edr-freight-web/backoffice/src/components/auth/AuthShell.tsx
@@ -0,0 +1,139 @@
+import type { ReactNode } from "react";
+import { ArrowUpRight, ChevronDown, Globe } from "lucide-react";
+
+const LOGIN_IMAGE = "/assets/login.png";
+const EDR_LOGO = "/assets/logo.svg";
+
+const LeftPanelDecor = () => (
+
+
+
+
+);
+
+const RightPanelDecor = () => (
+
+);
+
+export interface AuthShellProps {
+ children: ReactNode;
+ /** Tagline shown in the highlighted card over the left image panel. */
+ tagline?: string;
+ taglineBody?: string;
+}
+
+const LeftPanel = ({
+ tagline,
+ taglineBody,
+}: Pick) => (
+
+

+
+
+
+
+

+
+
+
+
+
+
+
+ {tagline ?? "Empower Your Freight Operations"}
+
+
+
+ {taglineBody ??
+ "Sign in to manage bookings, track cargo, and run logistics operations on the Ethio Djibouti Railway freight platform."}
+
+
+
+
+);
+
+const LanguageSelector = () => (
+
+
+ Eng
+
+
+);
+
+export default function AuthShell({
+ children,
+ tagline,
+ taglineBody,
+}: AuthShellProps) {
+ return (
+
+ );
+}
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 89e8557c3..849049bc2 100644
--- a/apps/edr-freight-web/backoffice/src/pages/auth/LoginPage.tsx
+++ b/apps/edr-freight-web/backoffice/src/pages/auth/LoginPage.tsx
@@ -1,162 +1,40 @@
import { type FormEvent, useState } from "react";
import {
- Eye,
- EyeOff,
- ArrowUpRight,
- Globe,
- ChevronDown,
-} from "lucide-react";
+ Alert,
+ Box,
+ Button,
+ Center,
+ Group,
+ Image,
+ PasswordInput,
+ PinInput,
+ Stack,
+ Text,
+ TextInput,
+ Title,
+} from "@mantine/core";
+import { AlertCircle, ArrowLeft } from "lucide-react";
import { useNavigate } from "react-router-dom";
import { useAuth } from "@/auth/useAuth";
+import AuthShell from "@/components/auth/AuthShell";
+import { extractApiError } from "@/utils/result";
/** 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/, "");
+ const local = digits.startsWith("251")
+ ? digits.slice(3)
+ : digits.replace(/^0/, "");
return `+251${local}`;
}
return v.toLowerCase();
};
-const LOGIN_IMAGE = "/assets/login.png";
const EDR_LOGO = "/assets/logo.svg";
-const fieldClass =
- "h-11 w-full rounded-xl border border-gray-200/90 bg-white px-4 text-sm text-gray-900 shadow-sm placeholder:text-gray-400 outline-none transition-all duration-200 hover:border-gray-300 focus:border-primary focus:bg-white focus:ring-4 focus:ring-primary/10";
-
-const primaryButtonClass =
- "h-11 w-full rounded-full bg-primary text-sm font-semibold text-primary-foreground shadow-[0_8px_20px_-6px_rgba(16,94,52,0.5)] transition-all duration-200 hover:bg-primary/90 hover:shadow-[0_10px_24px_-6px_rgba(16,94,52,0.55)] active:scale-[0.99] disabled:cursor-not-allowed disabled:opacity-60 disabled:shadow-none";
-
-const LeftPanelDecor = () => (
-
-
-
-
-);
-
-const RightPanelDecor = () => (
-
-);
-
-const LeftPanel = () => (
-
-

-
-
-
-
-
-
-
-
-
-
- Empower Your Freight Operations
-
-
-
- Sign in to manage bookings, track cargo, and run logistics operations
- on the Ethio Djibouti Railway freight platform.
-
-
-
-
-);
-
-const LanguageSelector = () => (
-
-
- Eng
-
-
-);
-
-const FormFooter = () => (
-
-);
-
const LoginPage = () => {
const navigate = useNavigate();
const { login, verifyMfa } = useAuth();
@@ -165,7 +43,6 @@ const LoginPage = () => {
const [otp, setOtp] = useState("");
const [needsMfa, setNeedsMfa] = useState(false);
const [submitting, setSubmitting] = useState(false);
- const [showPassword, setShowPassword] = useState(false);
const [normalizedIdentifier, setNormalizedIdentifier] = useState("");
const [error, setError] = useState(null);
@@ -179,15 +56,14 @@ const LoginPage = () => {
setNormalizedIdentifier(normalized);
const result = await login({ email: normalized, password });
- console.log(result);
if (result.mfaRequired) {
setNeedsMfa(true);
return;
}
// navigate("/dashboard/overview", { replace: true });
- } catch {
- setError("Unable to sign in with those credentials.");
+ } catch (err) {
+ setError(extractApiError(err).message);
} finally {
setSubmitting(false);
}
@@ -201,194 +77,132 @@ const LoginPage = () => {
try {
await verifyMfa({ email: normalizedIdentifier, otp: otp.trim() });
navigate("/dashboard/overview", { replace: true });
- } catch {
- setError("Unable to verify the one-time code.");
+ } catch (err) {
+ setError(extractApiError(err).message);
} finally {
setSubmitting(false);
}
};
const loginForm = (
-
+
+
+
);
const mfaForm = (
-
+ Verify
+
+
+
+
);
- return (
- <>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {!needsMfa ? loginForm : mfaForm}
-
-
-
-
-
-
-
-
- >
- );
+ return {!needsMfa ? loginForm : mfaForm};
};
export default LoginPage;
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 bdd10871b..323b5d5a8 100644
--- a/apps/edr-freight-web/portal/src/pages/accounts/LoginPage.tsx
+++ b/apps/edr-freight-web/portal/src/pages/accounts/LoginPage.tsx
@@ -1,9 +1,11 @@
import { type FormEvent, useState } from "react";
-import { Eye, EyeOff } from "lucide-react";
+import { Alert, Button, PasswordInput, Stack, TextInput } from "@mantine/core";
+import { AlertCircle } from "lucide-react";
import { useLocation, useNavigate } from "react-router-dom";
import useAuth from "@/hooks/useAuth";
-import AuthShell, { fieldClass, primaryButtonClass } from "@/components/auth/AuthShell";
+import AuthShell from "@/components/auth/AuthShell";
+import { extractApiError } from "@/utils/result";
const EDR_LOGO = "/assets/edr-logo.png";
@@ -24,7 +26,6 @@ export default function LoginPage() {
const { login } = useAuth();
const [identifier, setIdentifier] = useState("");
const [password, setPassword] = useState("");
- const [showPassword, setShowPassword] = useState(false);
const [error, setError] = useState(null);
const [loading, setLoading] = useState(false);
@@ -41,8 +42,8 @@ export default function LoginPage() {
} else {
setError(result.error.message);
}
- } catch {
- setError("An unexpected error occurred");
+ } catch (err) {
+ setError(extractApiError(err).message);
} finally {
setLoading(false);
}
@@ -64,60 +65,42 @@ export default function LoginPage() {
-
-
-
- setIdentifier(event.target.value)}
- placeholder="name@company.com or 09XXXXXXXX"
- disabled={loading}
- autoComplete="username"
- className={fieldClass}
- />
-
+
+ setIdentifier(event.target.value)}
+ />
-
-
-
+
+
-
- setPassword(event.target.value)}
- placeholder="Enter your password"
- disabled={loading}
- className={`${fieldClass} pr-11`}
- />
-
-
+
setPassword(event.target.value)}
+ />
{error ? (
-
+
) : null}
-
+
Don't have an account?{" "}
@@ -129,7 +112,7 @@ export default function LoginPage() {
Create an account
-
+
);