import type { ReactNode } from "react"; import { Badge, Stack, Tabs, Text } from "@mantine/core"; import { AlertTriangle, FileText, Receipt, Share2, ShieldAlert } from "lucide-react"; import type { Freight } from "@edr/types"; import { useAuth } from "@/auth/useAuth"; import { FREIGHT_PERMS, hasPermission } from "@/lib/permissions"; import { SectionCard } from "@/components/bookings/detail/SectionCard"; import { AssignRiskCard } from "@/components/contracts/gl-actions/AssignRiskCard"; import { IncidentReportCard } from "@/components/contracts/gl-actions/IncidentReportCard"; import { ClearanceUploadedDocumentsPanel } from "@/components/contracts/ClearanceUploadedDocumentsPanel"; import { ClearanceChargesTab } from "@/components/contracts/ClearanceChargesTab"; import { GlExchangePanel } from "@/components/contracts/GlExchangePanel"; export interface ClearanceOpsTabsProps { bookingId: string | undefined; milestones?: Freight.IClearanceMilestone[]; /** When false, only the clearance tab content is rendered (no tab bar). */ showOpsTabs?: boolean; clearanceTab: ReactNode; /** Phased customs workflow files — enables the Uploaded documents tab. */ workflowFiles?: Freight.ClearanceWorkflowFile[]; showWorkflowFilesTab?: boolean; /** * Booking or contract id whose GL Ethiopia ↔ GL Djibouti document exchange * belongs on this page. Undefined hides the tab; it is also hidden from staff * who hold neither desk's clearance-actions permission. */ exchangeEntityId?: string; tradeDirection?: string; onViewFile?: (file: { name: string; url: string }) => void; onDownloadFile?: (file: { id: string; name: string }) => void; } function findMilestone( milestones: Freight.IClearanceMilestone[] | undefined, code: string, ): Freight.IClearanceMilestone | undefined { return milestones?.find((m) => m.milestoneCode === code); } /** * Document Clearance detail layout: primary clearance workflow plus optional * uploaded documents, post-booking risk assignment, and incident reporting tabs. */ export function ClearanceOpsTabs({ bookingId, milestones, showOpsTabs = true, clearanceTab, workflowFiles = [], showWorkflowFilesTab = false, exchangeEntityId, tradeDirection = "IMPORT", onViewFile, onDownloadFile, }: ClearanceOpsTabsProps) { const riskMs = findMilestone(milestones, "RISK_ASSIGNED"); const hasOps = Boolean(bookingId); const isExport = tradeDirection === "EXPORT"; const uploadedDocCount = workflowFiles.filter((f) => { if (!f.file) return false; if (isExport) return f.category !== "duty"; return true; }).length; const showDocuments = showWorkflowFilesTab && Boolean(onViewFile); const { user } = useAuth(); const showExchange = Boolean(exchangeEntityId) && (hasPermission(user, FREIGHT_PERMS.contracts.clearanceEtActions) || hasPermission(user, FREIGHT_PERMS.contracts.clearanceDjActions)); // Post-finalization customer billing. This layout is only rendered on the ET // clearance pages — the DJ page (GlClearanceDetailPage) mounts its own tab. const showCharges = Boolean(bookingId) && Boolean(onViewFile) && hasPermission(user, FREIGHT_PERMS.contracts.clearanceEtActions); // Risk assignment + incident reporting hit bookings:operations endpoints. const canOps = hasPermission(user, FREIGHT_PERMS.bookings.operations); const hasTabs = (showOpsTabs && hasOps) || showDocuments || showExchange; if (!hasTabs) { return <>{clearanceTab}; } return ( Clearance {showDocuments ? ( } rightSection={ uploadedDocCount > 0 ? ( {uploadedDocCount} ) : undefined } > Uploaded documents ) : null} {showExchange ? ( }> Document exchange ) : null} {showCharges ? ( }> Customer charges ) : null} {showOpsTabs && canOps && riskMs ? ( }> Risk assignment ) : null} {showOpsTabs && canOps && bookingId ? ( }> Incidents ) : null} {clearanceTab} {showDocuments ? ( ) : null} {showExchange ? ( ) : null} {showCharges ? ( ) : null} {showOpsTabs && canOps && riskMs && bookingId ? ( ) : null} {showOpsTabs && canOps && bookingId ? ( Log container or seal issues discovered during clearance handling. ) : null} ); }