From b37e0cf7a7b55793ead1a96a2fd63340c7ea02f4 Mon Sep 17 00:00:00 2001 From: Nathnael Date: Wed, 10 Jun 2026 10:43:48 +0000 Subject: [PATCH] style: rebuild the my bookings page with new ui --- .../portal/src/pages/bookings/MyBookings.tsx | 541 ++++++++++-------- 1 file changed, 289 insertions(+), 252 deletions(-) diff --git a/apps/edr-freight-web/portal/src/pages/bookings/MyBookings.tsx b/apps/edr-freight-web/portal/src/pages/bookings/MyBookings.tsx index 3c863eb3d..e689fa230 100644 --- a/apps/edr-freight-web/portal/src/pages/bookings/MyBookings.tsx +++ b/apps/edr-freight-web/portal/src/pages/bookings/MyBookings.tsx @@ -1,32 +1,19 @@ -import { useMemo, useState } from "react"; +import { useMemo } from "react"; import { Link, useNavigate } from "react-router-dom"; import { useQuery } from "@tanstack/react-query"; import { ActionIcon, - Badge, Box, Button, Card, Group, Menu, - SimpleGrid, Stack, Text, - TextInput, ThemeIcon, Title, } from "@mantine/core"; -import { - ArrowRight, - Clock, - Eye, - Filter, - MoreHorizontal, - Package, - Plus, - Search, - Truck, -} from "lucide-react"; +import { ArrowUpDown, Download, Filter, MoreVertical, Package, Plus } from "lucide-react"; import { api } from "@/services/api"; import type { Freight } from "@edr/types"; @@ -37,63 +24,188 @@ import { usePagination, } from "@edr/ui-common"; +// ── Status badge ────────────────────────────────────────────────────────────── + +const STATUS_CONFIG: Record = { + DRAFT: { bg: "#F1F4F7", dot: "#94A3B8", color: "#475569", label: "Draft" }, + REVIEWING: { bg: "#E9F0F8", dot: "#3B6FB0", color: "#2E5B96", label: "Reviewing" }, + AWAITING_PAYMENT: { bg: "#FDF3E0", dot: "#F2A516", color: "#9A5B00", label: "Awaiting Payment" }, + CONFIRMED: { bg: "#ECF6F1", dot: "#0EA371", color: "#0A6F4D", label: "Confirmed" }, + IN_TRANSIT: { bg: "#ECF6F1", dot: "#0EA371", color: "#0A6F4D", label: "In Transit" }, + DELIVERED: { bg: "#E9F0F8", dot: "#3B6FB0", color: "#2E5B96", label: "Delivered" }, + CANCELLED: { bg: "#FBEAE7", dot: "#C0392B", color: "#C0392B", label: "Cancelled" }, +}; + +function StatusBadge({ status }: { status: string }) { + const cfg = STATUS_CONFIG[status] ?? { + bg: "#F1F4F7", + dot: "#94A3B8", + color: "#475569", + label: status.replace(/_/g, " "), + }; + return ( + + + + {cfg.label} + + + ); +} + +// ── Context-sensitive action button ─────────────────────────────────────────── + +function PrimaryAction({ + status, + id, + onNavigate, +}: { + status: string; + id: string; + onNavigate: (path: string) => void; +}) { + if (status === "DRAFT") { + return ( + + ); + } + if (status === "AWAITING_PAYMENT") { + return ( + + ); + } + if (status === "IN_TRANSIT") { + return ( + + ); + } + return ( + + ); +} + +// ── Column header label ─────────────────────────────────────────────────────── + +function ColHeader({ label }: { label: string }) { + return ( + + {label} + + ); +} + +const hMeta = { headerClassName: "bg-[#F4F7FA]" }; + +// ── Main component ──────────────────────────────────────────────────────────── + export default function MyBookings() { const navigate = useNavigate(); const { pagination, setPagination } = usePagination({ pageSize: 10 }); - const [searchTerm, setSearchTerm] = useState(""); - - const { data, isLoading, isError } = useQuery( - api.bookings.list.queryOptions(), - ); + const { data, isLoading, isError } = useQuery(api.bookings.list.queryOptions()); const bookings = data?.items ?? []; - const filteredData = useMemo(() => { - return bookings.filter((b) => { - const term = searchTerm.toLowerCase(); - return ( - b.reference.toLowerCase().includes(term) || - (b.originYard?.label ?? b.originYard?.code ?? "").toLowerCase().includes(term) || - (b.destinationYard?.label ?? b.destinationYard?.code ?? "").toLowerCase().includes(term) || - b.status.toLowerCase().includes(term) - ); - }); - }, [bookings, searchTerm]); - - const total = filteredData.length; + const total = bookings.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(() => filteredData.slice(start, end), [filteredData, start, end]); - - const activeCount = useMemo(() => { - return bookings.filter( - (b) => b.status === "CONFIRMED" || b.status === "IN_TRANSIT", - ).length; - }, [bookings]); - - const pendingCount = useMemo(() => { - return bookings.filter((b) => b.status === "DRAFT").length; - }, [bookings]); + const paginatedData = useMemo(() => bookings.slice(start, end), [bookings, start, end]); const columns: ColumnDef[] = [ { - accessorKey: "reference", - header: "Reference", + id: "booking", + size: 244, + meta: hMeta, + header: () => , cell: ({ row }) => { - const booking = row.original; + const b = row.original; + const cargoLabel = + b.freightType === "BULK" + ? "Bulk Cargo" + : b.freightType === "BREAK_BULK" + ? "Break Bulk" + : "Cargo"; return ( - - - + + + - - - {booking.reference} + + + {b.reference} - - {booking.scheduledDate ?? booking.createdAt} + + {cargoLabel} @@ -102,72 +214,80 @@ export default function MyBookings() { }, { id: "route", - header: "Route", - cell: ({ row }) => ( - - - {row.original.originYard?.label ?? row.original.originYard?.code ?? "—"} - - - - {row.original.destinationYard?.label ?? row.original.destinationYard?.code ?? "—"} - - - ), - }, - { - id: "cargo", - header: "Cargo", + size: 196, + meta: hMeta, + header: () => , cell: ({ row }) => { const b = row.original; - const containerCount = b.containers?.reduce((sum, c) => sum + c.qty, 0) ?? 0; - const containerType = b.containers?.[0]?.type ?? null; + const origin = b.originYard?.label ?? b.originYard?.code ?? "—"; + const dest = b.destinationYard?.label ?? b.destinationYard?.code ?? "—"; + const sub = b.scheduledDate ?? b.createdAt ?? ""; return ( - - {b.freightType === "BULK" ? "Bulk" : "Break Bulk"} - - - {containerType && containerCount > 0 ? `${containerCount} × ${containerType} · ` : ""} - {b.cargoTotalWeightVgm}t + + {origin} → {dest} + {sub && ( + + {sub} + + )} ); }, }, { - id: "transportMode", - header: "Transport", - cell: ({ row }) => ( - - {row.original.serviceType === "RAIL_AND_FORWARDING" ? "Rail & Forwarding" : "Rail"} - - ), - }, - { - accessorKey: "status", - header: "Status", + id: "status", + size: 190, + meta: hMeta, + header: () => , cell: ({ row }) => , }, + { + id: "amount", + size: 140, + meta: hMeta, + header: () => , + cell: ({ row }) => { + const b = row.original as Freight.IBooking & { totalAmount?: number; amount?: number }; + const amount = b.totalAmount ?? b.amount ?? null; + if (!amount) { + return ( + + — + + ); + } + return ( + + ETB {amount.toLocaleString()} + + ); + }, + }, { id: "actions", - size: 40, + meta: hMeta, + header: () => null, cell: ({ row }) => { const booking = row.original; return ( - e.stopPropagation()}> + e.stopPropagation()}> + - - + + - } - onClick={() => navigate(`/bookings/${booking.id}`)} - > - View + navigate(`/bookings/${booking.id}`)}> + View Details @@ -180,111 +300,85 @@ export default function MyBookings() { const dataTableStatus = isLoading ? "loading" : isError ? "error" : "success"; return ( - - - {/* ── Header band ─────────────────────────────────────────── */} - - - - - - My Bookings - - - View and manage your freight booking requests. - - - - - setSearchTerm(e.currentTarget.value)} - leftSection={} - radius="md" - className="w-full sm:w-80" - styles={{ input: { background: "white" } }} - /> - - + + + {/* ── Page header ─────────────────────────────────────────────── */} + + + + Bookings + + + Manage every cargo booking — from draft to delivery. + + + + + - + - {/* ── Stat cards ──────────────────────────────────────────── */} - - } - gradient="from-emerald-500 to-emerald-700 shadow-emerald-500/30" - /> - } - gradient="from-sky-500 to-blue-600 shadow-sky-500/30" - /> - } - gradient="from-amber-400 to-orange-500 shadow-amber-500/30" - /> - - - {/* ── Table ───────────────────────────────────────────────── */} - - - - Recent Requests - - A list of your recent freight bookings and their statuses. - - - + + {/* Empty state */} {total === 0 && dataTableStatus === "success" ? ( - + - - No bookings found + + No bookings yet - - {searchTerm - ? "No bookings match your current search filter." - : "You haven't requested any bookings yet."} + + You haven't made any booking requests yet. Create your first one to get started. - {!searchTerm && ( - - )} + ) : ( )} @@ -311,60 +405,3 @@ export default function MyBookings() { ); } - -function StatCard({ - label, - value, - icon, - gradient, -}: { - label: string; - value: number; - icon: React.ReactNode; - gradient: string; -}) { - return ( - - - - - {label} - - - {value} - - - - {icon} - - - - ); -} - -function StatusBadge({ status }: { status: string }) { - const colorMap: Record = { - DRAFT: "amber", - CONFIRMED: "edr-green", - IN_TRANSIT: "blue", - DELIVERED: "edr-green", - CANCELLED: "red", - }; - - return ( - - {status.replace(/_/g, " ")} - - ); -}