mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
Implement intercity document handling and rejection notes for contracts
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { useMemo, useState } from "react";
|
||||
import { AlertTriangle, Check, ShieldCheck, X } from "lucide-react";
|
||||
import { Check, ShieldCheck, X } from "lucide-react";
|
||||
import {
|
||||
Stack,
|
||||
Group,
|
||||
@@ -51,6 +51,11 @@ export function ContractApprovalStepsCard({
|
||||
|
||||
const nextPending = steps.find((s) => s.status === "PENDING");
|
||||
const summary = formatContractApprovalProgress(contract.status, steps);
|
||||
// The card also renders read-only trails (e.g. a REJECTED contract) — only
|
||||
// offer approve/reject while the backend accepts step actions.
|
||||
const actionable =
|
||||
contract.status === "PENDING_APPROVAL" ||
|
||||
contract.status === "APPROVED_PENDING_SIGNATURE";
|
||||
|
||||
// Approvers review a live preview of the document; there is no PDF to
|
||||
// generate first — the final approval is what produces it.
|
||||
@@ -160,7 +165,7 @@ export function ContractApprovalStepsCard({
|
||||
<StepRow
|
||||
key={step.id}
|
||||
step={step}
|
||||
isNext={nextPending?.id === step.id}
|
||||
isNext={actionable && nextPending?.id === step.id}
|
||||
isPending={
|
||||
mutations.approveStep.isPending ||
|
||||
mutations.rejectStep.isPending
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useNavigate, useParams, useSearchParams } from "react-router-dom";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import {
|
||||
AlertTriangle,
|
||||
ArrowLeft,
|
||||
ArrowRight,
|
||||
Box as BoxIcon,
|
||||
@@ -22,6 +23,7 @@ import {
|
||||
Users,
|
||||
} from "lucide-react";
|
||||
import {
|
||||
Alert,
|
||||
Badge,
|
||||
Box,
|
||||
Button,
|
||||
@@ -264,7 +266,8 @@ export default function ContractRequestDetailPage() {
|
||||
const showApprovalCard =
|
||||
contract.status === "PENDING_APPROVAL" ||
|
||||
contract.status === "APPROVED" ||
|
||||
contract.status === "APPROVED_PENDING_SIGNATURE";
|
||||
contract.status === "APPROVED_PENDING_SIGNATURE" ||
|
||||
contract.status === "REJECTED";
|
||||
|
||||
const showClearanceTab = CLEARANCE_REVIEW_STATUSES.includes(contract.status);
|
||||
const phasedCustoms =
|
||||
@@ -428,6 +431,32 @@ export default function ContractRequestDetailPage() {
|
||||
description={statusMeta.description}
|
||||
/>
|
||||
|
||||
{contract.status === "REJECTED" && contract.latestRejectionNote ? (
|
||||
<Alert
|
||||
color="red"
|
||||
radius="md"
|
||||
icon={<AlertTriangle size={18} />}
|
||||
title="Rejection reason"
|
||||
>
|
||||
<Text size="sm" style={{ whiteSpace: "pre-wrap" }}>
|
||||
{contract.latestRejectionNote}
|
||||
</Text>
|
||||
</Alert>
|
||||
) : null}
|
||||
|
||||
{contract.status === "PENDING_APPROVAL" && contract.latestSendBackNote ? (
|
||||
<Alert
|
||||
color="orange"
|
||||
radius="md"
|
||||
icon={<AlertTriangle size={18} />}
|
||||
title="Sent back in the approval chain"
|
||||
>
|
||||
<Text size="sm" style={{ whiteSpace: "pre-wrap" }}>
|
||||
{contract.latestSendBackNote}
|
||||
</Text>
|
||||
</Alert>
|
||||
) : null}
|
||||
|
||||
<Tabs
|
||||
value={currentTab}
|
||||
onChange={(v) => setTab(v ?? "details")}
|
||||
|
||||
@@ -409,6 +409,10 @@ export default function ContractDetailPage() {
|
||||
|
||||
const canSign = contract.status === "CONTRACT_READY";
|
||||
const customsPath = contract.customsClearingEnabled;
|
||||
// Intercity (DOMESTIC) has no customs — the document gate collects the
|
||||
// admin-configured intercity set, reviewed by Operations.
|
||||
const isIntercity = contract.tradeDirection === "DOMESTIC";
|
||||
const docNoun = isIntercity ? "intercity documents" : "clearance documents";
|
||||
// Only the NON-customs (Path A) customer books himself — once the contract is
|
||||
// executed after self-clearance. Customs (Path B) bookings are created by
|
||||
// Global Logistics on the customer's behalf, so the customer gets no booking
|
||||
@@ -586,8 +590,8 @@ export default function ContractDetailPage() {
|
||||
onClick={clearanceModal.open}
|
||||
>
|
||||
{contract.status === "CLEARANCE_UNDER_REVIEW"
|
||||
? "Manage clearance documents"
|
||||
: "Upload clearance documents"}
|
||||
? `Manage ${docNoun}`
|
||||
: `Upload ${docNoun}`}
|
||||
</Button>
|
||||
)}
|
||||
</Group>
|
||||
@@ -852,7 +856,9 @@ export default function ContractDetailPage() {
|
||||
<Text fw={700} fz={15} c={INK}>
|
||||
{customsPath
|
||||
? "Customs clearance shipment"
|
||||
: "Customs clearance required"}
|
||||
: isIntercity
|
||||
? "Intercity documents required"
|
||||
: "Customs clearance required"}
|
||||
</Text>
|
||||
</Group>
|
||||
<Text fz={13} c="dimmed">
|
||||
@@ -862,11 +868,17 @@ export default function ContractDetailPage() {
|
||||
: contract.status === "CLEARANCE_UNDER_REVIEW"
|
||||
? "Global Logistics is reviewing your clearance documents. Re-upload any queried documents to proceed."
|
||||
: "Your documents are cleared. You can now create a shipment booking under this contract."
|
||||
: contract.status === "AWAITING_CLEARANCE_DOCUMENTS"
|
||||
? "This service does not include EDR customs clearance. Clear the cargo yourself and upload your clearance documents so the Operations team can review them before you book a shipment."
|
||||
: contract.status === "CLEARANCE_UNDER_REVIEW"
|
||||
? "The Operations team is reviewing your clearance documents. Re-upload any queried documents to proceed."
|
||||
: "Your clearance documents are approved. You can now create a shipment booking under this contract."}
|
||||
: isIntercity
|
||||
? contract.status === "AWAITING_CLEARANCE_DOCUMENTS"
|
||||
? "Upload the required intercity documents so the Operations team can review them before you book a shipment."
|
||||
: contract.status === "CLEARANCE_UNDER_REVIEW"
|
||||
? "The Operations team is reviewing your intercity documents. Re-upload any queried documents to proceed."
|
||||
: "Your intercity documents are approved. You can now create a shipment booking under this contract."
|
||||
: contract.status === "AWAITING_CLEARANCE_DOCUMENTS"
|
||||
? "This service does not include EDR customs clearance. Clear the cargo yourself and upload your clearance documents so the Operations team can review them before you book a shipment."
|
||||
: contract.status === "CLEARANCE_UNDER_REVIEW"
|
||||
? "The Operations team is reviewing your clearance documents. Re-upload any queried documents to proceed."
|
||||
: "Your clearance documents are approved. You can now create a shipment booking under this contract."}
|
||||
</Text>
|
||||
{contract.status !== "CLEARANCE_READY_FOR_BOOKING" && (
|
||||
<Button
|
||||
@@ -1581,7 +1593,7 @@ export default function ContractDetailPage() {
|
||||
onClose={clearanceModal.close}
|
||||
title={
|
||||
<Text fw={700} fz={16}>
|
||||
Clearance documents
|
||||
{isIntercity ? "Intercity documents" : "Clearance documents"}
|
||||
</Text>
|
||||
}
|
||||
size="xl"
|
||||
|
||||
@@ -254,6 +254,14 @@ export function ContractStepBanner({ contract }: ContractStepBannerProps) {
|
||||
</Text>
|
||||
</Group>
|
||||
)}
|
||||
{contract.status === "REJECTED" && contract.latestRejectionNote && (
|
||||
<Text
|
||||
fz={12.5}
|
||||
style={{ color: "#B42318", whiteSpace: "pre-wrap", width: "100%" }}
|
||||
>
|
||||
Reason: {contract.latestRejectionNote}
|
||||
</Text>
|
||||
)}
|
||||
{expirySoon && (
|
||||
<Group gap={6} wrap="nowrap" align="center">
|
||||
<AlertTriangle size={14} color="#9A6700" />
|
||||
|
||||
Reference in New Issue
Block a user