
3D Extruded Buildings & Urban Footprints
Real-world 3D building extrusions with dynamic sunlight shadows, terrain DEM elevations, and pitch/bearing camera controls.
E-commerce and SaaS order fulfillment admin table: header with export CSV and new order action, 4 KPI summary metric cards, search and multi-status filter toolbar with date range selector, selectable order rows with customer avatars and status badges, slide-over order details drawer with line items breakdown and tracking info, bulk action bar, and pagination.
Also available for Vue ->$pnpm dlx shadcn@latest add https://uipkge.dev/r/react/order-management-page.json$npx shadcn@latest add https://uipkge.dev/r/react/order-management-page.json$yarn dlx shadcn@latest add https://uipkge.dev/r/react/order-management-page.json$bunx shadcn@latest add https://uipkge.dev/r/react/order-management-page.jsonnpx shadcn@latest add @uipkge-react/order-management-pageInstalls to:components/blocks/Type aliases exported from this item's source. Use these to shape the data you pass in.
OrderIteminterface OrderItem {
name: string
sku: string
quantity: number
price: number
}CustomerInfointerface CustomerInfo {
name: string
email: string
avatar?: string
address: string
city: string
state: string
zip: string
}Orderinterface Order {
id: string
orderNumber: string
date: string
customer: CustomerInfo
items: OrderItem[]
status: OrderStatus
paymentMethod: string
subtotal: number
shipping: number
tax: number
total: number
carrier?: string
trackingNumber?: string
}'use client'
import * as React from 'react'
import {
ArrowUpRight,
Calendar,
ChevronDown,
Clock,
DollarSign,
Download,
Eye,
FileText,
MoreHorizontal,
PackageCheck,
Plus,
RotateCcw,
Search,
ShoppingCart,
TrendingUp,
Truck,
} from 'lucide-react'
import { cn } from '@/lib/utils'
import { Avatar, AvatarFallback } from '@/components/ui/avatar'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Card, CardContent } from '@/components/ui/card'
import { Checkbox } from '@/components/ui/checkbox'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
import { Input } from '@/components/ui/input'
import {
Sheet,
SheetClose,
SheetContent,
SheetDescription,
SheetFooter,
SheetHeader,
SheetTitle,
} from '@/components/ui/sheet'
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
export type OrderStatus = 'paid' | 'processing' | 'shipped' | 'refunded'
export interface OrderItem {
name: string
sku: string
quantity: number
price: number
}
export interface CustomerInfo {
name: string
email: string
avatar?: string
address: string
city: string
state: string
zip: string
}
export interface Order {
id: string
orderNumber: string
date: string
customer: CustomerInfo
items: OrderItem[]
status: OrderStatus
paymentMethod: string
subtotal: number
shipping: number
tax: number
total: number
carrier?: string
trackingNumber?: string
}
const orders: Order[] = [
{
id: 'ord-1',
orderNumber: '#ORD-8492',
date: 'Oct 24, 2026',
customer: {
name: 'Sarah Chen',
email: '[email protected]',
address: '742 Evergreen Terrace',
city: 'Springfield',
state: 'OR',
zip: '97477',
},
items: [
{ name: 'Ergonomic Mechanical Keyboard', sku: 'KB-ERGO-01', quantity: 1, price: 189 },
{ name: 'Desk Mat Pro (Midnight)', sku: 'MAT-PRO-BLK', quantity: 1, price: 39 },
],
status: 'processing',
paymentMethod: 'Mastercard •••• 8492',
subtotal: 228,
shipping: 0,
tax: 18.24,
total: 246.24,
carrier: 'FedEx Priority',
trackingNumber: 'FX-9281034-US',
},
{
id: 'ord-2',
orderNumber: '#ORD-8491',
date: 'Oct 24, 2026',
customer: {
name: 'Marcus Vance',
email: '[email protected]',
address: '100 Enterprise Way, Suite 400',
city: 'San Francisco',
state: 'CA',
zip: '94105',
},
items: [
{ name: 'UltraWide Monitor 34"', sku: 'MON-34-UW', quantity: 1, price: 699 },
{ name: 'Dual Monitor Arm Mount', sku: 'ACC-ARM-02', quantity: 1, price: 129 },
{ name: 'Braided DisplayPort Cable 2m', sku: 'CAB-DP-02', quantity: 1, price: 24 },
],
status: 'shipped',
paymentMethod: 'Visa •••• 4242',
subtotal: 852,
shipping: 0,
tax: 68.16,
total: 920.16,
carrier: 'UPS Ground',
trackingNumber: '1Z999AA10123456784',
},
{
id: 'ord-3',
orderNumber: '#ORD-8490',
date: 'Oct 23, 2026',
customer: {
name: 'Elena Rostova',
email: '[email protected]',
address: '450 Innovation Parkway',
city: 'Austin',
state: 'TX',
zip: '78701',
},
items: [{ name: 'SaaS Enterprise Annual License', sku: 'LIC-ENT-YR', quantity: 1, price: 1200 }],
status: 'paid',
paymentMethod: 'Visa •••• 9104',
subtotal: 1200,
shipping: 0,
tax: 0,
total: 1200,
},
{
id: 'ord-4',
orderNumber: '#ORD-8489',
date: 'Oct 23, 2026',
customer: {
name: 'David Kim',
email: '[email protected]',
address: '88 King Street West',
city: 'Seattle',
state: 'WA',
zip: '98104',
},
items: [
{ name: 'USB-C Multiport Dock Pro', sku: 'HUB-USBC-8P', quantity: 2, price: 79 },
{ name: 'Thunderbolt 4 Braided Cable', sku: 'CAB-TB4-1M', quantity: 2, price: 29 },
],
status: 'shipped',
paymentMethod: 'Apple Pay',
subtotal: 216,
shipping: 0,
tax: 17.28,
total: 233.28,
carrier: 'DHL Express',
trackingNumber: 'DHL-8492019-EXP',
},
{
id: 'ord-5',
orderNumber: '#ORD-8488',
date: 'Oct 22, 2026',
customer: {
name: 'Olivia Taylor',
email: '[email protected]',
address: '12 Marina Boulevard',
city: 'Boston',
state: 'MA',
zip: '02210',
},
items: [{ name: 'Noise-Cancelling Studio Headphones', sku: 'AUD-NC-700', quantity: 1, price: 349 }],
status: 'refunded',
paymentMethod: 'Visa •••• 1128',
subtotal: 349,
shipping: 0,
tax: 27.92,
total: 376.92,
},
{
id: 'ord-6',
orderNumber: '#ORD-8487',
date: 'Oct 22, 2026',
customer: {
name: 'Liam Johansson',
email: '[email protected]',
address: '240 Queen Street',
city: 'New York',
state: 'NY',
zip: '10001',
},
items: [
{ name: 'Aluminum Laptop Riser', sku: 'STN-ALUM-SLV', quantity: 1, price: 65 },
{ name: 'Wireless Precision Mouse', sku: 'MOU-WL-01', quantity: 1, price: 99 },
],
status: 'processing',
paymentMethod: 'Mastercard •••• 5531',
subtotal: 164,
shipping: 0,
tax: 13.12,
total: 177.12,
carrier: 'USPS Priority',
trackingNumber: '9400100000000000000000',
},
{
id: 'ord-7',
orderNumber: '#ORD-8486',
date: 'Oct 21, 2026',
customer: {
name: 'Aisha Patel',
email: '[email protected]',
address: '500 Central Avenue',
city: 'Chicago',
state: 'IL',
zip: '60601',
},
items: [{ name: 'Cloud Storage Tier 3 (500GB)', sku: 'SRV-STR-500', quantity: 1, price: 450 }],
status: 'paid',
paymentMethod: 'Amex •••• 3009',
subtotal: 450,
shipping: 0,
tax: 36,
total: 486,
},
{
id: 'ord-8',
orderNumber: '#ORD-8485',
date: 'Oct 20, 2026',
customer: {
name: 'Lucas Moreau',
email: '[email protected]',
address: '15 Rue de la Paix',
city: 'Boulder',
state: 'CO',
zip: '80302',
},
items: [
{ name: 'Broadcast Studio Microphone', sku: 'MIC-POD-01', quantity: 1, price: 229 },
{ name: 'Dual-Layer Pop Filter', sku: 'ACC-POP-01', quantity: 1, price: 25 },
{ name: 'Gold-Plated XLR Cable 3m', sku: 'CAB-XLR-03', quantity: 1, price: 19 },
],
status: 'shipped',
paymentMethod: 'Visa •••• 7712',
subtotal: 273,
shipping: 0,
tax: 21.84,
total: 294.84,
carrier: 'FedEx Ground',
trackingNumber: 'FX-7712093-US',
},
]
const statusBadgeConfig: Record<OrderStatus, { label: string; class: string; dot: string }> = {
paid: {
label: 'Paid',
class: 'bg-success/10 text-success border-success/20',
dot: 'bg-success',
},
processing: {
label: 'Processing',
class: 'bg-info/10 text-info border-info/20',
dot: 'bg-info',
},
shipped: {
label: 'Shipped',
class: 'bg-chart-1/10 text-chart-1 border-chart-1/20',
dot: 'bg-chart-1',
},
refunded: {
label: 'Refunded',
class: 'bg-destructive/10 text-destructive border-destructive/20',
dot: 'bg-destructive',
},
}
function formatCurrency(val: number) {
return val.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })
}
function initials(name: string) {
return name
.split(' ')
.map((p) => p[0])
.slice(0, 2)
.join('')
.toUpperCase()
}
export function OrderManagementPage({ className }: { className?: string }) {
const [search, setSearch] = React.useState('')
const [statusFilter, setStatusFilter] = React.useState<'all' | OrderStatus>('all')
const [selected, setSelected] = React.useState<Set<string>>(new Set())
const [selectedOrder, setSelectedOrder] = React.useState<Order | null>(null)
const [drawerOpen, setDrawerOpen] = React.useState(false)
const filteredOrders = React.useMemo(() => {
const query = search.trim().toLowerCase()
return orders.filter((order) => {
const matchesStatus = statusFilter === 'all' || order.status === statusFilter
const matchesQuery =
!query ||
order.orderNumber.toLowerCase().includes(query) ||
order.customer.name.toLowerCase().includes(query) ||
order.customer.email.toLowerCase().includes(query)
return matchesStatus && matchesQuery
})
}, [search, statusFilter])
const statusCounts = React.useMemo(() => {
const counts: Record<string, number> = {
all: orders.length,
paid: 0,
processing: 0,
shipped: 0,
refunded: 0,
}
for (const ord of orders) {
counts[ord.status] = (counts[ord.status] || 0) + 1
}
return counts
}, [])
const statusOptions = [
{ value: 'all' as const, label: 'All', count: statusCounts.all },
{ value: 'paid' as const, label: 'Paid', count: statusCounts.paid },
{ value: 'processing' as const, label: 'Processing', count: statusCounts.processing },
{ value: 'shipped' as const, label: 'Shipped', count: statusCounts.shipped },
{ value: 'refunded' as const, label: 'Refunded', count: statusCounts.refunded },
]
const allSelected = filteredOrders.length > 0 && filteredOrders.every((ord) => selected.has(ord.id))
const someSelected = filteredOrders.some((ord) => selected.has(ord.id))
const toggleAll = (checked: boolean) => {
setSelected(checked ? new Set(filteredOrders.map((ord) => ord.id)) : new Set())
}
const toggleRow = (id: string, checked: boolean) => {
const next = new Set(selected)
if (checked) next.add(id)
else next.delete(id)
setSelected(next)
}
const clearSelection = () => setSelected(new Set())
const exportSelected = () => clearSelection()
const markSelectedShipped = () => clearSelection()
const openOrderDetails = (order: Order) => {
setSelectedOrder(order)
setDrawerOpen(true)
}
return (
<div data-slot="order-management-page" className={cn('w-full space-y-6', className)}>
{/* Page Header */}
<div className="flex flex-wrap items-center justify-between gap-4">
<div>
<h2 className="text-foreground text-2xl font-bold tracking-tight">Orders</h2>
<p className="text-muted-foreground mt-1 text-sm">Manage and track customer purchases</p>
</div>
<div className="flex items-center gap-2">
<Button aria-label="Download attachment" variant="outline" size="sm" className="text-xs">
<Download className="mr-1.5 size-3.5" aria-hidden="true" />
Export CSV
</Button>
<Button size="sm" className="text-xs">
<Plus className="mr-1.5 size-3.5" aria-hidden="true" />
New Order
</Button>
</div>
</div>
{/* 4 Stat/KPI Summary Cards */}
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
<Card className="shadow-xs">
<CardContent className="p-4 sm:p-5">
<div className="flex items-center justify-between">
<p className="text-muted-foreground text-xs font-medium tracking-wider uppercase">Total Orders</p>
<div className="bg-muted text-muted-foreground flex size-8 items-center justify-center rounded-md">
<ShoppingCart className="size-4" aria-hidden="true" />
</div>
</div>
<div className="mt-3 flex flex-wrap items-baseline justify-between gap-2">
<span className="text-foreground text-2xl font-bold tracking-tight tabular-nums">1,429</span>
<span className="text-success inline-flex items-center gap-0.5 text-xs font-medium">
<TrendingUp className="size-3.5" aria-hidden="true" />
+8.2%
</span>
</div>
<p className="text-muted-foreground mt-1 text-xs">vs. previous 30 days</p>
</CardContent>
</Card>
<Card className="shadow-xs">
<CardContent className="p-4 sm:p-5">
<div className="flex items-center justify-between">
<p className="text-muted-foreground text-xs font-medium tracking-wider uppercase">Pending Fulfillment</p>
<div className="bg-warning/10 text-warning flex size-8 items-center justify-center rounded-md">
<Clock className="size-4" aria-hidden="true" />
</div>
</div>
<div className="mt-3 flex flex-wrap items-baseline justify-between gap-2">
<span className="text-foreground text-2xl font-bold tracking-tight tabular-nums">28</span>
<span className="bg-warning/10 text-warning inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium">
12 urgent
</span>
</div>
<p className="text-muted-foreground mt-1 text-xs">Requires warehouse dispatch</p>
</CardContent>
</Card>
<Card className="shadow-xs">
<CardContent className="p-4 sm:p-5">
<div className="flex items-center justify-between">
<p className="text-muted-foreground text-xs font-medium tracking-wider uppercase">Processing</p>
<div className="bg-info/10 text-info flex size-8 items-center justify-center rounded-md">
<PackageCheck className="size-4" aria-hidden="true" />
</div>
</div>
<div className="mt-3 flex flex-wrap items-baseline justify-between gap-2">
<span className="text-foreground text-2xl font-bold tracking-tight tabular-nums">45</span>
<span className="bg-info/10 text-info inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium">
In queue
</span>
</div>
<p className="text-muted-foreground mt-1 text-xs">Packaging & label generation</p>
</CardContent>
</Card>
<Card className="shadow-xs">
<CardContent className="p-4 sm:p-5">
<div className="flex items-center justify-between">
<p className="text-muted-foreground text-xs font-medium tracking-wider uppercase">Total Revenue</p>
<div className="bg-muted text-muted-foreground flex size-8 items-center justify-center rounded-md">
<DollarSign className="size-4" aria-hidden="true" />
</div>
</div>
<div className="mt-3 flex flex-wrap items-baseline justify-between gap-2">
<span className="text-foreground text-2xl font-bold tracking-tight tabular-nums">$128,450</span>
<span className="text-success inline-flex items-center gap-0.5 text-xs font-medium">
<ArrowUpRight className="size-3.5" aria-hidden="true" />
+14.5%
</span>
</div>
<p className="text-muted-foreground mt-1 text-xs">vs. previous period</p>
</CardContent>
</Card>
</div>
{/* Search and Filter Toolbar */}
<div className="flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between">
<div className="flex flex-1 flex-wrap items-center gap-2">
<div className="relative min-w-[240px] flex-1 sm:max-w-xs">
<Search
className="text-muted-foreground pointer-events-none absolute top-1/2 left-3 size-4 -translate-y-1/2"
aria-hidden="true"
/>
<Input
value={search}
onChange={(e) => setSearch(e.target.value)}
placeholder="Search order ID, customer..."
className="h-9 pl-9 text-xs"
/>
</div>
<div className="bg-muted/60 flex items-center gap-1 rounded-lg border p-1">
{statusOptions.map((status) => (
<button
key={status.value}
type="button"
className={cn(
'rounded-md px-2.5 py-1 text-xs font-medium transition-colors',
statusFilter === status.value
? 'bg-background text-foreground shadow-xs'
: 'text-muted-foreground hover:text-foreground',
)}
onClick={() => setStatusFilter(status.value)}
>
{status.label}
{status.count !== undefined && (
<span
className={cn(
'ml-1.5 rounded-full px-1.5 py-0.5 text-xs font-normal tabular-nums',
statusFilter === status.value ? 'bg-muted text-foreground' : 'text-muted-foreground',
)}
>
{status.count}
</span>
)}
</button>
))}
</div>
</div>
<div className="flex items-center gap-2">
<Button variant="outline" size="sm" className="text-xs font-normal">
<Calendar className="text-muted-foreground mr-1.5 size-3.5" aria-hidden="true" />
Last 30 days
<ChevronDown className="text-muted-foreground ml-1.5 size-3.5" aria-hidden="true" />
</Button>
</div>
</div>
{/* Bulk Action Bar */}
{selected.size > 0 && (
<div className="bg-muted/50 flex flex-wrap items-center justify-between gap-2 rounded-lg border px-4 py-2.5 shadow-xs">
<div className="flex items-center gap-2">
<span className="bg-primary text-primary-foreground flex size-5 items-center justify-center rounded-full text-xs font-medium">
{selected.size}
</span>
<span className="text-foreground text-sm font-medium">
{selected.size} {selected.size === 1 ? 'order' : 'orders'} selected
</span>
</div>
<div className="flex items-center gap-2">
<Button
aria-label="Download attachment"
variant="outline"
size="sm"
className="text-xs"
onClick={exportSelected}
>
<Download className="mr-1.5 size-3.5" aria-hidden="true" />
Export selected
</Button>
<Button variant="outline" size="sm" className="text-xs" onClick={markSelectedShipped}>
<Truck className="mr-1.5 size-3.5" aria-hidden="true" />
Mark as shipped
</Button>
<Button
variant="ghost"
size="sm"
className="text-destructive hover:text-destructive text-xs"
onClick={clearSelection}
>
Cancel
</Button>
</div>
</div>
)}
{/* Orders Table */}
<div className="bg-card overflow-x-auto rounded-lg border shadow-xs">
<Table>
<TableHeader>
<TableRow className="hover:bg-transparent">
<TableHead className="w-10">
<Checkbox
checked={allSelected ? true : someSelected ? 'indeterminate' : false}
aria-label="Select all orders"
onCheckedChange={(checked) => toggleAll(checked === true)}
/>
</TableHead>
<TableHead className="min-w-[110px]">Order ID</TableHead>
<TableHead className="min-w-[100px]">Date</TableHead>
<TableHead className="min-w-[200px]">Customer</TableHead>
<TableHead className="min-w-[220px]">Items</TableHead>
<TableHead className="min-w-[90px] text-right">Total</TableHead>
<TableHead className="min-w-[120px]">Fulfillment</TableHead>
<TableHead className="w-10 text-right">
<span className="sr-only">Actions</span>
</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{filteredOrders.map((order) => (
<TableRow
key={order.id}
className="group hover:bg-muted/40 cursor-pointer transition-colors"
onClick={() => openOrderDetails(order)}
>
<TableCell className="w-10" onClick={(e) => e.stopPropagation()}>
<Checkbox
checked={selected.has(order.id)}
aria-label={`Select order ${order.orderNumber}`}
onCheckedChange={(checked) => toggleRow(order.id, checked === true)}
/>
</TableCell>
<TableCell>
<span className="text-foreground font-mono text-xs font-semibold hover:underline">
{order.orderNumber}
</span>
</TableCell>
<TableCell className="text-muted-foreground text-xs tabular-nums">{order.date}</TableCell>
<TableCell>
<div className="flex items-center gap-2.5">
<Avatar className="size-7">
<AvatarFallback className="text-xs font-medium">{initials(order.customer.name)}</AvatarFallback>
</Avatar>
<div className="min-w-0">
<p className="text-foreground truncate text-sm font-medium">{order.customer.name}</p>
<p className="text-muted-foreground truncate text-xs">{order.customer.email}</p>
</div>
</div>
</TableCell>
<TableCell>
<div className="text-xs">
<span className="text-foreground font-medium">
{order.items.reduce((acc, item) => acc + item.quantity, 0)}{' '}
{order.items.reduce((acc, item) => acc + item.quantity, 0) === 1 ? 'item' : 'items'}
</span>
<span className="text-muted-foreground ml-1.5 inline-block max-w-[180px] truncate align-bottom">
• {order.items[0]?.name}
{order.items.length > 1 ? ` +${order.items.length - 1} more` : ''}
</span>
</div>
</TableCell>
<TableCell className="text-foreground text-right font-mono text-xs font-semibold tabular-nums">
${formatCurrency(order.total)}
</TableCell>
<TableCell>
<Badge
variant="outline"
className={cn(
'gap-1.5 px-2 py-0.5 text-xs font-medium capitalize',
statusBadgeConfig[order.status].class,
)}
>
<span
className={cn('size-1.5 rounded-full', statusBadgeConfig[order.status].dot)}
aria-hidden="true"
/>
{statusBadgeConfig[order.status].label}
</Badge>
</TableCell>
<TableCell className="text-right" onClick={(e) => e.stopPropagation()}>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="icon-sm" className="text-muted-foreground hover:text-foreground">
<MoreHorizontal className="size-4" aria-hidden="true" />
<span className="sr-only">Actions for {order.orderNumber}</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-48">
<DropdownMenuLabel>Order actions</DropdownMenuLabel>
<DropdownMenuItem onClick={() => openOrderDetails(order)}>
<Eye className="mr-2 size-4" aria-hidden="true" />
View details
</DropdownMenuItem>
<DropdownMenuItem>
<FileText className="mr-2 size-4" aria-hidden="true" />
Download invoice
</DropdownMenuItem>
{order.status === 'processing' && (
<DropdownMenuItem>
<Truck className="mr-2 size-4" aria-hidden="true" />
Mark as shipped
</DropdownMenuItem>
)}
<DropdownMenuSeparator />
{order.status !== 'refunded' && (
<DropdownMenuItem className="text-destructive focus:text-destructive">
<RotateCcw className="mr-2 size-4" aria-hidden="true" />
Issue refund
</DropdownMenuItem>
)}
</DropdownMenuContent>
</DropdownMenu>
</TableCell>
</TableRow>
))}
{filteredOrders.length === 0 && (
<TableRow>
<TableCell colSpan={8} className="text-muted-foreground h-32 text-center text-sm">
No orders match your filter criteria.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
{/* Pagination Footer */}
<div className="flex flex-wrap items-center justify-between gap-4">
<p className="text-muted-foreground text-xs tabular-nums">Showing 1-{filteredOrders.length} of 1,429 orders</p>
<div className="flex items-center gap-2">
<Button variant="outline" size="sm" className="text-xs" disabled>
Previous
</Button>
<Button variant="outline" size="sm" className="text-xs">
Next
</Button>
</div>
</div>
{/* Quick Details Drawer / Sheet */}
<Sheet open={drawerOpen} onOpenChange={setDrawerOpen}>
<SheetContent side="right" className="flex w-full flex-col gap-0 overflow-y-auto p-0 sm:max-w-lg">
{selectedOrder && (
<div className="flex h-full flex-col">
{/* Sheet Header */}
<SheetHeader className="space-y-1 border-b px-6 py-5">
<div className="flex items-center justify-between pr-6">
<span className="text-foreground font-mono text-base font-bold">{selectedOrder.orderNumber}</span>
<Badge
variant="outline"
className={cn(
'gap-1.5 px-2.5 py-0.5 text-xs font-medium capitalize',
statusBadgeConfig[selectedOrder.status].class,
)}
>
<span
className={cn('size-1.5 rounded-full', statusBadgeConfig[selectedOrder.status].dot)}
aria-hidden="true"
/>
{statusBadgeConfig[selectedOrder.status].label}
</Badge>
</div>
<SheetTitle className="text-muted-foreground text-xs font-normal">
Placed on {selectedOrder.date}
</SheetTitle>
<SheetDescription className="sr-only">
Detailed view of order {selectedOrder.orderNumber}
</SheetDescription>
</SheetHeader>
{/* Sheet Body */}
<div className="flex-1 space-y-6 overflow-y-auto px-6 py-5 text-sm">
{/* Customer & Shipping */}
<div className="bg-muted/20 space-y-3 rounded-lg border p-4">
<div className="flex items-center justify-between">
<h4 className="text-muted-foreground text-xs font-semibold tracking-wider uppercase">
Customer & Shipping
</h4>
<span className="text-muted-foreground font-mono text-xs">{selectedOrder.paymentMethod}</span>
</div>
<div className="flex items-center gap-3">
<Avatar className="size-9">
<AvatarFallback className="text-xs font-semibold">
{initials(selectedOrder.customer.name)}
</AvatarFallback>
</Avatar>
<div className="min-w-0">
<p className="text-foreground font-medium">{selectedOrder.customer.name}</p>
<p className="text-muted-foreground text-xs">{selectedOrder.customer.email}</p>
</div>
</div>
<div className="text-muted-foreground border-t pt-2.5 text-xs">
<p className="text-foreground font-medium">Delivery address:</p>
<p>{selectedOrder.customer.address}</p>
<p>
{selectedOrder.customer.city}, {selectedOrder.customer.state} {selectedOrder.customer.zip}
</p>
</div>
</div>
{/* Fulfillment & Tracking if available */}
{selectedOrder.carrier && selectedOrder.trackingNumber && (
<div className="border-border bg-card space-y-2 rounded-lg border p-4">
<div className="flex items-center justify-between">
<span className="text-muted-foreground text-xs font-semibold tracking-wider uppercase">
Fulfillment
</span>
<Badge variant="secondary" className="text-xs font-normal">
In Transit
</Badge>
</div>
<div className="flex items-center justify-between pt-1 text-xs">
<span className="text-muted-foreground">Carrier:</span>
<span className="text-foreground font-medium">{selectedOrder.carrier}</span>
</div>
<div className="flex items-center justify-between text-xs">
<span className="text-muted-foreground">Tracking ID:</span>
<span className="text-foreground font-mono font-medium">{selectedOrder.trackingNumber}</span>
</div>
</div>
)}
{/* Line Items */}
<div className="space-y-3">
<h4 className="text-muted-foreground text-xs font-semibold tracking-wider uppercase">Line Items</h4>
<div className="divide-border bg-card divide-y rounded-lg border">
{selectedOrder.items.map((item, idx) => (
<div key={idx} className="flex items-center justify-between gap-3 p-3.5">
<div className="min-w-0 flex-1">
<p className="text-foreground truncate text-sm font-medium">{item.name}</p>
<p className="text-muted-foreground font-mono text-xs">
SKU: {item.sku} • Qty: {item.quantity} × ${formatCurrency(item.price)}
</p>
</div>
<div className="text-foreground font-mono text-xs font-semibold tabular-nums">
${formatCurrency(item.price * item.quantity)}
</div>
</div>
))}
</div>
</div>
{/* Cost Breakdown */}
<div className="bg-card space-y-2 rounded-lg border p-4 text-xs">
<div className="text-muted-foreground flex justify-between">
<span>Subtotal</span>
<span className="text-foreground font-mono tabular-nums">
${formatCurrency(selectedOrder.subtotal)}
</span>
</div>
<div className="text-muted-foreground flex justify-between">
<span>Estimated Shipping</span>
<span className="text-foreground font-mono tabular-nums">
{selectedOrder.shipping === 0 ? 'Free' : `$${formatCurrency(selectedOrder.shipping)}`}
</span>
</div>
<div className="text-muted-foreground flex justify-between">
<span>Tax</span>
<span className="text-foreground font-mono tabular-nums">${formatCurrency(selectedOrder.tax)}</span>
</div>
<div className="text-foreground flex justify-between border-t pt-2 text-sm font-medium">
<span>Total</span>
<span className="text-foreground font-mono font-bold tabular-nums">
${formatCurrency(selectedOrder.total)}
</span>
</div>
</div>
</div>
{/* Sheet Footer */}
<SheetFooter className="bg-muted/10 flex flex-row items-center justify-between gap-2 border-t px-6 py-4">
<Button aria-label="Download attachment" variant="outline" size="sm" className="text-xs">
<Download className="mr-1.5 size-3.5" aria-hidden="true" />
Invoice PDF
</Button>
<SheetClose asChild>
<Button variant="secondary" size="sm" className="text-xs">
Close
</Button>
</SheetClose>
</SheetFooter>
</div>
)}
</SheetContent>
</Sheet>
</div>
)
}
Raw manifest:https://uipkge.dev/r/react/order-management-page.json