diff --git a/apps/edr-freight-web/portal/src/App.tsx b/apps/edr-freight-web/portal/src/App.tsx index 647878972..139e74280 100644 --- a/apps/edr-freight-web/portal/src/App.tsx +++ b/apps/edr-freight-web/portal/src/App.tsx @@ -13,10 +13,12 @@ import { FileText, Home, Loader2, + User, } from "lucide-react"; import useAuth from "./hooks/useAuth"; +import ProfilePage from "./pages/ProfilePage"; import MyPortalPage from "./pages/MyPortalPage"; import EDRFreightLandingPage from "./pages/EDRFreightLandingPage"; import SignupPage from "./pages/accounts/SignupPage"; @@ -29,7 +31,6 @@ import BookingDetailPage from "./pages/bookings/BookingDetailPage"; import NewBookingPage from "./pages/bookings/NewBookingPage"; import TrackingPage from "./pages/tracking/TrackingPage"; import BillingPage from "./pages/billing/BillingPage"; -import DocumentsPage from "./pages/documents/DocumentsPage"; import { useEffect } from "react"; const sidebarItems: SidebarItem[] = [ @@ -37,7 +38,7 @@ const sidebarItems: SidebarItem[] = [ { label: "My Bookings", href: "/bookings", icon: }, { label: "Tracking", href: "/tracking", icon: }, { label: "Billing", href: "/billing", icon: }, - { label: "Documents", href: "/documents", icon: }, + { label: "Profile", href: "/profile", icon: }, ]; const App = () => { @@ -97,7 +98,7 @@ const App = () => { } /> } /> } /> - } /> + } /> } /> diff --git a/apps/edr-freight-web/portal/src/pages/ProfilePage.tsx b/apps/edr-freight-web/portal/src/pages/ProfilePage.tsx new file mode 100644 index 000000000..e0d364bc1 --- /dev/null +++ b/apps/edr-freight-web/portal/src/pages/ProfilePage.tsx @@ -0,0 +1,326 @@ +import { useMemo } from "react"; +import { + User, + Building2, + Phone, + Mail, + MapPin, + ShieldCheck, + Briefcase, + UserCheck, + Building, + Globe, + Fingerprint, + FileCheck, + Settings2, + ExternalLink, +} from "lucide-react"; +import useAuth from "@/hooks/useAuth"; +import { + Card, + CardHeader, + CardTitle, + CardDescription, + CardContent, + CardAction, + Badge, + Separator, + SmartFileInput, + Button, +} from "@edr/ui-common"; +import type { IFileUploadSetting } from "@edr/types/freight"; +import { cn } from "@/lib/utils"; + +export default function ProfilePage() { + const { user, customer, isPending } = useAuth(); + + const documentSettings = useMemo(() => ({ + id: "profile-docs", + code: "customer_documents", + label: "Customer Documents", + entity: "customer", + createdAt: new Date(), + updatedAt: new Date(), + fields: [ + { + id: "doc-tin", + settingId: "profile-docs", + fileKey: "tin_certificate", + fileLabel: "TIN Certificate", + isRequired: true, + isMultiple: false, + maxFiles: 1, + allowedExtensions: [".pdf", ".jpg", ".jpeg", ".png"], + maxSizeMb: 5, + order: 1, + createdAt: new Date(), + updatedAt: new Date(), + }, + { + id: "doc-license", + settingId: "profile-docs", + fileKey: "business_license", + fileLabel: "Business/Investment License", + isRequired: true, + isMultiple: false, + maxFiles: 1, + allowedExtensions: [".pdf", ".jpg", ".jpeg", ".png"], + maxSizeMb: 5, + order: 2, + createdAt: new Date(), + updatedAt: new Date(), + }, + { + id: "doc-reg", + settingId: "profile-docs", + fileKey: "registration_certificate", + fileLabel: "Business Registration Certificate", + isRequired: true, + isMultiple: false, + maxFiles: 1, + allowedExtensions: [".pdf", ".jpg", ".jpeg", ".png"], + maxSizeMb: 5, + order: 3, + createdAt: new Date(), + updatedAt: new Date(), + }, + { + id: "doc-id", + settingId: "profile-docs", + fileKey: "national_id", + fileLabel: "National ID", + isRequired: true, + isMultiple: false, + maxFiles: 1, + allowedExtensions: [".pdf", ".jpg", ".jpeg", ".png"], + maxSizeMb: 5, + order: 4, + createdAt: new Date(), + updatedAt: new Date(), + }, + { + id: "doc-poa", + settingId: "profile-docs", + fileKey: "power_of_attorney", + fileLabel: "Power of Attorney", + isRequired: false, + isMultiple: false, + maxFiles: 1, + allowedExtensions: [".pdf", ".jpg", ".jpeg", ".png"], + maxSizeMb: 5, + order: 5, + createdAt: new Date(), + updatedAt: new Date(), + }, + ], + }), []); + + if (isPending) { + return ( +
+
+
+ ); + } + + const displayName = user?.name?.en || user?.username || user?.email || "User"; + + return ( +
+
+ {/* Header Section */} +
+
+
+ +
+
+
+

+ {displayName} +

+ + Verified + +
+

+ + {customer?.companyName || "No Company Linked"} +

+
+
+
+ +
+
+ + + +
+ {/* Left Column - Personal & Company Info */} +
+
+ {/* Personal Details Card */} + + + + + Personal Details + + Your account contact information + + + + + + } label="Email Address" value={user?.email} /> + } label="Phone Number" value={user?.phoneNumber} /> + } label="Username" value={user?.username} /> + + + + {/* Company Details Card */} + + + + + Company Details + + Business registration information + + + } label="Location" value={customer?.companyLocation} /> + } label="Address" value={customer?.companyAddress} /> + } label="TIN Number" value={customer?.tinNumber} /> + } label="FAN Number" value={customer?.fanNumber} /> + + +
+ + {/* Personnel Card */} + + + + + Key Personnel + + Management and contact persons + + +
+

