mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 05:18:11 +00:00
Implement intercity document handling and rejection notes for contracts
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useMemo, useState } from "react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import {
|
||||
Alert,
|
||||
Badge,
|
||||
@@ -72,6 +72,7 @@ export type ClearanceViewLike = Pick<
|
||||
| "linkedBookingId"
|
||||
| "riskLevel"
|
||||
| "riskAssignedAt"
|
||||
| "riskHistory"
|
||||
| "secondDuty"
|
||||
| "importReleaseGranted"
|
||||
> & { operationReady?: boolean };
|
||||
@@ -1040,11 +1041,28 @@ function RiskStep({
|
||||
done: boolean;
|
||||
onChanged?: () => void;
|
||||
}) {
|
||||
const [level, setLevel] = useState<string>("GREEN");
|
||||
const assigned = done || Boolean(clearance.riskLevel);
|
||||
// Duty is advised off the risk level, so once that is done the decision is
|
||||
// final. Until then a mis-assigned level must stay correctable — the server
|
||||
// overwrites the milestone metadata on reassignment. Mirrors AssignRiskCard.
|
||||
const locked = isMilestoneDone(clearance.milestones, "DUTY_TAXES_ADVISED");
|
||||
|
||||
const [level, setLevel] = useState<string>(clearance.riskLevel ?? "GREEN");
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
if (done || clearance.riskLevel) {
|
||||
return (
|
||||
// The clearance view loads (and refetches after a reassignment) after first
|
||||
// render, so mirror the persisted level onto the control whenever it changes —
|
||||
// otherwise reopening the step offers GREEN whatever is actually assigned.
|
||||
useEffect(() => {
|
||||
if (clearance.riskLevel) setLevel(clearance.riskLevel);
|
||||
}, [clearance.riskLevel]);
|
||||
|
||||
// Only the decisions before the current one — the badge above already states
|
||||
// the level in force, so repeating it as a trail entry reads as a duplicate.
|
||||
const priorDecisions = (clearance.riskHistory ?? []).slice(0, -1);
|
||||
|
||||
const assignedSummary = assigned ? (
|
||||
<Stack gap={6}>
|
||||
<Group gap="sm">
|
||||
<Badge
|
||||
color={RISK_LEVEL_COLOR[clearance.riskLevel ?? ""] ?? "gray"}
|
||||
@@ -1061,12 +1079,35 @@ function RiskStep({
|
||||
. The customer can see this level.
|
||||
</Text>
|
||||
</Group>
|
||||
);
|
||||
{priorDecisions.length > 0 ? (
|
||||
<Stack gap={2} pl="xs">
|
||||
<Text size="xs" c="dimmed" fw={600}>
|
||||
Previously
|
||||
</Text>
|
||||
{priorDecisions.map((entry, index) => (
|
||||
<Text key={`${entry.assignedAt}-${index}`} size="xs" c="dimmed">
|
||||
{entry.level}
|
||||
{" · "}
|
||||
{new Date(entry.assignedAt).toLocaleString()}
|
||||
{entry.assignedBy ? ` · ${entry.assignedBy}` : ""}
|
||||
{entry.note ? ` · ${entry.note}` : ""}
|
||||
</Text>
|
||||
))}
|
||||
</Stack>
|
||||
) : null}
|
||||
</Stack>
|
||||
) : null;
|
||||
|
||||
// Assigned and final: the badge is all that is left to show.
|
||||
if (assigned && (locked || !canAct || !bookingId)) {
|
||||
return assignedSummary;
|
||||
}
|
||||
|
||||
// Customs cannot rate cargo still under transit — the server rejects the
|
||||
// assignment until the T1 is closed, so do not offer the control yet.
|
||||
if (!clearance.t1?.closed) {
|
||||
// assignment until the T1 is closed, so do not offer the control yet. Skipped
|
||||
// once a level exists: risk cannot have been assigned without a closed T1, so
|
||||
// a still-open T1 here is stale data and must not hide the assigned badge.
|
||||
if (!assigned && !clearance.t1?.closed) {
|
||||
return (
|
||||
<StepStatus
|
||||
done={false}
|
||||
@@ -1088,6 +1129,7 @@ function RiskStep({
|
||||
|
||||
return (
|
||||
<Stack gap="sm">
|
||||
{assignedSummary}
|
||||
<SegmentedControl
|
||||
fullWidth
|
||||
value={level}
|
||||
@@ -1100,19 +1142,24 @@ function RiskStep({
|
||||
/>
|
||||
<Group justify="space-between">
|
||||
<Text size="xs" c="dimmed">
|
||||
The customer sees the assigned risk level.
|
||||
{assigned
|
||||
? "Correctable until duty is advised. The customer sees the assigned risk level."
|
||||
: "The customer sees the assigned risk level."}
|
||||
</Text>
|
||||
<Button
|
||||
size="compact-sm"
|
||||
color="edr-green"
|
||||
loading={loading}
|
||||
disabled={assigned && level === clearance.riskLevel}
|
||||
onClick={async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
await contractsService.assignRisk(bookingId, {
|
||||
riskLevel: level as Freight.CustomsRiskLevel,
|
||||
});
|
||||
toast.success("Customs risk assigned");
|
||||
toast.success(
|
||||
assigned ? "Customs risk reassigned" : "Customs risk assigned",
|
||||
);
|
||||
onChanged?.();
|
||||
} catch (e) {
|
||||
toast.error(e instanceof Error ? e.message : "Failed");
|
||||
@@ -1121,7 +1168,7 @@ function RiskStep({
|
||||
}
|
||||
}}
|
||||
>
|
||||
Assign risk
|
||||
{assigned ? "Reassign risk" : "Assign risk"}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
|
||||
Reference in New Issue
Block a user