mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 05:18:11 +00:00
style: show the profile and the review ont the settings
This commit is contained in:
@@ -25,6 +25,7 @@ import {
|
||||
Menu as MenuIcon,
|
||||
Moon,
|
||||
Plus,
|
||||
RefreshCw,
|
||||
Search,
|
||||
Settings,
|
||||
Sun,
|
||||
@@ -59,7 +60,13 @@ export interface AppLayoutProps {
|
||||
userName?: string;
|
||||
userEmail?: string;
|
||||
/** Operational profiles for the company — surfaced as reference chips in the account menu. */
|
||||
companyProfiles?: { type: string; reference: string; status?: string }[];
|
||||
companyProfiles?: {
|
||||
id?: string;
|
||||
type: string;
|
||||
reference: string;
|
||||
status?: string;
|
||||
reviewNote?: string | null;
|
||||
}[];
|
||||
/** Company type (e.g. "customer", "forwarder") — gates the "Add service" control. */
|
||||
companyType?: string | null;
|
||||
/** Create a new service profile of the given type (with business license). */
|
||||
@@ -67,6 +74,11 @@ export interface AppLayoutProps {
|
||||
type: ServiceType,
|
||||
licenseFiles: File[],
|
||||
) => Promise<SwitchResult> | void;
|
||||
/** Resubmit a rejected service for approval, optionally replacing its license. */
|
||||
onReapplyProfile?: (
|
||||
profileId: string,
|
||||
licenseFiles: File[],
|
||||
) => Promise<SwitchResult> | void;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
@@ -145,6 +157,7 @@ export function AppLayout({
|
||||
companyProfiles = [],
|
||||
companyType,
|
||||
onCreateProfile,
|
||||
onReapplyProfile,
|
||||
children,
|
||||
}: AppLayoutProps) {
|
||||
const [mobileOpen, { toggle: toggleMobile }] = useDisclosure();
|
||||
@@ -178,36 +191,59 @@ export function AppLayout({
|
||||
const profileExists = (type: ServiceType) =>
|
||||
companyProfiles.some((p) => p.type === type);
|
||||
const addableServices = CUSTOMER_SERVICES.filter((t) => !profileExists(t));
|
||||
const canAddService = isCustomer && addableServices.length > 0;
|
||||
// Rejected services can't be re-added (they exist), so they'd otherwise be
|
||||
// invisible here — surface them for resubmission alongside addable ones.
|
||||
const rejectedServices = isCustomer
|
||||
? companyProfiles.filter(
|
||||
(p) =>
|
||||
p.status === "rejected" &&
|
||||
p.id &&
|
||||
CUSTOMER_SERVICES.includes(p.type as ServiceType),
|
||||
)
|
||||
: [];
|
||||
const canManageServices =
|
||||
isCustomer && (addableServices.length > 0 || rejectedServices.length > 0);
|
||||
|
||||
const [switching, setSwitching] = useState(false);
|
||||
const [createOpen, setCreateOpen] = useState(false);
|
||||
const [createTarget, setCreateTarget] = useState<ServiceType>("importer");
|
||||
// Non-null while resubmitting a rejected service; null while creating a new one.
|
||||
const [reapplyId, setReapplyId] = useState<string | null>(null);
|
||||
const [licenseFiles, setLicenseFiles] = useState<File[]>([]);
|
||||
const [createError, setCreateError] = useState<string | null>(null);
|
||||
|
||||
const handleAddService = (type: ServiceType) => {
|
||||
// Collect a business license, then create the profile.
|
||||
const openServiceModal = (
|
||||
type: ServiceType,
|
||||
profileId: string | null,
|
||||
) => {
|
||||
setCreateTarget(type);
|
||||
setReapplyId(profileId);
|
||||
setLicenseFiles([]);
|
||||
setCreateError(null);
|
||||
setCreateOpen(true);
|
||||
};
|
||||
|
||||
const handleAddService = (type: ServiceType) => openServiceModal(type, null);
|
||||
|
||||
const handleCreateConfirm = async () => {
|
||||
if (licenseFiles.length === 0) {
|
||||
const isReapply = reapplyId !== null;
|
||||
// A new profile needs its license up front; a resubmit may reuse the old one.
|
||||
if (!isReapply && licenseFiles.length === 0) {
|
||||
setCreateError("Please upload at least one business license file.");
|
||||
return;
|
||||
}
|
||||
setSwitching(true);
|
||||
setCreateError(null);
|
||||
try {
|
||||
const res = await onCreateProfile?.(createTarget, licenseFiles);
|
||||
const res = isReapply
|
||||
? await onReapplyProfile?.(reapplyId, licenseFiles)
|
||||
: await onCreateProfile?.(createTarget, licenseFiles);
|
||||
if (res && !res.success) {
|
||||
setCreateError(res.error?.message ?? "Failed to create profile");
|
||||
setCreateError(res.error?.message ?? "Failed to submit service");
|
||||
return;
|
||||
}
|
||||
setCreateOpen(false);
|
||||
setReapplyId(null);
|
||||
} finally {
|
||||
setSwitching(false);
|
||||
}
|
||||
@@ -287,10 +323,10 @@ export function AppLayout({
|
||||
|
||||
{/* Right: switch + search + bell + avatar */}
|
||||
<Group gap={10} wrap="nowrap" align="center">
|
||||
{/* Add a service (customer companies that don't yet have all three) */}
|
||||
{canAddService && (
|
||||
{/* Add a service, or resubmit a rejected one (customer companies) */}
|
||||
{canManageServices && (
|
||||
<Menu
|
||||
width={220}
|
||||
width={240}
|
||||
position="bottom-end"
|
||||
withinPortal
|
||||
shadow="md"
|
||||
@@ -313,16 +349,38 @@ export function AppLayout({
|
||||
</Button>
|
||||
</Menu.Target>
|
||||
<Menu.Dropdown>
|
||||
<Menu.Label>Add a service</Menu.Label>
|
||||
{addableServices.map((type) => (
|
||||
<Menu.Item
|
||||
key={type}
|
||||
onClick={() => handleAddService(type)}
|
||||
leftSection={<Plus size={15} strokeWidth={1.8} />}
|
||||
>
|
||||
{serviceLabel(type)}
|
||||
</Menu.Item>
|
||||
))}
|
||||
{addableServices.length > 0 && (
|
||||
<>
|
||||
<Menu.Label>Add a service</Menu.Label>
|
||||
{addableServices.map((type) => (
|
||||
<Menu.Item
|
||||
key={type}
|
||||
onClick={() => handleAddService(type)}
|
||||
leftSection={<Plus size={15} strokeWidth={1.8} />}
|
||||
>
|
||||
{serviceLabel(type)}
|
||||
</Menu.Item>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
{rejectedServices.length > 0 && (
|
||||
<>
|
||||
{addableServices.length > 0 && <Menu.Divider />}
|
||||
<Menu.Label>Rejected — resubmit</Menu.Label>
|
||||
{rejectedServices.map((p) => (
|
||||
<Menu.Item
|
||||
key={p.id}
|
||||
color="red"
|
||||
onClick={() =>
|
||||
openServiceModal(p.type as ServiceType, p.id!)
|
||||
}
|
||||
leftSection={<RefreshCw size={15} strokeWidth={1.8} />}
|
||||
>
|
||||
{serviceLabel(p.type as ServiceType)}
|
||||
</Menu.Item>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</Menu.Dropdown>
|
||||
</Menu>
|
||||
)}
|
||||
@@ -790,18 +848,28 @@ export function AppLayout({
|
||||
<Modal
|
||||
opened={createOpen}
|
||||
onClose={() => (switching ? undefined : setCreateOpen(false))}
|
||||
title={`Set up your ${serviceLabel(createTarget)} profile`}
|
||||
title={
|
||||
reapplyId
|
||||
? `Resubmit your ${serviceLabel(createTarget)} service`
|
||||
: `Set up your ${serviceLabel(createTarget)} profile`
|
||||
}
|
||||
centered
|
||||
radius="lg"
|
||||
>
|
||||
<Stack gap="md">
|
||||
<Text size="sm" c="dimmed">
|
||||
You don't have a {serviceLabel(createTarget).toLowerCase()} profile
|
||||
yet. Add your business license to create one and switch to{" "}
|
||||
{serviceLabel(createTarget).toLowerCase()}.
|
||||
{reapplyId
|
||||
? `Your ${serviceLabel(
|
||||
createTarget,
|
||||
).toLowerCase()} service was rejected. Replace the business license if needed, then resubmit for approval.`
|
||||
: `You don't have a ${serviceLabel(
|
||||
createTarget,
|
||||
).toLowerCase()} profile yet. Add your business license to create one and switch to ${serviceLabel(
|
||||
createTarget,
|
||||
).toLowerCase()}.`}
|
||||
</Text>
|
||||
<FileInput
|
||||
label="Business license"
|
||||
label={reapplyId ? "Business license (optional)" : "Business license"}
|
||||
multiple
|
||||
clearable
|
||||
accept="application/pdf,image/png,image/jpeg"
|
||||
@@ -824,7 +892,7 @@ export function AppLayout({
|
||||
onClick={handleCreateConfirm}
|
||||
loading={switching}
|
||||
>
|
||||
Create & switch
|
||||
{reapplyId ? "Resubmit" : "Create & switch"}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
|
||||
Reference in New Issue
Block a user