+ Contact Person +

+
+ + +
+
+
+

+ General Manager +

+
+ + + +
+
+
+
+ + {/* Power of Attorney Section (Conditional) */} + {customer?.poaName && ( + + + + + Power of Attorney + + Authorized representative details + + + + + + + + + )} +
+ + {/* Right Column - Documents */} +
+ + + + + Documents + + Manage required business documents + + + + + + + +
+ +
+ +

Secure Account

+

+ Your information is protected by enterprise-grade security. + Contact support for verified information updates. +

+
+ +
+
+
+
+
+
+
+ ); +} + +function InfoItem({ + icon, + label, + value, +}: { + icon?: React.ReactNode; + label: string; + value?: string | null; +}) { + return ( +
+ {icon && ( +
+ {icon} +
+ )} +
+

+ {label} +

+

+ {value || "—"} +

+
+
+ ); +} diff --git a/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage.tsx b/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage.tsx index 1ce8f76d5..8b153515d 100644 --- a/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage.tsx +++ b/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage.tsx @@ -1,22 +1,69 @@ -import { Link, useNavigate, useParams } from "react-router-dom"; +import { useNavigate, useParams } from "react-router-dom"; import { - ArrowLeft, - ArrowRight, Calendar, - Flag, MapPin, Package, StickyNote, - Trash2, Train, - User, Weight, + Ship, + Truck, + Anchor, + FileText, + ShieldCheck, + AlertTriangle, + Info, + Clock, + Layers, + CheckCircle2, + History, + ArrowRight, + ClipboardCheck, + CreditCard, + FileSignature, + PackageCheck, } from "lucide-react"; import Breadcrumbs from "@/components/Breadcrumbs"; -import DeleteBookingDialog from "./DeleteBookingDialog"; -import { getBookingById, deleteBooking, type BookingStatus } from "./bookings.mock"; -import { Button, Card } from "@edr/ui-common"; +import { getBookingById } from "./bookings.mock"; +import { + Card, + CardHeader, + CardTitle, + CardDescription, + CardContent, + Badge, + Separator, +} from "@edr/ui-common"; +import { cn } from "@/lib/utils"; + +// Grouping the 15 granular statuses into 6 logical progress stages for the UI tracker +const PROGRESS_STAGES = [ + { label: "Request", icon: FileText, statuses: ["DRAFT", "RFQ_SUBMITTED"] }, + { label: "Quotation", icon: ClipboardCheck, statuses: ["QUOTATION_SENT", "QUOTATION_APPROVED", "QUOTATION_REJECTED"] }, + { label: "Approval", icon: ShieldCheck, statuses: ["PENDING_APPROVAL", "APPROVED"] }, + { label: "Execution", icon: FileSignature, statuses: ["SIGNED_CUSTOMER", "FULLY_EXECUTED", "PAID"] }, + { label: "In Transit", icon: Train, statuses: ["IN_TRANSIT", "PENDING_CONSOLIDATION", "CONSOLIDATED"] }, + { label: "Complete", icon: PackageCheck, statuses: ["COMPLETED"] }, +]; + +const STATUS_MAP: Record = { + DRAFT: { title: "Drafting Request", description: "Booking is being prepared and has not been submitted.", color: "text-slate-500", stage: 0 }, + RFQ_SUBMITTED: { title: "RFQ Submitted", description: "Request for Quotation has been sent to the operations team.", color: "text-amber-600", stage: 0 }, + QUOTATION_SENT: { title: "Quotation Received", description: "EDR has sent a formal quotation for your review.", color: "text-sky-600", stage: 1 }, + QUOTATION_APPROVED: { title: "Quotation Approved", description: "You have accepted the quotation terms.", color: "text-emerald-600", stage: 1 }, + QUOTATION_REJECTED: { title: "Quotation Rejected", description: "The quotation was not accepted.", color: "text-red-600", stage: 1 }, + PENDING_APPROVAL: { title: "Internal Approval", description: "Booking is undergoing final administrative review.", color: "text-amber-600", stage: 2 }, + APPROVED: { title: "Booking Approved", description: "Request is fully approved and ready for execution.", color: "text-emerald-600", stage: 2 }, + SIGNED_CUSTOMER: { title: "Customer Signed", description: "Contract has been signed by the customer.", color: "text-sky-600", stage: 3 }, + FULLY_EXECUTED: { title: "Contract Executed", description: "All parties have signed. Operational setup in progress.", color: "text-indigo-600", stage: 3 }, + PAID: { title: "Payment Received", description: "Initial payments confirmed. Cargo ready for dispatch.", color: "text-emerald-600", stage: 3 }, + IN_TRANSIT: { title: "Cargo Moving", description: "Shipment is currently moving through the rail network.", color: "text-sky-600", stage: 4 }, + PENDING_CONSOLIDATION: { title: "Consolidation Node", description: "Cargo is waiting to be consolidated with other shipments.", color: "text-amber-500", stage: 4 }, + CONSOLIDATED: { title: "Load Consolidated", description: "Cargo has been successfully merged into a larger shipment.", color: "text-indigo-500", stage: 4 }, + COMPLETED: { title: "Service Complete", description: "Cargo delivered and service successfully terminated.", color: "text-emerald-600", stage: 5 }, + CANCELLED: { title: "Cancelled", description: "This booking process has been terminated.", color: "text-red-600", stage: -1 }, +}; export default function BookingDetailPage() { const { id } = useParams<{ id: string }>(); @@ -25,37 +72,29 @@ export default function BookingDetailPage() { if (!booking) { return ( -
-
- - -

