mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
style: some clean up
This commit is contained in:
@@ -87,7 +87,12 @@ const FreightDashboardHeader = ({
|
||||
<Text
|
||||
fw={700}
|
||||
truncate
|
||||
style={{ fontSize: "20px", lineHeight: 1.2, color: "#0f172a", letterSpacing: "-0.4px" }}
|
||||
style={{
|
||||
fontSize: "20px",
|
||||
lineHeight: 1.2,
|
||||
color: "#0f172a",
|
||||
letterSpacing: "-0.4px",
|
||||
}}
|
||||
>
|
||||
{pageMeta.title}
|
||||
</Text>
|
||||
@@ -120,13 +125,6 @@ const FreightDashboardHeader = ({
|
||||
</button>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip label="Messages" withArrow openDelay={300}>
|
||||
<button type="button" className="fdh-icon-btn" aria-label="Messages">
|
||||
<MessageSquare size={18} />
|
||||
<span className="fdh-badge">3</span>
|
||||
</button>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip label="Notifications" withArrow openDelay={300}>
|
||||
<button
|
||||
type="button"
|
||||
@@ -189,7 +187,12 @@ const FreightDashboardHeader = ({
|
||||
<div className="fdh-avatar">{initials}</div>
|
||||
</div>
|
||||
<Stack gap={0} style={{ minWidth: 0 }}>
|
||||
<Text size="sm" fw={600} truncate style={{ color: "#0f172a" }}>
|
||||
<Text
|
||||
size="sm"
|
||||
fw={600}
|
||||
truncate
|
||||
style={{ color: "#0f172a" }}
|
||||
>
|
||||
{userName}
|
||||
</Text>
|
||||
{userEmail && (
|
||||
|
||||
@@ -65,25 +65,24 @@ const FreightDashboardLayout = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="" />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;600;700&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
|
||||
<MantineProvider theme={freightMantineTheme}>
|
||||
<Box
|
||||
style={{
|
||||
display: "flex",
|
||||
height: "100dvh",
|
||||
overflow: "hidden",
|
||||
background: "var(--mantine-color-gray-1)",
|
||||
padding: "0px",
|
||||
fontFamily: "'Outfit', var(--font-sans)",
|
||||
}}
|
||||
>
|
||||
<Box style={{ display: "flex", height: "100%", minHeight: 0, width: "100%", gap: "5px" }}>
|
||||
<Box
|
||||
style={{
|
||||
display: "flex",
|
||||
height: "100%",
|
||||
minHeight: 0,
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<FreightSidebar
|
||||
sections={sidebarSections}
|
||||
activeHref={activeHref}
|
||||
@@ -98,18 +97,6 @@ const FreightDashboardLayout = ({
|
||||
minWidth: 0,
|
||||
flex: 1,
|
||||
flexDirection: "column",
|
||||
gap: "8px",
|
||||
}}
|
||||
>
|
||||
<Paper
|
||||
p={0}
|
||||
radius="lg"
|
||||
withBorder
|
||||
style={{
|
||||
flexShrink: 0,
|
||||
background: "white",
|
||||
border: "1px solid var(--mantine-color-gray-2)",
|
||||
boxShadow: "0 1px 3px rgba(0, 0, 0, 0.05)",
|
||||
}}
|
||||
>
|
||||
<FreightDashboardHeader
|
||||
@@ -123,24 +110,8 @@ const FreightDashboardLayout = ({
|
||||
theme={theme}
|
||||
onToggleTheme={toggleTheme}
|
||||
/>
|
||||
</Paper>
|
||||
|
||||
<Paper
|
||||
p={{ base: 16, md: 24 }}
|
||||
radius="lg"
|
||||
withBorder
|
||||
style={{
|
||||
minHeight: 0,
|
||||
flex: 1,
|
||||
overflowY: "auto",
|
||||
overscrollBehavior: "contain",
|
||||
background: "white",
|
||||
border: "1px solid var(--mantine-color-gray-2)",
|
||||
boxShadow: "0 1px 3px rgba(0, 0, 0, 0.05)",
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Paper>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
@@ -8,12 +8,7 @@
|
||||
max-height: 100%;
|
||||
width: 280px;
|
||||
flex-shrink: 0;
|
||||
border-radius: 16px;
|
||||
border: 1px solid #eef1f4;
|
||||
background: #ffffff;
|
||||
box-shadow:
|
||||
0 1px 2px rgba(15, 23, 42, 0.04),
|
||||
0 8px 24px -16px rgba(15, 23, 42, 0.12);
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
@@ -36,8 +31,9 @@
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background:
|
||||
radial-gradient(120px 80px at 24px 18px, rgba(34, 197, 94, 0.08), transparent 70%);
|
||||
background: radial-gradient(120px 80px at 24px 18px,
|
||||
rgba(34, 197, 94, 0.08),
|
||||
transparent 70%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@@ -50,7 +46,7 @@
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 13px;
|
||||
background: linear-gradient(135deg, #2DBF95 0%, #1B9E7A 60%, #15805F 100%);
|
||||
background: linear-gradient(135deg, #2dbf95 0%, #1b9e7a 60%, #15805f 100%);
|
||||
box-shadow:
|
||||
0 6px 16px -4px rgba(27, 158, 122, 0.45),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.25);
|
||||
@@ -72,13 +68,16 @@
|
||||
.fsb-nav::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.fsb-nav::-webkit-scrollbar-thumb {
|
||||
background: #e2e8f0;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.fsb-nav::-webkit-scrollbar-thumb:hover {
|
||||
background: #cbd5e1;
|
||||
}
|
||||
|
||||
.fsb-nav::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
@@ -121,12 +120,10 @@
|
||||
}
|
||||
|
||||
.fsb-item[data-active="true"] {
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
background: linear-gradient(135deg,
|
||||
rgba(34, 197, 94, 0.12) 0%,
|
||||
rgba(27, 158, 122, 0.06) 100%
|
||||
);
|
||||
color: #1B9E7A;
|
||||
rgba(27, 158, 122, 0.06) 100%);
|
||||
color: #1b9e7a;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
@@ -139,7 +136,7 @@
|
||||
width: 3px;
|
||||
height: 22px;
|
||||
border-radius: 0 4px 4px 0;
|
||||
background: linear-gradient(180deg, #2DBF95 0%, #1B9E7A 100%);
|
||||
background: linear-gradient(180deg, #2dbf95 0%, #1b9e7a 100%);
|
||||
}
|
||||
|
||||
.fsb-item-label {
|
||||
@@ -155,8 +152,6 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 31px;
|
||||
height: 31px;
|
||||
border-radius: 9px;
|
||||
flex-shrink: 0;
|
||||
background: #f1f5f9;
|
||||
@@ -164,13 +159,19 @@
|
||||
transition: all 160ms ease;
|
||||
}
|
||||
|
||||
/* ---- Icon well ---- */
|
||||
.fsb-icon svg {
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
.fsb-item:hover .fsb-icon {
|
||||
background: #e6ebf1;
|
||||
color: #334155;
|
||||
}
|
||||
|
||||
.fsb-item[data-active="true"] .fsb-icon {
|
||||
background: linear-gradient(135deg, #2DBF95 0%, #1B9E7A 100%);
|
||||
background: linear-gradient(135deg, #2dbf95 0%, #1b9e7a 100%);
|
||||
color: #ffffff;
|
||||
box-shadow: 0 5px 12px -2px rgba(27, 158, 122, 0.45);
|
||||
}
|
||||
@@ -219,9 +220,11 @@
|
||||
background: transparent;
|
||||
transition: background-color 150ms ease;
|
||||
}
|
||||
|
||||
.fsb-group:hover {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
.fsb-group-label {
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
@@ -229,8 +232,9 @@
|
||||
text-transform: uppercase;
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
.fsb-group[data-active="true"] .fsb-group-label {
|
||||
color: #1B9E7A;
|
||||
color: #1b9e7a;
|
||||
}
|
||||
|
||||
/* child leaf */
|
||||
@@ -251,12 +255,14 @@
|
||||
background-color 150ms ease,
|
||||
color 150ms ease;
|
||||
}
|
||||
|
||||
.fsb-child:hover {
|
||||
background-color: #f5f7fa;
|
||||
color: #0f172a;
|
||||
}
|
||||
|
||||
.fsb-child[data-active="true"] {
|
||||
color: #1B9E7A;
|
||||
color: #1b9e7a;
|
||||
font-weight: 600;
|
||||
background-color: rgba(27, 158, 122, 0.08);
|
||||
}
|
||||
@@ -269,11 +275,13 @@
|
||||
background: #cbd5e1;
|
||||
transition: all 150ms ease;
|
||||
}
|
||||
|
||||
.fsb-child:hover .fsb-dot {
|
||||
background: #94a3b8;
|
||||
}
|
||||
|
||||
.fsb-child[data-active="true"] .fsb-dot {
|
||||
background: #1B9E7A;
|
||||
background: #1b9e7a;
|
||||
box-shadow: 0 0 0 3px rgba(27, 158, 122, 0.16);
|
||||
}
|
||||
|
||||
@@ -283,36 +291,41 @@
|
||||
padding: 12px;
|
||||
border-top: 1px solid #f1f5f9;
|
||||
}
|
||||
|
||||
.fsb-status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 10px 12px;
|
||||
border-radius: 11px;
|
||||
background: linear-gradient(135deg, #E7F8F2 0%, #f8fafc 100%);
|
||||
background: linear-gradient(135deg, #e7f8f2 0%, #f8fafc 100%);
|
||||
border: 1px solid #e7f3ec;
|
||||
}
|
||||
|
||||
.fsb-pulse {
|
||||
position: relative;
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
border-radius: 50%;
|
||||
background: #2DBF95;
|
||||
background: #2dbf95;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.fsb-pulse::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: 50%;
|
||||
background: #2DBF95;
|
||||
background: #2dbf95;
|
||||
animation: fsb-pulse 2s ease-out infinite;
|
||||
}
|
||||
|
||||
@keyframes fsb-pulse {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(2.6);
|
||||
opacity: 0;
|
||||
|
||||
@@ -11,6 +11,7 @@ import { Box, Stack, Text } from "@mantine/core";
|
||||
|
||||
import type { SidebarItem, SidebarSection } from "./types";
|
||||
import "./FreightSidebar.css";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
export interface FreightSidebarProps {
|
||||
sections: SidebarSection[];
|
||||
@@ -176,20 +177,7 @@ const FreightSidebar = ({
|
||||
|
||||
return (
|
||||
<Box key={item.href}>
|
||||
<a
|
||||
href={item.href}
|
||||
className="fsb-item"
|
||||
data-active={isActive}
|
||||
onClick={(e) => {
|
||||
if (hasChildren) {
|
||||
setExpanded((current) => ({
|
||||
...current,
|
||||
[item.href!]: true,
|
||||
}));
|
||||
}
|
||||
navigateTo(e, item.href!);
|
||||
}}
|
||||
>
|
||||
<Link to={item.href} className="fsb-item" data-active={isActive}>
|
||||
{item.icon && <span className="fsb-icon">{item.icon}</span>}
|
||||
<span className="fsb-item-label">{item.label}</span>
|
||||
{hasChildren && (
|
||||
@@ -212,7 +200,7 @@ const FreightSidebar = ({
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
</a>
|
||||
</Link>
|
||||
|
||||
{hasChildren && isOpen && (
|
||||
<div className="fsb-branch">
|
||||
@@ -224,7 +212,16 @@ const FreightSidebar = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<Box component="aside" className="fsb-aside">
|
||||
<Box
|
||||
component="aside"
|
||||
className="fsb-aside"
|
||||
m="4px"
|
||||
bg="edr-bg"
|
||||
style={{
|
||||
border: "1px solid var(--mantine-color-gray-2)",
|
||||
boxShadow: "0 1px 3px rgba(0, 0, 0, 0.05)",
|
||||
}}
|
||||
>
|
||||
<div className="fsb-brand">
|
||||
<div className="fsb-logo">
|
||||
<Train size={23} color="white" strokeWidth={2.1} />
|
||||
@@ -233,7 +230,11 @@ const FreightSidebar = ({
|
||||
<Text
|
||||
size="md"
|
||||
fw={700}
|
||||
style={{ letterSpacing: "-0.3px", lineHeight: 1.2, color: "#0f172a" }}
|
||||
style={{
|
||||
letterSpacing: "-0.3px",
|
||||
lineHeight: 1.2,
|
||||
color: "#0f172a",
|
||||
}}
|
||||
>
|
||||
EDR Freight
|
||||
</Text>
|
||||
@@ -257,20 +258,6 @@ const FreightSidebar = ({
|
||||
</div>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
<div className="fsb-footer">
|
||||
<div className="fsb-status">
|
||||
<span className="fsb-pulse" />
|
||||
<Stack gap={0} style={{ minWidth: 0 }}>
|
||||
<Text size="xs" fw={600} style={{ color: "#15805F", lineHeight: 1.3 }}>
|
||||
All systems operational
|
||||
</Text>
|
||||
<Text size="10px" style={{ color: "#94a3b8", lineHeight: 1.3 }}>
|
||||
EDR Platform · v1.0
|
||||
</Text>
|
||||
</Stack>
|
||||
</div>
|
||||
</div>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
import { type FormEvent, useState } from "react";
|
||||
import { parsePhoneNumberFromString } from "libphonenumber-js";
|
||||
import { Eye, EyeOff, Mail, Smartphone, UserRound, ArrowUpRight, Globe, ChevronDown } from "lucide-react";
|
||||
import {
|
||||
Eye,
|
||||
EyeOff,
|
||||
Mail,
|
||||
Smartphone,
|
||||
UserRound,
|
||||
ArrowUpRight,
|
||||
Globe,
|
||||
ChevronDown,
|
||||
} from "lucide-react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import { useAuth } from "@/auth/useAuth";
|
||||
@@ -13,10 +22,25 @@ const loginModes: Array<{
|
||||
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" },
|
||||
];
|
||||
{
|
||||
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}$/;
|
||||
@@ -43,7 +67,9 @@ const normalizeIdentifier = (mode: LoginMode, value: string) => {
|
||||
}
|
||||
|
||||
if (!usernamePattern.test(trimmed)) {
|
||||
throw new Error("Username must be 3-32 characters and use letters, numbers, ., _, or -.");
|
||||
throw new Error(
|
||||
"Username must be 3-32 characters and use letters, numbers, ., _, or -.",
|
||||
);
|
||||
}
|
||||
|
||||
return trimmed;
|
||||
@@ -59,14 +85,24 @@ 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 = () => (
|
||||
<div className="pointer-events-none absolute inset-0 overflow-hidden" aria-hidden>
|
||||
<div
|
||||
className="pointer-events-none absolute inset-0 overflow-hidden"
|
||||
aria-hidden
|
||||
>
|
||||
<svg
|
||||
className="absolute -bottom-24 -left-24 h-[420px] w-[420px] text-white/[0.07]"
|
||||
viewBox="0 0 400 400"
|
||||
fill="none"
|
||||
>
|
||||
{[0, 1, 2, 3, 4, 5].map((ring) => (
|
||||
<circle key={ring} cx="200" cy="200" r={60 + ring * 36} stroke="currentColor" strokeWidth="1" />
|
||||
<circle
|
||||
key={ring}
|
||||
cx="200"
|
||||
cy="200"
|
||||
r={60 + ring * 36}
|
||||
stroke="currentColor"
|
||||
strokeWidth="1"
|
||||
/>
|
||||
))}
|
||||
</svg>
|
||||
<div className="absolute right-0 top-0 h-40 w-40 rounded-full bg-white/[0.06] blur-2xl" />
|
||||
@@ -74,12 +110,23 @@ const LeftPanelDecor = () => (
|
||||
);
|
||||
|
||||
const RightPanelDecor = () => (
|
||||
<div className="pointer-events-none absolute inset-0 overflow-hidden" aria-hidden>
|
||||
<div
|
||||
className="pointer-events-none absolute inset-0 overflow-hidden"
|
||||
aria-hidden
|
||||
>
|
||||
<div className="absolute -right-16 -top-20 h-56 w-56 rounded-full bg-primary/[0.06] blur-3xl" />
|
||||
<div className="absolute -bottom-12 left-1/4 h-40 w-40 rounded-full bg-primary/[0.04] blur-2xl" />
|
||||
<svg className="absolute inset-0 h-full w-full text-gray-200/40" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg
|
||||
className="absolute inset-0 h-full w-full text-gray-200/40"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<defs>
|
||||
<pattern id="login-grid" width="28" height="28" patternUnits="userSpaceOnUse">
|
||||
<pattern
|
||||
id="login-grid"
|
||||
width="28"
|
||||
height="28"
|
||||
patternUnits="userSpaceOnUse"
|
||||
>
|
||||
<circle cx="1" cy="1" r="0.75" fill="currentColor" />
|
||||
</pattern>
|
||||
</defs>
|
||||
@@ -99,7 +146,11 @@ const LeftPanel = () => (
|
||||
<LeftPanelDecor />
|
||||
|
||||
<div className="relative z-10 flex shrink-0 items-center justify-between px-4 pt-4 sm:px-6 sm:pt-6 lg:px-8 lg:pt-8">
|
||||
<img src={EDR_LOGO} alt="EDR Freight" className="h-7 w-auto brightness-0 invert sm:h-9" />
|
||||
<img
|
||||
src={EDR_LOGO}
|
||||
alt="EDR Freight"
|
||||
className="h-7 w-auto brightness-0 invert sm:h-9"
|
||||
/>
|
||||
<a
|
||||
href="#"
|
||||
className="flex items-center gap-1.5 rounded-full border border-white/70 bg-white/10 px-3 py-1.5 text-xs font-medium text-white backdrop-blur-sm transition-colors hover:bg-white/20 sm:px-4 sm:py-2 sm:text-sm"
|
||||
@@ -118,8 +169,8 @@ const LeftPanel = () => (
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm leading-relaxed text-white/85">
|
||||
Sign in to manage bookings, track cargo, and run logistics operations on the
|
||||
Ethio Djibouti Railway freight platform.
|
||||
Sign in to manage bookings, track cargo, and run logistics operations
|
||||
on the Ethio Djibouti Railway freight platform.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -138,13 +189,22 @@ const FormFooter = () => (
|
||||
<div className="relative z-10 flex shrink-0 flex-col items-center justify-between gap-3 border-t border-gray-100 px-4 py-4 text-xs text-gray-400 sm:flex-row sm:gap-4 sm:px-6 sm:py-4 lg:px-8 lg:pb-6">
|
||||
<span className="shrink-0">© 2026 EDR Freight</span>
|
||||
<div className="flex flex-wrap items-center justify-center gap-3 sm:justify-end sm:gap-6">
|
||||
<a href="#" className="font-semibold text-gray-700 transition-colors hover:text-primary">
|
||||
<a
|
||||
href="#"
|
||||
className="font-semibold text-gray-700 transition-colors hover:text-primary"
|
||||
>
|
||||
Terms & Conditions
|
||||
</a>
|
||||
<a href="#" className="font-semibold text-gray-700 transition-colors hover:text-primary">
|
||||
<a
|
||||
href="#"
|
||||
className="font-semibold text-gray-700 transition-colors hover:text-primary"
|
||||
>
|
||||
Privacy Policy
|
||||
</a>
|
||||
<a href="#" className="font-semibold text-gray-700 transition-colors hover:text-primary">
|
||||
<a
|
||||
href="#"
|
||||
className="font-semibold text-gray-700 transition-colors hover:text-primary"
|
||||
>
|
||||
Help & Support
|
||||
</a>
|
||||
</div>
|
||||
@@ -176,7 +236,7 @@ const LoginPage = () => {
|
||||
setNormalizedIdentifier(normalized);
|
||||
|
||||
const result = await login({ email: normalized, password });
|
||||
console.log(result)
|
||||
console.log(result);
|
||||
if (result.mfaRequired) {
|
||||
setNeedsMfa(true);
|
||||
return;
|
||||
@@ -212,15 +272,20 @@ const LoginPage = () => {
|
||||
</div>
|
||||
|
||||
<div className="mb-4 space-y-1.5 text-center sm:mb-5">
|
||||
<h1 className="text-xl font-bold tracking-tight text-gray-900 sm:text-2xl">Get Started</h1>
|
||||
<h1 className="text-xl font-bold tracking-tight text-gray-900 sm:text-2xl">
|
||||
Get Started
|
||||
</h1>
|
||||
<p className="text-sm leading-relaxed text-gray-500">
|
||||
Log in to access the freight backoffice & explore all logistics resources.
|
||||
Log in to access the freight backoffice & explore all logistics
|
||||
resources.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex w-full flex-col gap-4">
|
||||
<div className="space-y-1.5">
|
||||
<label className="text-sm font-medium text-gray-800">Sign in method</label>
|
||||
<label className="text-sm font-medium text-gray-800">
|
||||
Sign in method
|
||||
</label>
|
||||
<div className="relative">
|
||||
<select
|
||||
value={mode}
|
||||
@@ -267,32 +332,26 @@ const LoginPage = () => {
|
||||
className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 transition-colors hover:text-gray-600"
|
||||
aria-label={showPassword ? "Hide password" : "Show password"}
|
||||
>
|
||||
{showPassword ? <EyeOff className="h-5 w-5" /> : <Eye className="h-5 w-5" />}
|
||||
{showPassword ? (
|
||||
<EyeOff className="h-5 w-5" />
|
||||
) : (
|
||||
<Eye className="h-5 w-5" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label className="flex cursor-pointer items-start gap-2.5">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="mt-0.5 h-4 w-4 shrink-0 cursor-pointer rounded-md border-gray-300 text-primary transition-colors focus:ring-2 focus:ring-primary/20 focus:ring-offset-0"
|
||||
/>
|
||||
<span className="text-sm leading-snug text-gray-600">
|
||||
I agree to EDR Freight{" "}
|
||||
<a href="#" className="font-semibold text-primary hover:underline">
|
||||
Terms & Conditions
|
||||
</a>
|
||||
.
|
||||
</span>
|
||||
</label>
|
||||
|
||||
{error ? (
|
||||
<div className="rounded-lg border border-red-200 bg-red-50 px-3 py-2.5 text-sm text-red-700">
|
||||
{error}
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<button type="submit" disabled={submitting} className={primaryButtonClass}>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={submitting}
|
||||
className={primaryButtonClass}
|
||||
>
|
||||
{submitting ? "Signing in..." : "Sign In"}
|
||||
</button>
|
||||
|
||||
@@ -318,8 +377,10 @@ const LoginPage = () => {
|
||||
</h1>
|
||||
<p className="text-sm leading-relaxed text-gray-500">
|
||||
We sent a verification code to{" "}
|
||||
<span className="font-medium text-gray-700">{normalizedIdentifier}</span>. Enter it below
|
||||
to complete sign in.
|
||||
<span className="font-medium text-gray-700">
|
||||
{normalizedIdentifier}
|
||||
</span>
|
||||
. Enter it below to complete sign in.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -354,7 +415,11 @@ const LoginPage = () => {
|
||||
>
|
||||
Back
|
||||
</button>
|
||||
<button type="submit" disabled={submitting} className={`${primaryButtonClass} min-w-0 flex-1`}>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={submitting}
|
||||
className={`${primaryButtonClass} min-w-0 flex-1`}
|
||||
>
|
||||
{submitting ? "Verifying..." : "Verify"}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { createTheme, type MantineColorsTuple } from "@mantine/core";
|
||||
import {
|
||||
colorsTuple,
|
||||
createTheme,
|
||||
type MantineColorsTuple,
|
||||
} from "@mantine/core";
|
||||
|
||||
/** EDR Freight primary brand green */
|
||||
export const FREIGHT_BRAND = "#1B9E7A";
|
||||
@@ -17,24 +21,155 @@ export const freightBrand = {
|
||||
mutedBorder: "#B7EBDC",
|
||||
} as const;
|
||||
|
||||
/** Mantine green scale with #1B9E7A at index 6 (filled buttons, nav active). */
|
||||
const freightGreen: MantineColorsTuple = [
|
||||
"#E7F8F2",
|
||||
"#C5EFE1",
|
||||
"#9CE4CD",
|
||||
"#6BD6B5",
|
||||
"#41C8A0",
|
||||
"#25B58C",
|
||||
FREIGHT_BRAND,
|
||||
FREIGHT_BRAND_DARK,
|
||||
"#105F47",
|
||||
"#0A3D2E",
|
||||
const edrGreen: MantineColorsTuple = [
|
||||
"#ecfdf5",
|
||||
"#d1fae5",
|
||||
"#a7f3d0",
|
||||
"#6ee7b7",
|
||||
"#34d399",
|
||||
"#0EA371",
|
||||
"#0A8A5F",
|
||||
"#0A6F4D",
|
||||
"#065f46",
|
||||
"#064e3b",
|
||||
];
|
||||
|
||||
const neutral: MantineColorsTuple = [
|
||||
"#F4F7FA",
|
||||
"#eef2f7",
|
||||
"#E6ECF2",
|
||||
"#d0dae6",
|
||||
"#b0bfce",
|
||||
"#8fa0b2",
|
||||
"#6B7C8E",
|
||||
"#4b5a6a",
|
||||
"#10202F",
|
||||
"#0C1A2B",
|
||||
];
|
||||
|
||||
export const freightMantineTheme = createTheme({
|
||||
primaryColor: "green",
|
||||
colors: {
|
||||
green: freightGreen,
|
||||
"edr-green": edrGreen,
|
||||
gray: neutral,
|
||||
|
||||
// Brand surface + text tokens (single-value semantic colors).
|
||||
// Each generates --mantine-color-{name}-{0..9} CSS variables.
|
||||
"edr-bg": colorsTuple("#F7FAFC"),
|
||||
"edr-card": colorsTuple("#FFFFFF"),
|
||||
"edr-border": colorsTuple("#E6ECF2"),
|
||||
"edr-divider": colorsTuple("#EEF1F5"),
|
||||
"edr-text": colorsTuple("#10202F"),
|
||||
"edr-muted": colorsTuple("#6B7C8E"),
|
||||
"edr-soft": colorsTuple("#ECF6F1"),
|
||||
"edr-ink": colorsTuple("#0C1A2B"),
|
||||
"edr-accent": colorsTuple("#F2A516"),
|
||||
|
||||
// Semantic status shades (from the design system).
|
||||
"edr-amber-soft": colorsTuple("#FDF3E0"),
|
||||
"edr-amber-text": colorsTuple("#9A5B00"),
|
||||
"edr-blue": colorsTuple("#2E5B96"),
|
||||
"edr-blue-soft": colorsTuple("#E9F0F8"),
|
||||
"edr-blue-dot": colorsTuple("#3B6FB0"),
|
||||
"edr-red": colorsTuple("#C0392B"),
|
||||
"edr-red-soft": colorsTuple("#FBEAE7"),
|
||||
"edr-slate": colorsTuple("#475569"),
|
||||
"edr-slate-soft": colorsTuple("#F1F4F7"),
|
||||
"edr-slate-soft2": colorsTuple("#EEF2F6"),
|
||||
"edr-step": colorsTuple("#94A3B8"),
|
||||
"edr-step-idle": colorsTuple("#D5DBE2"),
|
||||
"edr-conn-idle": colorsTuple("#E6EAEF"),
|
||||
},
|
||||
primaryColor: "edr-green",
|
||||
primaryShade: { light: 5, dark: 4 },
|
||||
|
||||
white: "#ffffff",
|
||||
black: "#10202F",
|
||||
|
||||
fontFamily:
|
||||
'"Inter", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
|
||||
|
||||
defaultRadius: "md",
|
||||
|
||||
radius: {
|
||||
xs: "4px",
|
||||
sm: "6px",
|
||||
md: "8px",
|
||||
lg: "12px",
|
||||
xl: "18px",
|
||||
},
|
||||
|
||||
spacing: {
|
||||
xs: "8px",
|
||||
sm: "12px",
|
||||
md: "16px",
|
||||
lg: "24px",
|
||||
xl: "32px",
|
||||
},
|
||||
|
||||
fontSizes: {
|
||||
xs: "12px",
|
||||
sm: "14px",
|
||||
md: "16px",
|
||||
lg: "20px",
|
||||
xl: "24px",
|
||||
},
|
||||
|
||||
lineHeights: {
|
||||
xs: "1.4",
|
||||
sm: "1.45",
|
||||
md: "1.55",
|
||||
lg: "1.55",
|
||||
xl: "1.5",
|
||||
},
|
||||
|
||||
headings: {
|
||||
fontFamily: '"Inter", var(--mantine-font-family)',
|
||||
fontWeight: "700",
|
||||
sizes: {
|
||||
h1: { fontSize: "36px", lineHeight: "1.1", fontWeight: "800" },
|
||||
h2: { fontSize: "28px", lineHeight: "1.2", fontWeight: "700" },
|
||||
h3: { fontSize: "22px", lineHeight: "1.3", fontWeight: "600" },
|
||||
h4: { fontSize: "18px", lineHeight: "1.4", fontWeight: "600" },
|
||||
h5: { fontSize: "15px", lineHeight: "1.45", fontWeight: "600" },
|
||||
h6: { fontSize: "13px", lineHeight: "1.45", fontWeight: "600" },
|
||||
},
|
||||
},
|
||||
|
||||
shadows: {
|
||||
xs: "0 1px 2px rgba(16, 24, 40, 0.04)",
|
||||
sm: "0 1px 3px rgba(16, 24, 40, 0.06)",
|
||||
md: "0 4px 12px rgba(16, 24, 40, 0.06)",
|
||||
},
|
||||
|
||||
components: {
|
||||
Card: {
|
||||
defaultProps: {
|
||||
radius: "xl",
|
||||
withBorder: true,
|
||||
shadow: "none",
|
||||
padding: "lg",
|
||||
},
|
||||
styles: {
|
||||
root: { borderColor: "#E6ECF2" },
|
||||
},
|
||||
},
|
||||
Button: {
|
||||
defaultProps: { radius: "md" },
|
||||
styles: { root: { fontWeight: 600 } },
|
||||
},
|
||||
Badge: {
|
||||
defaultProps: { radius: "xl", variant: "light" },
|
||||
styles: { root: { fontWeight: 600, textTransform: "none" } },
|
||||
},
|
||||
Paper: {
|
||||
defaultProps: { radius: "xl", shadow: "none", withBorder: true },
|
||||
styles: { root: { borderColor: "#E6ECF2" } },
|
||||
},
|
||||
Table: {
|
||||
defaultProps: { verticalSpacing: "sm", horizontalSpacing: "md" },
|
||||
},
|
||||
Title: {
|
||||
styles: { root: { letterSpacing: "-0.01em" } },
|
||||
},
|
||||
},
|
||||
fontFamily: "'Outfit', var(--font-sans)",
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user