mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-07 16:19:01 +00:00
* feat(vendors): support company roster workflows * fix(vendors): address roster review feedback * test(vendors): preserve draft on roster load failure * fix(vendors): preserve current roster selection
130 lines
4 KiB
TypeScript
130 lines
4 KiB
TypeScript
import type { ReactNode } from "react";
|
|
import { useNavigate } from "react-router";
|
|
import { Alert, Box, Button, CircularProgress, Paper, Stack, Typography } from "@mui/material";
|
|
import { VendorPortalTokenPanel } from "./vendor-portal-token-panel";
|
|
import { VendorRosterConflictAlert } from "./vendor-roster-conflict-alert";
|
|
import { VendorRosterFormFields } from "./vendor-roster-form-fields";
|
|
import { VendorRosterLoadErrorAlert } from "./vendor-roster-load-error";
|
|
import { useVendorRosterForm } from "./use-vendor-roster-form";
|
|
|
|
interface VendorRosterPageProps {
|
|
vendorId?: string;
|
|
companyId?: string;
|
|
}
|
|
|
|
function PageShell({
|
|
title,
|
|
subtitle,
|
|
children,
|
|
}: {
|
|
title: string;
|
|
subtitle: string;
|
|
children: ReactNode;
|
|
}) {
|
|
return (
|
|
<Box className="mx-auto max-w-4xl p-4">
|
|
<Paper variant="outlined" className="p-6">
|
|
<Stack spacing={4}>
|
|
<Box>
|
|
<Typography variant="h5" component="h1">
|
|
{title}
|
|
</Typography>
|
|
<Typography variant="body2" sx={{ color: "text.secondary", mt: 0.5 }}>
|
|
{subtitle}
|
|
</Typography>
|
|
</Box>
|
|
{children}
|
|
</Stack>
|
|
</Paper>
|
|
</Box>
|
|
);
|
|
}
|
|
|
|
export default function VendorRosterPage({ vendorId, companyId }: VendorRosterPageProps) {
|
|
const navigate = useNavigate();
|
|
const isEdit = vendorId !== undefined || companyId !== undefined;
|
|
const form = useVendorRosterForm({
|
|
mode: isEdit ? "update" : "create",
|
|
vendorId,
|
|
companyId,
|
|
onSuccess: () => navigate("/vendors"),
|
|
});
|
|
|
|
if (isEdit && form.isLoading) {
|
|
return (
|
|
<Box className="flex justify-center p-10">
|
|
<CircularProgress />
|
|
</Box>
|
|
);
|
|
}
|
|
|
|
if (isEdit && form.isError) {
|
|
return (
|
|
<PageShell title="Vendor company" subtitle="Edit the company and its technicians.">
|
|
<Alert severity="error">Failed to load vendor company. Try again.</Alert>
|
|
<Stack direction="row" sx={{ justifyContent: "flex-end" }}>
|
|
<Button variant="outlined" onClick={() => navigate("/vendors")}>
|
|
Back to vendors
|
|
</Button>
|
|
</Stack>
|
|
</PageShell>
|
|
);
|
|
}
|
|
|
|
if (isEdit && !form.roster) {
|
|
return null;
|
|
}
|
|
|
|
const title = isEdit ? "Edit Vendor Company" : "Add Vendor Company";
|
|
const subtitle = isEdit
|
|
? "Update company details and technicians in one save."
|
|
: "Register a company and add zero, one, or multiple technicians.";
|
|
const companySelectionProps = isEdit
|
|
? {}
|
|
: {
|
|
companies: form.companies,
|
|
selectedCompanyId: form.selectedCompanyId,
|
|
onSelectCompany: form.selectCompany,
|
|
onClearSelectedCompany: form.clearSelectedCompany,
|
|
};
|
|
|
|
return (
|
|
<PageShell title={title} subtitle={subtitle}>
|
|
<Box component="form" onSubmit={form.handleSubmit(form.submit)}>
|
|
<Stack spacing={3}>
|
|
{form.loadErrorMessage !== null && (
|
|
<VendorRosterLoadErrorAlert message={form.loadErrorMessage} onRetry={form.retryLoad} />
|
|
)}
|
|
|
|
{form.conflict !== null && (
|
|
<VendorRosterConflictAlert conflict={form.conflict} onReload={form.reload} />
|
|
)}
|
|
|
|
<VendorRosterFormFields
|
|
control={form.control}
|
|
errors={form.errors}
|
|
tradeOptions={form.trades}
|
|
{...companySelectionProps}
|
|
/>
|
|
|
|
{isEdit && vendorId !== undefined && vendorId !== "" && (
|
|
<VendorPortalTokenPanel vendorId={vendorId} />
|
|
)}
|
|
|
|
<Stack direction="row" spacing={2} sx={{ justifyContent: "flex-end" }}>
|
|
<Button
|
|
variant="outlined"
|
|
onClick={() => navigate("/vendors")}
|
|
disabled={form.isSaving}
|
|
>
|
|
Cancel
|
|
</Button>
|
|
<Button type="submit" variant="contained" disabled={form.isSaving || !form.isValid}>
|
|
{form.isSaving ? "Saving..." : isEdit ? "Save" : "Create"}
|
|
</Button>
|
|
</Stack>
|
|
</Stack>
|
|
</Box>
|
|
</PageShell>
|
|
);
|
|
}
|