- Booking not found -

-

- The booking you're looking for doesn't exist or has been removed. -

- - - Back to Bookings - -
-
+
+ +
+ +
+

+ Booking not found +

+
); } + // Normalize status to upper case for mapping + const normalizedStatus = (booking.status === "In Transit" ? "IN_TRANSIT" : booking.status === "Pending" ? "RFQ_SUBMITTED" : booking.status.toUpperCase()) as keyof typeof STATUS_MAP; + const statusConfig = STATUS_MAP[normalizedStatus] || STATUS_MAP.DRAFT; + const currentStageIndex = statusConfig.stage; + return ( -
-
+
+
+ + {/* Breadcrumbs Restored */} - -
-
-
- + {/* Compact Header Card */} + + +
+
+
-
-

- {booking.reference} -

-
- {booking.customer} - - {booking.requestedDate} - - +
+
+

+ {booking.reference} +

+ +
+
+ {booking.customer} + + + + {booking.requestedDate} +
- -
- - - { - deleteBooking(booking.id); - navigate("/bookings"); - }} - > - - -
-
+ - -
- } - /> -
- - + {/* Granular Status Lifecycle */} + + + + + Booking Status Lifecycle + + Track the journey from request to completion + + +
+ {/* Progress Line */} +
+
= 0 ? `${(currentStageIndex / (PROGRESS_STAGES.length - 1)) * 100}%` : '0%' }} + /> +
+ + {PROGRESS_STAGES.map((stage, idx) => { + const isCompleted = idx < currentStageIndex; + const isActive = idx === currentStageIndex; + + return ( +
+
+ {isCompleted ? : } +
+ + {stage.label} + +
+ ); + })}
- } - /> -
+ +
+
+ {normalizedStatus === "CANCELLED" ? : } +
+
+

