Wire CommandPalette/Provider into the frontend layout and add Cmd+K hint in the SiteHeader. Register context-aware commands for banking, helpdesk, mission RSVP, locker, shipments, and market pages. Also removes the locker search's duplicate Cmd+K listener (now handled globally), renames its button to 'Search Locker', and cleans up a flappy page parenthetical.
113 lines
3.2 KiB
TypeScript
113 lines
3.2 KiB
TypeScript
"use client";
|
|
|
|
import { useMemo, useState } from "react";
|
|
import type { Shipment } from "@/payload-types";
|
|
import { ShipmentCard } from "@/components/frontend/logistics/ShipmentCard";
|
|
import { Button } from "@/components/ui/button";
|
|
import { usePageCommands } from "@/components/command-palette/CommandPaletteContext";
|
|
import {
|
|
CheckCircleIcon,
|
|
ClockIcon,
|
|
ListFilterIcon,
|
|
TruckIcon,
|
|
XCircleIcon,
|
|
} from "lucide-react";
|
|
|
|
const statusFilters = [
|
|
{ label: "All", value: null },
|
|
{ label: "Active", value: "active" },
|
|
{ label: "Completed", value: "completed" },
|
|
{ label: "Cancelled", value: "cancelled" },
|
|
{ label: "Stranded", value: "stranded" },
|
|
];
|
|
|
|
const activeStatuses = ["dispatched", "in_transit", "pending"];
|
|
|
|
interface ShipmentsListProps {
|
|
shipments: Shipment[];
|
|
}
|
|
|
|
export function ShipmentsList({ shipments }: ShipmentsListProps) {
|
|
const [filter, setFilter] = useState<string | null>(null);
|
|
|
|
usePageCommands([
|
|
{
|
|
id: "shipments:filter-all",
|
|
group: "Shipments",
|
|
label: "Filter — All Shipments",
|
|
icon: ListFilterIcon,
|
|
keywords: ["filter", "all", "shipments"],
|
|
onSelect: () => setFilter(null),
|
|
},
|
|
{
|
|
id: "shipments:filter-active",
|
|
group: "Shipments",
|
|
label: "Filter — Active Shipments",
|
|
icon: ClockIcon,
|
|
keywords: ["filter", "active", "in transit", "pending"],
|
|
onSelect: () => setFilter("active"),
|
|
},
|
|
{
|
|
id: "shipments:filter-completed",
|
|
group: "Shipments",
|
|
label: "Filter — Completed Shipments",
|
|
icon: CheckCircleIcon,
|
|
keywords: ["filter", "completed", "arrived", "done"],
|
|
onSelect: () => setFilter("completed"),
|
|
},
|
|
{
|
|
id: "shipments:filter-cancelled",
|
|
group: "Shipments",
|
|
label: "Filter — Cancelled Shipments",
|
|
icon: XCircleIcon,
|
|
keywords: ["filter", "cancelled", "failed"],
|
|
onSelect: () => setFilter("cancelled"),
|
|
},
|
|
{
|
|
id: "shipments:filter-stranded",
|
|
group: "Shipments",
|
|
label: "Filter — Stranded Shipments",
|
|
icon: TruckIcon,
|
|
keywords: ["filter", "stranded", "stuck"],
|
|
onSelect: () => setFilter("stranded"),
|
|
},
|
|
]);
|
|
|
|
const filtered = useMemo(() => {
|
|
if (!filter) return shipments;
|
|
if (filter === "active") {
|
|
return shipments.filter((s) => activeStatuses.includes(s.status));
|
|
}
|
|
return shipments.filter((s) => s.status === filter);
|
|
}, [shipments, filter]);
|
|
|
|
return (
|
|
<div className="flex flex-col gap-4">
|
|
<div className="flex flex-wrap gap-1.5">
|
|
{statusFilters.map((f) => (
|
|
<Button
|
|
key={f.value ?? "all"}
|
|
variant={filter === f.value ? "default" : "outline"}
|
|
size="sm"
|
|
className="h-7 text-xs"
|
|
onClick={() => setFilter(f.value)}
|
|
>
|
|
{f.label}
|
|
</Button>
|
|
))}
|
|
</div>
|
|
|
|
{filtered.length === 0 ? (
|
|
<div className="flex flex-col items-center justify-center py-12 text-muted-foreground">
|
|
<p className="text-sm">No shipments found.</p>
|
|
</div>
|
|
) : (
|
|
<div className="flex flex-col gap-2">
|
|
{filtered.map((shipment) => (
|
|
<ShipmentCard key={shipment.id} shipment={shipment} />
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|