mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 08:48:11 +00:00
enhance contract review and editing experience
This commit is contained in:
@@ -7,13 +7,16 @@ import {
|
||||
Calendar,
|
||||
CalendarClock,
|
||||
FileText,
|
||||
Files,
|
||||
Flame,
|
||||
LayoutGrid,
|
||||
Package,
|
||||
Receipt,
|
||||
RefreshCw,
|
||||
Route as RouteIcon,
|
||||
ShieldCheck,
|
||||
Snowflake,
|
||||
Users,
|
||||
} from "lucide-react";
|
||||
import {
|
||||
Badge,
|
||||
@@ -31,6 +34,8 @@ import {
|
||||
Title,
|
||||
} from "@mantine/core";
|
||||
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
import "@/components/overview/overview.css";
|
||||
import { PageContainer } from "@/components/page";
|
||||
import Breadcrumbs from "@/components/ui/Breadcrumbs";
|
||||
@@ -41,11 +46,18 @@ import { ContractWorkflowStepper } from "@/components/contracts/ContractWorkflow
|
||||
import { ContractActionsToolbar } from "@/components/contracts/ContractActionsToolbar";
|
||||
import { ContractApprovalStepsCard } from "@/components/contracts/ContractApprovalStepsCard";
|
||||
import { ContractClearanceReviewSection } from "@/components/contracts/ContractClearanceReviewSection";
|
||||
import {
|
||||
ContractCustomerCard,
|
||||
ContractDocumentsCard,
|
||||
} from "@/components/contracts/detail/ContractDetailTabCards";
|
||||
import { getContractStatusMeta } from "@/features/contracts/contract-status.config";
|
||||
import { useFileViewer } from "@/hooks/useFileViewer";
|
||||
import {
|
||||
useContractDetail,
|
||||
useContractMutations,
|
||||
} from "@/hooks/contracts/useContracts";
|
||||
import { downloadBookingFile } from "@/services/files.service";
|
||||
import type { Freight } from "@edr/types";
|
||||
|
||||
// Clearance phase — staff can still ACT (approve / query / finalize).
|
||||
const CLEARANCE_ACTIVE_STATUSES = [
|
||||
@@ -94,7 +106,8 @@ export default function ContractRequestDetailPage() {
|
||||
} = useContractDetail(id);
|
||||
const mutations = useContractMutations(id ?? "");
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const activeTab = searchParams.get("tab") === "clearance" ? "clearance" : "details";
|
||||
const { view, viewer } = useFileViewer();
|
||||
const requestedTab = searchParams.get("tab");
|
||||
const setTab = (tab: string) =>
|
||||
setSearchParams(
|
||||
(prev) => {
|
||||
@@ -106,6 +119,23 @@ export default function ContractRequestDetailPage() {
|
||||
{ replace: true },
|
||||
);
|
||||
|
||||
const handleViewFile = (file: NonNullable<Freight.IContract["files"]>[number]) =>
|
||||
view({
|
||||
name: file.name,
|
||||
url: file.signedUrl ?? file.url,
|
||||
mimeType: file.mimeType,
|
||||
});
|
||||
|
||||
const handleDownloadFile = async (
|
||||
file: NonNullable<Freight.IContract["files"]>[number],
|
||||
) => {
|
||||
try {
|
||||
await downloadBookingFile(file.id, file.name);
|
||||
} catch {
|
||||
toast.error("Could not download file.");
|
||||
}
|
||||
};
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<PageContainer>
|
||||
@@ -176,9 +206,17 @@ export default function ContractRequestDetailPage() {
|
||||
const clearanceReadOnly = CLEARANCE_DONE_STATUSES.includes(contract.status);
|
||||
// Path A (no customs) → Operations reviews; Path B (customs) → GL reviews.
|
||||
const selfClear = !contract.customsClearingEnabled;
|
||||
// If the tab param points at clearance but the contract isn't in a clearance
|
||||
// phase, fall back to details so we never show an empty tab.
|
||||
const currentTab = activeTab === "clearance" && showClearanceTab ? "clearance" : "details";
|
||||
const files = contract.files ?? [];
|
||||
// Resolve the active tab from the URL, falling back to details when the
|
||||
// requested tab isn't available for this contract (e.g. clearance pre-phase).
|
||||
const currentTab =
|
||||
requestedTab === "documents"
|
||||
? "documents"
|
||||
: requestedTab === "customer"
|
||||
? "customer"
|
||||
: requestedTab === "clearance" && showClearanceTab
|
||||
? "clearance"
|
||||
: "details";
|
||||
|
||||
const customerLabel = contract.isGovernment
|
||||
? (contract.governmentInstitution ?? "Government")
|
||||
@@ -264,27 +302,43 @@ export default function ContractRequestDetailPage() {
|
||||
description={statusMeta.description}
|
||||
/>
|
||||
|
||||
{showClearanceTab && (
|
||||
<Tabs
|
||||
value={currentTab}
|
||||
onChange={(v) => setTab(v ?? "details")}
|
||||
variant="pills"
|
||||
color="edr-green"
|
||||
classNames={{ list: "ov-tablist", tab: "ov-tab" }}
|
||||
>
|
||||
<Tabs.List>
|
||||
<Tabs.Tab value="details" leftSection={<FileText size={16} />}>
|
||||
Details
|
||||
</Tabs.Tab>
|
||||
<Tabs
|
||||
value={currentTab}
|
||||
onChange={(v) => setTab(v ?? "details")}
|
||||
variant="pills"
|
||||
color="edr-green"
|
||||
classNames={{ list: "ov-tablist", tab: "ov-tab" }}
|
||||
>
|
||||
<Tabs.List>
|
||||
<Tabs.Tab value="details" leftSection={<LayoutGrid size={16} />}>
|
||||
Details
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
value="documents"
|
||||
leftSection={<Files size={16} />}
|
||||
rightSection={
|
||||
files.length > 0 ? (
|
||||
<Badge size="xs" variant="light" color="gray" radius="sm">
|
||||
{files.length}
|
||||
</Badge>
|
||||
) : null
|
||||
}
|
||||
>
|
||||
Documents
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab value="customer" leftSection={<Users size={16} />}>
|
||||
Customer
|
||||
</Tabs.Tab>
|
||||
{showClearanceTab && (
|
||||
<Tabs.Tab
|
||||
value="clearance"
|
||||
leftSection={<ShieldCheck size={16} />}
|
||||
>
|
||||
Clearance Review
|
||||
</Tabs.Tab>
|
||||
</Tabs.List>
|
||||
</Tabs>
|
||||
)}
|
||||
)}
|
||||
</Tabs.List>
|
||||
</Tabs>
|
||||
|
||||
<Grid gap="lg">
|
||||
{/* LEFT — primary content */}
|
||||
@@ -296,6 +350,14 @@ export default function ContractRequestDetailPage() {
|
||||
readOnly={clearanceReadOnly}
|
||||
onChanged={() => refetch()}
|
||||
/>
|
||||
) : currentTab === "documents" ? (
|
||||
<ContractDocumentsCard
|
||||
files={files}
|
||||
onView={handleViewFile}
|
||||
onDownload={handleDownloadFile}
|
||||
/>
|
||||
) : currentTab === "customer" ? (
|
||||
<ContractCustomerCard contract={contract} />
|
||||
) : (
|
||||
<Stack gap="lg">
|
||||
<SectionCard icon={RouteIcon} title="Routes">
|
||||
@@ -453,6 +515,8 @@ export default function ContractRequestDetailPage() {
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</Stack>
|
||||
|
||||
{viewer}
|
||||
</PageContainer>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user