+ {statusConfig.title} +

+

+ {statusConfig.description} +

+
+ {normalizedStatus !== "CANCELLED" && normalizedStatus !== "COMPLETED" && ( +
+
+

Est. Waiting

+

1-2 Working Days

+
+ +
+ )} +
+
- {booking.transportMode === "Multimodal" && - booking.legs && - booking.legs.length > 0 ? ( - -
- -

- Transport Legs -

- - {booking.legs.length} legs - -
-
- {booking.legs.map((leg, i) => ( -
-
-
- {i + 1} -
-
-

- Leg {i + 1} · {leg.mode} -

-

- {leg.from || "—"} - - {leg.to || "—"} -

+
+
+ {/* Route & Core Service Card */} + + + + + Route & Service + + + +
+ } + /> +
+
+ +
+ + Rail + +
+ } + /> +
+ +
+ } label="Service" value="Rail & Forwarding" /> + } label="Return" value="With Return" /> + } label="Customs" value="Enabled" /> +
+
+
+ + {/* Mile Services Card */} + + + + + Mile Services + + + +
+

+ First Mile +

+ +
+
+

+ Last Mile +

+

Not requested

+
+
+
+ + {/* Cargo Specifications Card */} + + + + + Cargo Specifications + + + +
+ } label="Category" value={booking.cargoType} /> + } label="Weight" value={`${booking.weightTons} Tons`} /> + } label="Shipping Line" value="MSC" /> +
+ + + +
+

Load Details

+
+ + + + + + + + + + + + + + + +
DescriptionUnitValue
Main Equipment20FT Container4 Units
- ))} -
- - ) : null} + + +
-
- - } - label="Customer" - value={booking.customer} - /> - } - label="Cargo Type" - value={booking.cargoType} - /> - } - label="Container" - value={`${booking.containerCount} × ${booking.containerType}`} - /> - } - label="Weight" - value={`${booking.weightTons} tons`} - /> - +
+ {/* Contract Card */} + + + + + Contract Info + + + + + + +
+ + Hazardous: No + + + Refrigerated: No + +
+
+
- - } - label="Transport Mode" - value={booking.transportMode} - /> - } - label="Requested Date" - value={booking.requestedDate} - /> - } - label="Priority" - value={booking.priority} - /> - - - -
- -

{booking.cargoDescription}

-
-
- - -
- -

{booking.specialInstructions}

-
-
+ {/* Notes Card */} + + + Additional Info + + +
+

Description

+

"{booking.cargoDescription}"

+
+ +
+

Instructions

+
+

+ + {booking.specialInstructions} +

