import { useMemo, useState } from "react"; import { Check, ShieldCheck } from "lucide-react"; import { Stack, Group, Text, Badge, Button, Box, Modal, } from "@mantine/core"; import type { Freight } from "@edr/types"; import { formatContractApprovalProgress } from "@/features/contracts/contract-approval-progress"; import { SectionCard } from "@/components/bookings/detail/SectionCard"; import type { useContractMutations } from "@/hooks/contracts/useContracts"; type Mutations = ReturnType; interface ContractApprovalStepsCardProps { contract: Freight.IContract; mutations: Mutations; } /** Approval chain with inline approve on the next pending step. */ export function ContractApprovalStepsCard({ contract, mutations, }: ContractApprovalStepsCardProps) { const [confirmOpen, setConfirmOpen] = useState(false); const [pendingStep, setPendingStep] = useState(null); const steps = useMemo( () => [...(contract.approvalSteps ?? [])].sort( (a, b) => a.stepOrder - b.stepOrder, ), [contract.approvalSteps], ); const nextPending = steps.find((s) => s.status === "PENDING"); const summary = formatContractApprovalProgress(contract.status, steps); const openApprove = (step: Freight.IContractApprovalStep) => { setPendingStep(step); setConfirmOpen(true); }; const closeApprove = () => { setConfirmOpen(false); setPendingStep(null); }; const runApprove = () => { if (!pendingStep) return; mutations.approveStep.mutate( { stepId: pendingStep.id, requiredRole: pendingStep.requiredRole }, { onSuccess: () => closeApprove() }, ); }; const subtitle = summary.detail || (nextPending ? `Next: ${nextPending.requiredRole} ยท step ${nextPending.stepOrder}` : steps.length ? "All steps complete" : "Accept submission to begin"); return ( <> {steps.filter((s) => s.status === "APPROVED").length}/{steps.length} } > {subtitle} {steps.length === 0 ? ( Use Accept for approval in staff actions to instantiate steps. ) : ( {steps.map((step) => ( openApprove(step)} /> ))} )} You are about to approve the{" "} {pendingStep?.requiredRole} {" "} step for contract{" "} {contract.reference} . This action cannot be undone from this screen. ); } function StepRow({ step, isNext, isPending, onApprove, }: { step: Freight.IContractApprovalStep; isNext: boolean; isPending: boolean; onApprove: () => void; }) { const statusColor = step.status === "APPROVED" ? "edr-green" : step.status === "REJECTED" ? "red" : isNext ? "edr-green" : "gray"; return ( {step.stepOrder} {step.requiredRole} {step.note && ( {step.note} )} {isNext && step.status === "PENDING" && ( )} {step.status} ); }