mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-07 22:25:42 +00:00
411 lines
12 KiB
TypeScript
411 lines
12 KiB
TypeScript
import { api } from "@/services/api";
|
|
import {
|
|
Badge,
|
|
Box,
|
|
Button,
|
|
Card,
|
|
Center,
|
|
Container,
|
|
Divider,
|
|
Grid,
|
|
Group,
|
|
Loader,
|
|
SimpleGrid,
|
|
Stack,
|
|
Text,
|
|
ThemeIcon,
|
|
Title,
|
|
} from "@mantine/core";
|
|
import { useQuery } from "@tanstack/react-query";
|
|
import {
|
|
BadgeCheck,
|
|
Briefcase,
|
|
Building,
|
|
Building2,
|
|
FileCheck,
|
|
Globe,
|
|
Mail,
|
|
MapPin,
|
|
Phone,
|
|
Plus,
|
|
ShieldCheck,
|
|
User,
|
|
UserCheck,
|
|
} from "lucide-react";
|
|
import { Link } from "react-router-dom";
|
|
import { rolesForCompanyType } from "./settings/companyRoles";
|
|
|
|
function InfoItem({
|
|
icon,
|
|
label,
|
|
value,
|
|
}: {
|
|
icon?: React.ReactNode;
|
|
label: string;
|
|
value?: string | null;
|
|
}) {
|
|
return (
|
|
<Group gap="sm" align="flex-start" wrap="nowrap">
|
|
{icon && (
|
|
<ThemeIcon variant="light" color="edr-green" size="md" radius="md">
|
|
{icon}
|
|
</ThemeIcon>
|
|
)}
|
|
<Stack gap={2}>
|
|
<Text size="xs" fw={700} tt="uppercase" c="edr-muted">
|
|
{label}
|
|
</Text>
|
|
<Text size="sm" fw={600} c="edr-text">
|
|
{value || "—"}
|
|
</Text>
|
|
</Stack>
|
|
</Group>
|
|
);
|
|
}
|
|
|
|
function CardHeading({
|
|
icon,
|
|
title,
|
|
description,
|
|
}: {
|
|
icon: React.ReactNode;
|
|
title: string;
|
|
description: string;
|
|
}) {
|
|
return (
|
|
<Stack gap={2} mb="md">
|
|
<Group gap="sm">
|
|
{icon}
|
|
<Title order={4} size="h5">
|
|
{title}
|
|
</Title>
|
|
</Group>
|
|
<Text size="sm" c="edr-muted">
|
|
{description}
|
|
</Text>
|
|
</Stack>
|
|
);
|
|
}
|
|
|
|
function PersonnelGroup({
|
|
color,
|
|
title,
|
|
children,
|
|
}: {
|
|
color: string;
|
|
title: string;
|
|
children: React.ReactNode;
|
|
}) {
|
|
return (
|
|
<Stack gap="sm">
|
|
<Group gap="xs">
|
|
<Box w={4} h={16} bg={color} style={{ borderRadius: 2 }} />
|
|
<Text size="sm" fw={700} tt="uppercase" c="edr-text">
|
|
{title}
|
|
</Text>
|
|
</Group>
|
|
<Stack gap="sm" pl="lg">
|
|
{children}
|
|
</Stack>
|
|
</Stack>
|
|
);
|
|
}
|
|
|
|
export default function ProfilePage() {
|
|
const { data: profile, isPending } = useQuery(
|
|
api.companies.getProfile.queryOptions(),
|
|
);
|
|
|
|
if (isPending) {
|
|
return (
|
|
<Center h="100%">
|
|
<Loader color="edr-green" size="lg" />
|
|
</Center>
|
|
);
|
|
}
|
|
|
|
if (!profile) {
|
|
return (
|
|
<Center h="100%">
|
|
<Text c="edr-muted">No company profile found.</Text>
|
|
</Center>
|
|
);
|
|
}
|
|
|
|
// Registered operational profiles keyed by type, plus the roles this company
|
|
// type may hold (importer/exporter for a customer). Mirrors CompanyRolesCard.
|
|
const refByType = new Map(profile.companyProfiles.map((p) => [p.type, p]));
|
|
const roleOptions = rolesForCompanyType(profile.companyType);
|
|
const activeOptions = roleOptions.filter((o) => refByType.has(o.type));
|
|
|
|
return (
|
|
<Container size="xl" px="lg" py="xl">
|
|
{/* Header */}
|
|
<Group gap="lg" align="center" mb="lg">
|
|
<ThemeIcon variant="light" color="edr-green" size={88} radius="lg">
|
|
<User size={44} />
|
|
</ThemeIcon>
|
|
<Stack gap={6}>
|
|
<Group gap="sm" align="center">
|
|
<Title order={1} size="h2">
|
|
{profile.companyName}
|
|
</Title>
|
|
<Badge color="edr-green" variant="light">
|
|
Verified
|
|
</Badge>
|
|
</Group>
|
|
{activeOptions.length > 0 ? (
|
|
<Group gap="xs">
|
|
{activeOptions.map((opt) => (
|
|
<Badge
|
|
key={opt.type}
|
|
variant="light"
|
|
color="edr-green"
|
|
size="lg"
|
|
radius="sm"
|
|
>
|
|
{opt.label} · {refByType.get(opt.type)!.reference}
|
|
</Badge>
|
|
))}
|
|
</Group>
|
|
) : (
|
|
<Group gap={6} c="edr-muted">
|
|
<Building size={16} />
|
|
<Text c="edr-muted" fw={500}>
|
|
{profile.companyType}
|
|
</Text>
|
|
</Group>
|
|
)}
|
|
</Stack>
|
|
</Group>
|
|
|
|
<Divider mb="lg" />
|
|
|
|
<Grid gap="lg">
|
|
{/* Left Column */}
|
|
<Grid.Col span={{ base: 12, lg: 8 }}>
|
|
<Stack gap="lg">
|
|
{/* Company Details */}
|
|
<Card>
|
|
<CardHeading
|
|
icon={
|
|
<Building2
|
|
size={20}
|
|
color="var(--mantine-color-edr-green-6)"
|
|
/>
|
|
}
|
|
title="Company Details"
|
|
description="Business registration information"
|
|
/>
|
|
<SimpleGrid cols={{ base: 1, sm: 2 }} spacing="md">
|
|
<InfoItem
|
|
icon={<Globe size={16} />}
|
|
label="Location"
|
|
value={profile.companyLocation}
|
|
/>
|
|
<InfoItem
|
|
icon={<MapPin size={16} />}
|
|
label="Address"
|
|
value={profile.companyAddress}
|
|
/>
|
|
<InfoItem
|
|
icon={<FileCheck size={16} />}
|
|
label="TIN Number"
|
|
value={profile.tinNumber}
|
|
/>
|
|
<InfoItem
|
|
icon={<ShieldCheck size={16} />}
|
|
label="FAN Number"
|
|
value={profile.fanNumber}
|
|
/>
|
|
<InfoItem
|
|
icon={<Mail size={16} />}
|
|
label="Email"
|
|
value={profile.companyEmail}
|
|
/>
|
|
<InfoItem
|
|
icon={<Phone size={16} />}
|
|
label="Phone"
|
|
value={profile.companyPhone}
|
|
/>
|
|
</SimpleGrid>
|
|
</Card>
|
|
|
|
{/* Key Personnel */}
|
|
<Card>
|
|
<CardHeading
|
|
icon={
|
|
<Briefcase
|
|
size={20}
|
|
color="var(--mantine-color-edr-green-6)"
|
|
/>
|
|
}
|
|
title="Key Personnel"
|
|
description="Management and contact persons"
|
|
/>
|
|
<SimpleGrid cols={{ base: 1, md: 2 }} spacing="lg">
|
|
<PersonnelGroup color="edr-green" title="Contact Person">
|
|
<InfoItem label="Name" value={profile.contactPersonName} />
|
|
<InfoItem label="Phone" value={profile.contactPersonPhone} />
|
|
</PersonnelGroup>
|
|
<PersonnelGroup color="edr-accent" title="General Manager">
|
|
<InfoItem label="Name" value={profile.generalManagerName} />
|
|
<InfoItem label="Email" value={profile.generalManagerEmail} />
|
|
<InfoItem label="Phone" value={profile.generalManagerPhone} />
|
|
</PersonnelGroup>
|
|
</SimpleGrid>
|
|
</Card>
|
|
|
|
{/* Power of Attorney */}
|
|
{profile.poaName && (
|
|
<Card style={{ borderStyle: "dashed" }}>
|
|
<CardHeading
|
|
icon={
|
|
<UserCheck
|
|
size={20}
|
|
color="var(--mantine-color-edr-accent-6)"
|
|
/>
|
|
}
|
|
title="Power of Attorney"
|
|
description="Authorized representative details"
|
|
/>
|
|
<SimpleGrid cols={{ base: 1, md: 2 }} spacing="md">
|
|
<InfoItem label="PoA Name" value={profile.poaName} />
|
|
<InfoItem label="PoA Email" value={profile.poaEmail} />
|
|
<InfoItem label="PoA Phone" value={profile.poaPhone} />
|
|
<InfoItem label="PoA Location" value={profile.poaLocation} />
|
|
</SimpleGrid>
|
|
</Card>
|
|
)}
|
|
</Stack>
|
|
</Grid.Col>
|
|
|
|
{/* Right Column */}
|
|
<Grid.Col span={{ base: 12, lg: 4 }}>
|
|
<Stack gap="lg">
|
|
{/* Operating Roles */}
|
|
<Card>
|
|
<CardHeading
|
|
icon={
|
|
<BadgeCheck
|
|
size={20}
|
|
color="var(--mantine-color-edr-green-6)"
|
|
/>
|
|
}
|
|
title="Operating Roles"
|
|
description="Your registered freight roles and reference numbers"
|
|
/>
|
|
{roleOptions.length === 0 ? (
|
|
<Text size="sm" c="edr-muted">
|
|
Role management for this company type is coming soon.
|
|
</Text>
|
|
) : (
|
|
<Stack gap="md">
|
|
{roleOptions.map((opt) => {
|
|
const active = refByType.get(opt.type);
|
|
return (
|
|
<Group
|
|
key={opt.type}
|
|
justify="space-between"
|
|
wrap="nowrap"
|
|
align="center"
|
|
>
|
|
<Group gap="sm" wrap="nowrap">
|
|
<ThemeIcon
|
|
variant="light"
|
|
color="edr-green"
|
|
size="lg"
|
|
radius="md"
|
|
>
|
|
{opt.icon}
|
|
</ThemeIcon>
|
|
<Stack gap={2}>
|
|
<Text size="sm" fw={600} c="edr-text">
|
|
{opt.label}
|
|
</Text>
|
|
<Text
|
|
size="xs"
|
|
c="edr-muted"
|
|
ff={active ? "monospace" : undefined}
|
|
>
|
|
{active ? active.reference : "Not registered"}
|
|
</Text>
|
|
</Stack>
|
|
</Group>
|
|
{active ? (
|
|
<Badge
|
|
variant="light"
|
|
color={
|
|
active.status === "active"
|
|
? "edr-green"
|
|
: "edr-accent"
|
|
}
|
|
tt="capitalize"
|
|
>
|
|
{active.status}
|
|
</Badge>
|
|
) : (
|
|
<Button
|
|
component={Link}
|
|
to="/settings?tab=company"
|
|
size="xs"
|
|
variant="light"
|
|
color="edr-green"
|
|
leftSection={<Plus size={14} />}
|
|
>
|
|
Add {opt.label}
|
|
</Button>
|
|
)}
|
|
</Group>
|
|
);
|
|
})}
|
|
</Stack>
|
|
)}
|
|
</Card>
|
|
|
|
{/* Secure Account */}
|
|
<Card
|
|
padding="xl"
|
|
style={{
|
|
background: "var(--mantine-color-edr-ink-6)",
|
|
position: "relative",
|
|
overflow: "hidden",
|
|
}}
|
|
>
|
|
<Box
|
|
style={{
|
|
position: "absolute",
|
|
top: 16,
|
|
right: 16,
|
|
opacity: 0.1,
|
|
}}
|
|
>
|
|
<ShieldCheck size={128} color="white" />
|
|
</Box>
|
|
<Stack gap="md" style={{ position: "relative", zIndex: 1 }}>
|
|
<Title order={3} size="h4" c="white">
|
|
Secure Account
|
|
</Title>
|
|
<Text size="sm" c="gray.4">
|
|
Your information is protected by enterprise-grade security.
|
|
Contact support for verified information updates.
|
|
</Text>
|
|
<Button
|
|
component={Link}
|
|
to="/settings"
|
|
variant="white"
|
|
color="dark"
|
|
mt="xs"
|
|
w="fit-content"
|
|
>
|
|
Edit Settings
|
|
</Button>
|
|
</Stack>
|
|
</Card>
|
|
</Stack>
|
|
</Grid.Col>
|
|
</Grid>
|
|
</Container>
|
|
);
|
|
}
|