+
+
+
+
+
@@ -233,68 +369,64 @@ function RouteEndpoint({ }) { return (
-
- {icon} +
+ {icon &&
{icon}
}
-
-

+

+

{label}

-

{station}

+

{station}

); } -function DetailCard({ - title, - children, -}: { - title: string; - children: React.ReactNode; +function InfoItem({ + icon, + label, + value +}: { + icon?: React.ReactNode; + label: string; + value?: string | number | null }) { return ( - -

{title}

-
{children}
-
- ); -} - -function DetailRow({ - icon, - label, - value, -}: { - icon: React.ReactNode; - label: string; - value: string; -}) { - return ( -
-
{icon}
-
-

{label}

-

{value}

+
+ {icon &&
{icon}
} +
+

{label}

+

{value || "—"}

); } -function StatusBadge({ status }: { status: BookingStatus }) { - const styles: Record = { - Pending: "bg-amber-100 text-amber-700", - Confirmed: "bg-sky-100 text-sky-700", - "In Transit": "bg-indigo-100 text-indigo-700", - Delivered: "bg-emerald-100 text-emerald-700", - Cancelled: "bg-red-100 text-red-700", +function StatusBadge({ status }: { status: string }) { + const statusColors: Record = { + DRAFT: "bg-slate-50 text-slate-700 border-slate-200", + RFQ_SUBMITTED: "bg-amber-50 text-amber-700 border-amber-200", + QUOTATION_SENT: "bg-sky-50 text-sky-700 border-sky-200", + QUOTATION_APPROVED: "bg-emerald-50 text-emerald-700 border-emerald-200", + QUOTATION_REJECTED: "bg-red-50 text-red-700 border-red-200", + PENDING_APPROVAL: "bg-amber-50 text-amber-700 border-amber-200", + APPROVED: "bg-emerald-50 text-emerald-700 border-emerald-200", + SIGNED_CUSTOMER: "bg-sky-50 text-sky-700 border-sky-200", + FULLY_EXECUTED: "bg-indigo-50 text-indigo-700 border-indigo-200", + PAID: "bg-emerald-50 text-emerald-700 border-emerald-200", + IN_TRANSIT: "bg-sky-50 text-sky-700 border-sky-200", + COMPLETED: "bg-indigo-50 text-indigo-700 border-indigo-200", + CANCELLED: "bg-red-50 text-red-700 border-red-200", + PENDING_CONSOLIDATION: "bg-amber-50 text-amber-700 border-amber-200", + CONSOLIDATED: "bg-indigo-50 text-indigo-700 border-indigo-200", }; return ( - - {status} - + {status.replace(/_/g, ' ')} + ); } diff --git a/apps/edr-freight-web/portal/src/pages/documents/DeleteDocumentDialog.tsx b/apps/edr-freight-web/portal/src/pages/documents/DeleteDocumentDialog.tsx deleted file mode 100644 index 4bd53adc6..000000000 --- a/apps/edr-freight-web/portal/src/pages/documents/DeleteDocumentDialog.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import type { ReactNode } from "react"; - -import { - Dialog, - DialogClose, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, - DialogTrigger, -} from "@/components/ui/dialog"; - -import { Button } from "@/components/ui/button"; - -export interface DeleteDocumentDialogProps { - documentName: string; - onConfirm?: () => void; - children: ReactNode; -} - -export default function DeleteDocumentDialog({ - documentName, - onConfirm, - children, -}: DeleteDocumentDialogProps) { - return ( - - {children} - - - - - Delete document? - - - - This will permanently delete{" "} - {documentName}{" "} - and remove it from object storage. This action cannot be undone. - - - - - - - - - - - - - - - ); -} diff --git a/apps/edr-freight-web/portal/src/pages/documents/DocumentsPage.tsx b/apps/edr-freight-web/portal/src/pages/documents/DocumentsPage.tsx deleted file mode 100644 index 25c1b1dbe..000000000 --- a/apps/edr-freight-web/portal/src/pages/documents/DocumentsPage.tsx +++ /dev/null @@ -1,576 +0,0 @@ -import { useMemo, useState } from "react"; -import { - CheckCircle2, - Clock, - Download, - Eye, - File, - FileImage, - FileSpreadsheet, - FileText, - Filter, - HardDrive, - LayoutGrid, - List, - MoreHorizontal, - Pencil, - Plus, - Search, - Trash2, -} from "lucide-react"; - -import Breadcrumbs from "@/components/Breadcrumbs"; -import NewDocumentPage from "./NewDocumentPage"; -import DeleteDocumentDialog from "./DeleteDocumentDialog"; -import { - documents, - formatBytes, - type DocumentFormat, - type DocumentRecord, - type DocumentStatus, -} from "./documents.mock"; -import { - DataTable, - DataTableFooter, - type ColumnDef, - usePagination, - Button, - Card, - CardHeader, - CardTitle, - CardDescription, - CardContent, - Input, - DropdownMenu, - DropdownMenuTrigger, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuSeparator, -} from "@edr/ui-common"; - -type FilterValue = "All" | DocumentStatus; -type ViewMode = "grid" | "table"; - -const FILTERS: FilterValue[] = [ - "All", - "Draft", - "Pending Review", - "Approved", - "Rejected", - "Expired", -]; - -export default function DocumentsPage() { - const { pagination, setPagination } = usePagination({ pageSize: 10 }); - const [filter, setFilter] = useState("All"); - const [query, setQuery] = useState(""); - const [view, setView] = useState("table"); - - const filtered = useMemo(() => { - const q = query.trim().toLowerCase(); - return documents.filter((d) => { - if (filter !== "All" && d.status !== filter) return false; - if (!q) return true; - return ( - d.name.toLowerCase().includes(q) || - d.type.toLowerCase().includes(q) || - d.linkedReference.toLowerCase().includes(q) || - d.uploadedBy.toLowerCase().includes(q) - ); - }); - }, [filter, query]); - - const total = filtered.length; - const pageCount = Math.ceil(total / pagination.pageSize); - const start = pagination.pageIndex * pagination.pageSize; - const end = Math.min(start + pagination.pageSize, total); - - const paginatedData = useMemo( - () => filtered.slice(start, end), - [start, end, filtered], - ); - - const totalSize = documents.reduce((sum, d) => sum + d.sizeBytes, 0); - const approvedCount = documents.filter((d) => d.status === "Approved").length; - const pendingCount = documents.filter( - (d) => d.status === "Pending Review", - ).length; - - const columns: ColumnDef[] = [ - { - id: "document", - header: "Document", - cell: ({ row }) => { - const doc = row.original; - return ( -
-
- -
-
-

{doc.name}

-

- {doc.format} · By {doc.uploadedBy} -

-
-
- ); - }, - }, - { - accessorKey: "type", - header: "Type", - }, - { - id: "linkedTo", - header: "Linked To", - cell: ({ row }) => ( -
-

{row.original.linkedReference}

-

{row.original.linkedType}

-
- ), - }, - { - id: "size", - header: "Size", - cell: ({ row }) => ( - - {formatBytes(row.original.sizeBytes)} - - ), - }, - { - accessorKey: "uploadedAt", - header: "Uploaded", - }, - { - accessorKey: "status", - header: "Status", - cell: ({ row }) => , - }, - { - id: "actions", - size: 40, - cell: ({ row }) => { - const doc = row.original; - return ( -
e.stopPropagation()} - > - - - - - - - - Preview - - - - Download - - - - e.preventDefault()}> - - Edit - - - - - e.preventDefault()} - variant="destructive" - > - - Delete - - - - -
- ); - }, - }, - ]; - - return ( -
-
- - - -
-

- Documents -

-

- Manage freight documents linked to bookings, consignments, and - invoices. -

-
- -
-
- - { - setQuery(e.target.value); - setPagination({ - pageIndex: 0, - pageSize: pagination.pageSize, - }); - }} - placeholder="Search documents..." - className="pl-8!" - /> -
- - - - -
-
- -
- - -
-

Total Documents

-

- {documents.length} -

-
-
- -
-
-
- - - -
-

Approved

-

- {approvedCount} -

-
-
- -
-
-
- - - -
-

Pending Review

-

- {pendingCount} -

-
-
- -
-
-
- - - -
-

Storage Used

-

- {formatBytes(totalSize)} -

-
-
- -
-
-
-
- - -
-
- {FILTERS.map((f) => { - const isActive = f === filter; - const count = - f === "All" - ? documents.length - : documents.filter((d) => d.status === f).length; - return ( - - ); - })} -
- -
- setView("grid")} - label="Grid view" - > - - Grid - - setView("table")} - label="Table view" - > - - Table - -
-
-
- - {paginatedData.length === 0 ? ( - -

- No documents match your filters. -

-
- ) : view === "grid" ? ( -
- {paginatedData.map((doc) => ( - - ))} -
- ) : ( - - -
- Document Library - - All freight documents stored in the system. - -
- -
- - - { }} - pagination={{ - pageIndex: pagination.pageIndex, - pageSize: pagination.pageSize, - pageCount: pageCount, - totalCount: total, - }} - tableOptions={{ - state: { pagination }, - onPaginationChange: setPagination, - }} - containerClassName="border-b shadow-none" - footer={DataTableFooter} - /> - -
- )} -
-
- ); -} - -function ViewToggleButton({ - active, - onClick, - label, - children, -}: { - active: boolean; - onClick: () => void; - label: string; - children: React.ReactNode; -}) { - return ( - - ); -} - -function FormatIcon({ format }: { format: DocumentFormat }) { - if (format === "PDF") return ; - if (format === "DOCX") return ; - if (format === "XLSX") return ; - if (format === "PNG" || format === "JPG") return ; - return ; -} - -function DocumentCard({ doc }: { doc: DocumentRecord }) { - return ( - -
-
-
-
- -
-
-

- {doc.name} -

-

{doc.type}

-
-
- -
- -
- - - - -
- -

By {doc.uploadedBy}

- -
e.stopPropagation()} - > - - - - - - - - Preview - - - - Download - - - - e.preventDefault()}> - - Edit - - - - - e.preventDefault()} - variant="destructive" - > - - Delete - - - - -
-
-
- ); -} - -function MetaRow({ label, value }: { label: string; value: string }) { - return ( -
-

{label}

-

{value}

-
- ); -} - -function StatusBadge({ status }: { status: DocumentStatus }) { - const styles: Record = { - Draft: "bg-slate-100 text-slate-600", - "Pending Review": "bg-amber-100 text-amber-700", - Approved: "bg-emerald-100 text-emerald-700", - Rejected: "bg-red-100 text-red-700", - Expired: "bg-slate-200 text-slate-700", - }; - - return ( - - {status} - - ); -} diff --git a/apps/edr-freight-web/portal/src/pages/documents/NewDocumentPage.tsx b/apps/edr-freight-web/portal/src/pages/documents/NewDocumentPage.tsx deleted file mode 100644 index 96ae42703..000000000 --- a/apps/edr-freight-web/portal/src/pages/documents/NewDocumentPage.tsx +++ /dev/null @@ -1,242 +0,0 @@ -import { useState, type ReactNode } from "react"; -import { FileUp, Hash } from "lucide-react"; - -import { - Dialog, - DialogContent, - DialogDescription, - DialogHeader, - DialogTitle, - DialogTrigger, -} from "@/components/ui/dialog"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; -import { Button } from "@/components/ui/button"; -import { Textarea } from "@/components/ui/textarea"; - -import { bookings } from "../bookings/bookings.mock"; -import { consignments } from "../consignments/consignments.mock"; -import { customers } from "../customers/customers.mock"; -import type { - DocumentLinkType, - DocumentStatus, - DocumentType, -} from "./documents.mock"; - -export interface DocumentFormData { - name?: string; - type?: DocumentType; - status?: DocumentStatus; - linkedType?: DocumentLinkType; - linkedReference?: string; - notes?: string; -} - -export interface NewDocumentPageProps { - mode?: "create" | "edit"; - document?: DocumentFormData; - children?: ReactNode; -} - -const selectClass = - "flex h-10 w-full rounded-md border border-slate-200 bg-white px-3 py-2 text-sm text-slate-700 shadow-xs outline-none transition hover:border-slate-300 focus:border-[#10B981]/50 focus:ring-2 focus:ring-[#10B981]/20"; - -export default function NewDocumentPage({ - mode = "create", - document, - children, -}: NewDocumentPageProps = {}) { - const isEdit = mode === "edit"; - const title = isEdit ? "Edit Document" : "Upload Document"; - const description = isEdit - ? "Update document metadata." - : "Upload a freight document and link it to a booking, consignment, or invoice."; - const submitLabel = isEdit ? "Save Changes" : "Upload"; - - const [linkedType, setLinkedType] = useState( - document?.linkedType ?? "Booking", - ); - const [fileName, setFileName] = useState(""); - - const referenceOptions = (() => { - if (linkedType === "Booking") { - return bookings.map((b) => ({ - value: b.reference, - label: `${b.reference} — ${b.customer}`, - })); - } - if (linkedType === "Consignment") { - return consignments.map((c) => ({ - value: c.trackingNumber, - label: `${c.trackingNumber} — ${c.customer}`, - })); - } - if (linkedType === "Customer") { - return customers.map((c) => ({ - value: c.company, - label: c.company, - })); - } - return [] as Array<{ value: string; label: string }>; - })(); - - return ( - - - {children ?? } - - - - - {title} - {description} - - -
- {/* File picker — drop zone */} - {!isEdit ? ( -
- - -
- ) : null} - - {/* Document Name */} -
- -
- - -
-
- - {/* Document Type */} -
- - -
- - {/* Linked To */} -
- - -
- - {/* Reference */} -
- - {referenceOptions.length > 0 ? ( - - ) : ( - - )} -
- - {/* Status */} -
- - -
- - {/* Notes */} -
- -