From 64a4bcbd5c5152914e5eed8d7674acd9b8ed8317 Mon Sep 17 00:00:00 2001 From: Hagernesh Date: Tue, 21 Jul 2026 08:52:51 +0000 Subject: [PATCH 1/2] feat(warehouse): make the ops KPI cards link to their detail MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dashboard counters were static — "Trucks on-site" showed a number with no way to open the list behind it, same for the others. KpiStrip now takes an optional href per item: a cell with one becomes a link (pointer, hover tint, aria-label) and a cell without one stays exactly as before, so every existing strip is unaffected. The warehouse ops strip wires each card to its detail: trucks on-site to the Trucks on Site page, received-today and pending-inspection to the inventory board filtered to RECEIVED, items-aging to the inventory board. Co-Authored-By: Claude Opus 4.8 --- .../src/components/page/KpiStrip.tsx | 21 ++++++++++++++++--- .../warehouses/WarehouseOpsKpiStrip.tsx | 7 +++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/apps/edr-freight-web/backoffice/src/components/page/KpiStrip.tsx b/apps/edr-freight-web/backoffice/src/components/page/KpiStrip.tsx index 4dfd39b0c..6b90d6c8a 100644 --- a/apps/edr-freight-web/backoffice/src/components/page/KpiStrip.tsx +++ b/apps/edr-freight-web/backoffice/src/components/page/KpiStrip.tsx @@ -1,6 +1,7 @@ import { Card, Skeleton, Text } from "@mantine/core"; import type { LucideIcon } from "lucide-react"; -import type { ReactNode } from "react"; +import type { ElementType, ReactNode } from "react"; +import { Link } from "react-router-dom"; import { cn } from "@/lib/utils"; @@ -22,6 +23,11 @@ export interface KpiItem { * (green up, red down, muted zero). E.g. today's count minus yesterday's. */ delta?: number; + /** + * Optional route the cell links to — its detail view. When set the cell + * becomes clickable (pointer, hover tint); when absent it stays static. + */ + href?: string; } export interface KpiStripProps { @@ -47,13 +53,22 @@ export function KpiStrip({ items, loading = false }: KpiStripProps) { {cells.map((item, index) => { const Icon = item.icon; const color = item.color ?? "edr-green"; + // A cell with an href becomes a link to its detail; without one it + // stays a plain div. Same layout classes either way. + const Cell: ElementType = item.href ? Link : "div"; + const linkProps = item.href + ? { to: item.href, "aria-label": `${item.label} — view detail` } + : {}; return ( -
)} className={cn( "flex flex-1 items-center gap-3 px-5 py-4", index > 0 && "border-t border-edr-border sm:border-l sm:border-t-0", + item.href && + "cursor-pointer no-underline transition-colors hover:bg-gray-50 focus-visible:bg-gray-50", )} > {Icon ? ( @@ -102,7 +117,7 @@ export function KpiStrip({ items, loading = false }: KpiStripProps) { {item.hint ? ` · ${item.hint}` : ""}
- + ); })} diff --git a/apps/edr-freight-web/backoffice/src/components/warehouses/WarehouseOpsKpiStrip.tsx b/apps/edr-freight-web/backoffice/src/components/warehouses/WarehouseOpsKpiStrip.tsx index 88a60bbb4..4e2fdcf15 100644 --- a/apps/edr-freight-web/backoffice/src/components/warehouses/WarehouseOpsKpiStrip.tsx +++ b/apps/edr-freight-web/backoffice/src/components/warehouses/WarehouseOpsKpiStrip.tsx @@ -23,18 +23,23 @@ export function WarehouseOpsKpiStrip() { delta: data != null ? data.receivedToday - data.receivedYesterday : undefined, hint: "vs yesterday", + // The received cargo itself, on the inventory board. + href: "/dashboard/warehouse-inventory?status=RECEIVED", }, { label: "Pending inspection", value: data?.pendingInspection ?? 0, icon: ClipboardCheck, color: "yellow", + // Received cargo still awaiting inspection lives in the RECEIVED bucket. + href: "/dashboard/warehouse-inventory?status=RECEIVED", }, { label: "Trucks on-site", value: data?.trucksOnSite ?? 0, icon: Truck, color: "blue", + href: "/dashboard/trucks-on-site", }, { label: "Items aging (>7d)", @@ -42,6 +47,8 @@ export function WarehouseOpsKpiStrip() { icon: AlertTriangle, color: (data?.itemsAging ?? 0) > 0 ? "red" : "edr-green", hint: "In warehouse over 7 days", + // No aging filter on the board; the inventory list is the landing. + href: "/dashboard/warehouse-inventory", }, ]} /> From 92a0cc197bef88aa10b9bf51de77ff42b06e9195 Mon Sep 17 00:00:00 2001 From: Hagernesh Date: Tue, 21 Jul 2026 09:13:50 +0000 Subject: [PATCH 2/2] feat(fleet): make the dashboard stat cards link to their detail MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The four headline cards — total vehicles, drivers, fuel spend, maintenance — were static numbers with no way through to the list behind them. Each now takes an optional href and, when set, wraps in a link to its detail page (vehicles, drivers, fuel purchases, maintenance). A card without an href stays exactly as before. The Card is wrapped rather than turned into a link so Mantine's Card typing stays clean. Co-Authored-By: Claude Opus 4.8 --- .../src/pages/fleet/FleetDashboard.tsx | 65 ++++++++++++------- 1 file changed, 42 insertions(+), 23 deletions(-) diff --git a/apps/edr-freight-web/backoffice/src/pages/fleet/FleetDashboard.tsx b/apps/edr-freight-web/backoffice/src/pages/fleet/FleetDashboard.tsx index 32341505a..beb16406a 100644 --- a/apps/edr-freight-web/backoffice/src/pages/fleet/FleetDashboard.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/fleet/FleetDashboard.tsx @@ -3,6 +3,7 @@ import { useQuery } from '@tanstack/react-query'; import { Card, Stack, Group, Grid, Text, ThemeIcon, Progress, Badge, Table, RingProgress, Container, Title, Box, Tabs } from '@mantine/core'; import { Truck, Fuel, Wrench, AlertCircle, Users, User, MapPin } from 'lucide-react'; import type { LucideIcon } from 'lucide-react'; +import { Link } from 'react-router-dom'; import Breadcrumbs from '@/components/ui/Breadcrumbs'; import { QUERY_KEYS } from '@/constants/QUERY_KEYS'; import { api } from '@/auth/http'; @@ -51,28 +52,46 @@ interface StatCardProps { value: string | number; color?: string; change?: number; + /** Detail route the card opens. When set the card is a link; otherwise static. */ + href?: string; } -const StatCard = ({ icon: Icon, label, value, color = 'edr-green', change }: StatCardProps) => ( - - - - - - - - - {label} - - - - {value} - - {change && 0 ? 'edr-green' : 'edr-red'} size="lg">{change > 0 ? '+' : ''}{change}%} +const StatCard = ({ icon: Icon, label, value, color = 'edr-green', change, href }: StatCardProps) => { + const card = ( + + + + + - - -); + + + {label} + + + + {value} + + {change && 0 ? 'edr-green' : 'edr-red'} size="lg">{change > 0 ? '+' : ''}{change}%} + + + + ); + + // Wrap in a link to the detail view rather than morphing the Card itself — + // keeps Mantine's Card typing clean. Static when no href. + return href ? ( + + {card} + + ) : ( + card + ); +}; export function FleetDashboard() { const { data: vehicles = [] } = useQuery({ @@ -160,16 +179,16 @@ export function FleetDashboard() { {/* Primary Metrics */} - + - + - + - +