mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 18:48:11 +00:00
260 lines
7.0 KiB
TypeScript
260 lines
7.0 KiB
TypeScript
import { api } from "@/services/api";
|
|
import type { ProfileResponse } from "@/types/profile";
|
|
import {
|
|
Alert,
|
|
Card,
|
|
Center,
|
|
Container,
|
|
Group,
|
|
Loader,
|
|
Tabs,
|
|
Text,
|
|
Title,
|
|
} from "@mantine/core";
|
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
import {
|
|
AlertCircle,
|
|
Briefcase,
|
|
Building2,
|
|
FileCheck,
|
|
User,
|
|
UserCheck,
|
|
} from "lucide-react";
|
|
import { useCallback, useEffect, useState } from "react";
|
|
import { useNavigate, useSearchParams } from "react-router-dom";
|
|
import TabCompanyProfile from "./settings/TabCompanyProfile";
|
|
import TabContactPerson from "./settings/TabContactPerson";
|
|
import TabDocuments from "./settings/TabDocuments";
|
|
import TabGeneralManager from "./settings/TabGeneralManager";
|
|
import TabPowerOfAttorney from "./settings/TabPowerOfAttorney";
|
|
|
|
type SettingsTab = "company" | "contact" | "gm" | "poa" | "documents";
|
|
|
|
function tabIncomplete(tabId: SettingsTab, profile?: ProfileResponse | null): boolean {
|
|
if (!profile) return false;
|
|
switch (tabId) {
|
|
case "company":
|
|
return !profile.companyEmail || !profile.companyPhone || !profile.companyAddress || !profile.fanNumber;
|
|
case "contact":
|
|
return !profile.contactPersonName || !profile.contactPersonPhone;
|
|
case "gm":
|
|
return !profile.generalManagerName || !profile.generalManagerEmail || !profile.generalManagerPhone;
|
|
case "poa":
|
|
return false;
|
|
case "documents":
|
|
return false;
|
|
}
|
|
}
|
|
|
|
const TABS: { id: SettingsTab; label: string; icon: React.ReactNode }[] = [
|
|
{ id: "company", label: "Company Profile", icon: <Building2 size={16} /> },
|
|
{ id: "contact", label: "Contact Person", icon: <User size={16} /> },
|
|
{ id: "gm", label: "General Manager", icon: <Briefcase size={16} /> },
|
|
{ id: "poa", label: "Power of Attorney", icon: <UserCheck size={16} /> },
|
|
{ id: "documents", label: "Documents", icon: <FileCheck size={16} /> },
|
|
];
|
|
|
|
const TAB_ORDER: SettingsTab[] = [
|
|
"company",
|
|
"contact",
|
|
"gm",
|
|
"poa",
|
|
"documents",
|
|
];
|
|
|
|
export default function SettingsPage() {
|
|
const navigate = useNavigate();
|
|
const queryClient = useQueryClient();
|
|
const [searchParams, setSearchParams] = useSearchParams();
|
|
const tab = (searchParams.get("tab") as SettingsTab) || "company";
|
|
const setTab = useCallback(
|
|
(t: SettingsTab) => {
|
|
setSearchParams(
|
|
(prev) => {
|
|
const next = new URLSearchParams(prev);
|
|
next.set("tab", t);
|
|
return next;
|
|
},
|
|
{ replace: true },
|
|
);
|
|
},
|
|
[setSearchParams],
|
|
);
|
|
|
|
const profileQuery = useQuery(
|
|
api.companies.getProfile.queryOptions({
|
|
retry: false,
|
|
refetchOnWindowFocus: false,
|
|
}),
|
|
);
|
|
|
|
const profile = profileQuery.data;
|
|
|
|
useEffect(() => {
|
|
if (profileQuery.dataUpdatedAt > 0) {
|
|
queryClient.invalidateQueries({
|
|
queryKey: api.companies.getInfo.queryKey(),
|
|
});
|
|
}
|
|
}, [profileQuery.dataUpdatedAt, queryClient]);
|
|
|
|
const [isOnboarding, setIsOnboarding] = useState<boolean | null>(null);
|
|
|
|
useEffect(() => {
|
|
if (profileQuery.isFetched && isOnboarding === null) {
|
|
setIsOnboarding(!profileQuery.data);
|
|
}
|
|
}, [profileQuery.isFetched, profileQuery.data, isOnboarding]);
|
|
|
|
const handleOnboardingSuccess = useCallback(() => {
|
|
setTab("contact");
|
|
}, [setTab]);
|
|
|
|
const handleContactContinue = useCallback(() => {
|
|
setTab("gm");
|
|
}, [setTab]);
|
|
|
|
const handleGMContinue = useCallback(() => {
|
|
setTab("poa");
|
|
}, [setTab]);
|
|
|
|
const handlePOAContinue = useCallback(() => {
|
|
setTab("documents");
|
|
}, [setTab]);
|
|
|
|
const handleDocumentsContinue = useCallback(() => {
|
|
navigate("/portal");
|
|
}, [navigate]);
|
|
|
|
if (profileQuery.isPending) {
|
|
return (
|
|
<Center h="100%">
|
|
<Loader color="edr-green" size="lg" />
|
|
</Center>
|
|
);
|
|
}
|
|
|
|
const onboarding = isOnboarding === true;
|
|
|
|
const renderProfileContent = (children: React.ReactNode) => {
|
|
if (onboarding && tab !== "company" && !profile) {
|
|
return (
|
|
<Center h={200}>
|
|
<Loader color="edr-green" />
|
|
</Center>
|
|
);
|
|
}
|
|
if (!profile) {
|
|
return (
|
|
<Card padding="xl">
|
|
<Center>
|
|
<Alert
|
|
icon={<AlertCircle size={24} />}
|
|
color="gray"
|
|
variant="light"
|
|
>
|
|
Please complete the company profile first.
|
|
</Alert>
|
|
</Center>
|
|
</Card>
|
|
);
|
|
}
|
|
return children;
|
|
};
|
|
|
|
return (
|
|
<Container size="xl" px="lg">
|
|
<Group justify="space-between" mb="xl">
|
|
<div>
|
|
<Title order={1} size="h2">
|
|
{onboarding ? "Complete Your Profile" : "Account Settings"}
|
|
</Title>
|
|
<Text c="edr-muted" size="sm" mt={4}>
|
|
{onboarding
|
|
? "Set up your company profile, personnel, and documents to get started"
|
|
: "Manage your company profile, personnel, and documents"}
|
|
</Text>
|
|
</div>
|
|
</Group>
|
|
|
|
<Tabs
|
|
value={tab}
|
|
onChange={(value) => {
|
|
if (!value) return;
|
|
// if (onboarding) return;
|
|
setTab(value as SettingsTab);
|
|
}}
|
|
>
|
|
<Tabs.List mb="md">
|
|
{TABS.map((t) => (
|
|
<Tabs.Tab
|
|
key={t.id}
|
|
value={t.id}
|
|
leftSection={t.icon}
|
|
disabled={!onboarding && !profile && t.id !== "company"}
|
|
rightSection={
|
|
!onboarding && profile && tabIncomplete(t.id, profile) ? (
|
|
<AlertCircle size={14} color="red" />
|
|
) : undefined
|
|
}
|
|
>
|
|
{t.label}
|
|
</Tabs.Tab>
|
|
))}
|
|
</Tabs.List>
|
|
|
|
<Tabs.Panel value="company">
|
|
{!profile ? (
|
|
<TabCompanyProfile
|
|
mode="create"
|
|
onCreateSuccess={handleOnboardingSuccess}
|
|
/>
|
|
) : (
|
|
<TabCompanyProfile mode="edit" profile={profile} />
|
|
)}
|
|
</Tabs.Panel>
|
|
|
|
<Tabs.Panel value="contact">
|
|
{renderProfileContent(
|
|
<TabContactPerson
|
|
profile={profile!}
|
|
mode={onboarding ? "onboarding" : "edit"}
|
|
onContinue={handleContactContinue}
|
|
/>,
|
|
)}
|
|
</Tabs.Panel>
|
|
|
|
<Tabs.Panel value="gm">
|
|
{renderProfileContent(
|
|
<TabGeneralManager
|
|
profile={profile!}
|
|
mode={onboarding ? "onboarding" : "edit"}
|
|
onContinue={handleGMContinue}
|
|
/>,
|
|
)}
|
|
</Tabs.Panel>
|
|
|
|
<Tabs.Panel value="poa">
|
|
{renderProfileContent(
|
|
<TabPowerOfAttorney
|
|
profile={profile!}
|
|
mode={onboarding ? "onboarding" : "edit"}
|
|
onContinue={handlePOAContinue}
|
|
/>,
|
|
)}
|
|
</Tabs.Panel>
|
|
|
|
<Tabs.Panel value="documents">
|
|
{renderProfileContent(
|
|
<TabDocuments
|
|
profile={profile!}
|
|
mode={onboarding ? "onboarding" : "edit"}
|
|
onContinue={handleDocumentsContinue}
|
|
/>,
|
|
)}
|
|
</Tabs.Panel>
|
|
</Tabs>
|
|
</Container>
|
|
);
|
|
}
|