From 6c0a2827659c0f0380c8d961e306854d302b44a7 Mon Sep 17 00:00:00 2001 From: Tyler Date: Wed, 17 Jun 2026 00:19:39 -0600 Subject: [PATCH] feat(admin): unified command center dashboard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - KPI strip uses new component (4 cards) - New 'What needs attention' feed replaces 4 tabs of cards: * no orders today → suggest campaign * pending stops → link to stops * usage > 85% → warn * starter plan → upgrade prompt - Quick actions + plan usage consolidated into one card - Recent orders uses new - All 15 sections in a single grid (not 4 tabs), with 'All / Operations / Fulfillment / Management / Tools' filter - 'Press ⌘K to search' hint under section grid - All inline SVGs replaced with lucide-react - All hardcoded colors replaced with new design tokens --- src/components/admin/DashboardClient.tsx | 632 +++++++++++------------ 1 file changed, 316 insertions(+), 316 deletions(-) diff --git a/src/components/admin/DashboardClient.tsx b/src/components/admin/DashboardClient.tsx index a2c931f..cf79c61 100644 --- a/src/components/admin/DashboardClient.tsx +++ b/src/components/admin/DashboardClient.tsx @@ -3,7 +3,8 @@ import { useState, type ReactNode } from "react"; import Link from "next/link"; import dynamic from "next/dynamic"; -import { PageHeader, AdminButton, AdminFilterTabs, AdminBadge } from "@/components/admin/design-system"; +import { PageHeader, AdminButton, AdminFilterTabs, AdminBadge, KPIStat, EmptyState } from "@/components/admin/design-system"; +import { ShoppingCart, MapPin, Package, DollarSign, Plus, Send, AlertCircle, Calendar, Inbox, Sparkles, BarChart3, BrainCircuit, Upload, Clock, Droplets, Route, Truck, Receipt, Settings as SettingsIcon, Store, type LucideIcon } from "lucide-react"; import type { DashboardStats } from "@/actions/dashboard"; // Lazy-load the upgrade modal — only needed when starter tier + brandId present @@ -20,69 +21,35 @@ type Section = { addonKey?: string; upgradeText?: string; prominent?: boolean; + icon: LucideIcon; }; const sections: Section[] = [ - { title: "Orders", href: "/admin/orders", description: "View orders, pickup status, fulfillment, and customer details.", group: "operations", prominent: true }, - { title: "Products", href: "/admin/products", description: "Manage products, pricing, shipping type, and availability.", group: "operations", prominent: true }, - { title: "Tours & Stops", href: "/admin/stops", description: "Manage routes, pickup locations, dates, and cutoff times.", group: "fulfillment" }, - { title: "Driver Pickup", href: "/admin/pickup", description: "Mobile pickup lookup, QR scanning, and completion tools.", group: "fulfillment" }, - { title: "Shipping", href: "/admin/shipping", description: "FedEx integration, label creation, and shipment tracking.", group: "fulfillment" }, - { title: "Reports", href: "/admin/reports", description: "Sales, route, product, pickup, and customer reports.", group: "management" }, - { title: "Tax Dashboard", href: "/admin/taxes", description: "Sales tax collected, breakdown by state, and exportable reports.", group: "management" }, - { title: "Settings", href: "/admin/settings", description: "Users, billing, brand, integrations, payments, and shipping.", group: "management" }, - { title: "Harvest Reach", href: "/admin/communications", description: "Email campaigns, stop blast, templates, and audience segments.", group: "tools", addonKey: "harvest_reach", upgradeText: "Enable to access email & SMS marketing" }, - { title: "Wholesale Portal", href: "/admin/wholesale", description: "Standalone B2B portal with custom pricing, credit limits, and net-30.", group: "tools", addonKey: "wholesale_portal", upgradeText: "Enable to unlock B2B buyer portal" }, - { title: "Import Center", href: "/admin/import", description: "AI-powered import for products, orders, contacts, and stops.", group: "tools", addonKey: "ai_tools", upgradeText: "Enable AI import with smart column mapping" }, - { title: "AI Intelligence", href: "/admin/settings/ai", description: "Campaign writer, pricing advisor, and report explainer.", group: "tools", addonKey: "ai_tools", upgradeText: "Enable AI tools for marketing and pricing" }, - { title: "Time Tracking", href: "/admin/time-tracking", description: "Worker clock-in/out, hours tracking, and overtime management.", group: "tools", addonKey: "time_tracking", upgradeText: "Enable for field worker time tracking" }, - { title: "Water Log", href: "/admin/water-log", description: "Irrigation tracking and water usage reporting.", group: "tools", addonKey: "water_log", upgradeText: "Enable for agricultural water tracking" }, - { title: "Route Trace", href: "/admin/route-trace", description: "Lot tracking, QR stickers, hauling board, and supply chain traceability.", group: "tools", addonKey: "route_trace", upgradeText: "Enable for field-to-delivery traceability" }, + { title: "Orders", href: "/admin/orders", description: "View orders, pickup status, fulfillment, and customer details.", group: "operations", prominent: true, icon: ShoppingCart }, + { title: "Products", href: "/admin/products", description: "Manage products, pricing, shipping type, and availability.", group: "operations", prominent: true, icon: Package }, + { title: "Tours & Stops", href: "/admin/stops", description: "Manage routes, pickup locations, dates, and cutoff times.", group: "fulfillment", icon: MapPin }, + { title: "Driver Pickup", href: "/admin/pickup", description: "Mobile pickup lookup, QR scanning, and completion tools.", group: "fulfillment", icon: Truck }, + { title: "Shipping", href: "/admin/shipping", description: "FedEx integration, label creation, and shipment tracking.", group: "fulfillment", icon: Receipt }, + { title: "Reports", href: "/admin/reports", description: "Sales, route, product, pickup, and customer reports.", group: "management", icon: BarChart3 }, + { title: "Tax Dashboard", href: "/admin/taxes", description: "Sales tax collected, breakdown by state, and exportable reports.", group: "management", icon: Receipt }, + { title: "Settings", href: "/admin/settings", description: "Users, billing, brand, integrations, payments, and shipping.", group: "management", icon: SettingsIcon }, + { title: "Harvest Reach", href: "/admin/communications", description: "Email campaigns, stop blast, templates, and audience segments.", group: "tools", addonKey: "harvest_reach", upgradeText: "Enable to access email & SMS marketing", icon: Send }, + { title: "Wholesale Portal", href: "/admin/wholesale", description: "Standalone B2B portal with custom pricing, credit limits, and net-30.", group: "tools", addonKey: "wholesale_portal", upgradeText: "Enable to unlock B2B buyer portal", icon: Store }, + { title: "Import Center", href: "/admin/import", description: "AI-powered import for products, orders, contacts, and stops.", group: "tools", addonKey: "ai_tools", upgradeText: "Enable AI import with smart column mapping", icon: Upload }, + { title: "AI Intelligence", href: "/admin/settings/ai", description: "Campaign writer, pricing advisor, and report explainer.", group: "tools", addonKey: "ai_tools", upgradeText: "Enable AI tools for marketing and pricing", icon: BrainCircuit }, + { title: "Time Tracking", href: "/admin/time-tracking", description: "Worker clock-in/out, hours tracking, and overtime management.", group: "tools", addonKey: "time_tracking", upgradeText: "Enable for field worker time tracking", icon: Clock }, + { title: "Water Log", href: "/admin/water-log", description: "Irrigation tracking and water usage reporting.", group: "tools", addonKey: "water_log", upgradeText: "Enable for agricultural water tracking", icon: Droplets }, + { title: "Route Trace", href: "/admin/route-trace", description: "Lot tracking, QR stickers, hauling board, and supply chain traceability.", group: "tools", addonKey: "route_trace", upgradeText: "Enable for field-to-delivery traceability", icon: Route }, ]; -type Tab = "operations" | "fulfillment" | "management" | "tools"; +type GroupFilter = "all" | "operations" | "fulfillment" | "management" | "tools"; -const TABS: { id: Tab; label: string; icon: ReactNode }[] = [ - { - id: "operations", - label: "Operations", - icon: ( - - - - ), - }, - { - id: "fulfillment", - label: "Fulfillment", - icon: ( - - - - - - - ), - }, - { - id: "management", - label: "Management", - icon: ( - - - - - ), - }, - { - id: "tools", - label: "Tools", - icon: ( - - - - ), - }, +const GROUP_FILTERS: { id: GroupFilter; label: string }[] = [ + { id: "all", label: "All" }, + { id: "operations", label: "Operations" }, + { id: "fulfillment", label: "Fulfillment" }, + { id: "management", label: "Management" }, + { id: "tools", label: "Tools" }, ]; type Props = { @@ -96,6 +63,15 @@ type Props = { stats: DashboardStats; }; +type AttentionItem = { + id: string; + title: string; + detail: string; + href: string; + tone: "primary" | "accent" | "warning" | "danger"; + icon: LucideIcon; +}; + export default function DashboardClient({ brandId, brandName, @@ -106,11 +82,8 @@ export default function DashboardClient({ limits, stats, }: Props) { - const [activeTab, setActiveTab] = useState("operations"); const [isUpgradeOpen, setIsUpgradeOpen] = useState(false); - - // Stats are pre-fetched server-side — no loading state needed. - // All values are guaranteed to be present before this component renders. + const [groupFilter, setGroupFilter] = useState("all"); const usagePct = { users: limits.max_users > 0 ? (usage.users / limits.max_users) * 100 : 0, @@ -118,8 +91,6 @@ export default function DashboardClient({ products: limits.max_products > 0 ? (usage.products / limits.max_products) * 100 : 0, }; - const tabSections = sections.filter((s) => s.group === activeTab); - const formatCurrency = (amount: number) => { return new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" }).format(amount); }; @@ -133,27 +104,77 @@ export default function DashboardClient({ return styles[status] || { bg: "var(--admin-bg)", text: "var(--admin-text-secondary)" }; }; + // "What needs attention" feed — composes a flat list of items that need action + const attentionItems: AttentionItem[] = []; + if (stats.todayOrders === 0) { + attentionItems.push({ + id: "no-orders-today", + title: "No orders today yet", + detail: "Send a campaign or check that your storefront is reachable.", + href: "/admin/communications/compose", + tone: "accent", + icon: Send, + }); + } + if (stats.pendingStops > 0) { + attentionItems.push({ + id: "pending-stops", + title: `${stats.pendingStops} stop${stats.pendingStops === 1 ? "" : "s"} pending`, + detail: "Review upcoming routes and confirm pickup locations.", + href: "/admin/stops", + tone: "primary", + icon: MapPin, + }); + } + if (usagePct.products > 85) { + attentionItems.push({ + id: "products-near-limit", + title: `${Math.round(usagePct.products)}% of product limit used`, + detail: "Consider archiving inactive products or upgrading your plan.", + href: "/admin/products", + tone: "warning", + icon: Package, + }); + } else if (usagePct.users > 85) { + attentionItems.push({ + id: "users-near-limit", + title: `${Math.round(usagePct.users)}% of user limit used`, + detail: "Add more seats or review who has access.", + href: "/admin/users", + tone: "warning", + icon: AlertCircle, + }); + } + if (planTier === "starter" && brandId) { + attentionItems.push({ + id: "upgrade-prompt", + title: "Unlock unlimited stops & products", + detail: "Upgrade from Starter to Farm for $149/mo.", + href: "/admin/settings/billing", + tone: "accent", + icon: Sparkles, + }); + } + const quickActions = [ - { label: "New Order", href: "/admin/orders?new=true", icon: "plus" }, - { label: "Add Stop", href: "/admin/stops/new", icon: "map" }, - { label: "Add Product", href: "/admin/products/new", icon: "package" }, - { label: "Send Blast", href: "/admin/communications/compose", icon: "mail" }, + { label: "New Order", href: "/admin/orders?new=true", icon: Plus }, + { label: "Add Stop", href: "/admin/stops/new", icon: MapPin }, + { label: "Add Product", href: "/admin/products/new", icon: Package }, + { label: "Send Blast", href: "/admin/communications/compose", icon: Send }, ]; + const visibleSections = sections + .filter((s) => !(s.title === "Water Log" && !isWaterLogVisible)) + .filter((s) => !(s.title === "Route Trace" && !enabledAddons["route_trace"])) + .filter((s) => groupFilter === "all" || s.group === groupFilter); + return (
{/* Page Header */}
- - - - - - } - title="Admin Dashboard" + icon={} + title="Dashboard" subtitle={brandName} actions={
@@ -174,154 +195,157 @@ export default function DashboardClient({ {/* Main Content */}
- {/* ── Stats Cards Row ────────────────────────────────────── */} + {/* ── KPI Strip (top metrics) ──────────────────────────── */}
- {/* Today's Orders */} -
-
-
- - - -
-
- Today's Orders - {stats.todayOrders} -
-
-
- - {/* Today's Revenue */} -
-
-
- - - -
-
- Today's Revenue - {formatCurrency(stats.todayRevenue)} -
-
-
- - {/* Pending Stops */} -
-
-
- - - - -
-
- Pending Stops - {stats.pendingStops} -
-
-
- - {/* Active Products */} -
-
-
- - - -
-
- Active Products - {usage.products} -
-
-
+ } + tone="primary" + /> + } + tone="accent" + /> + } + tone="default" + /> + } + tone="primary" + />
- {/* ── Quick Actions + Usage Row ─────────────────────────── */} + {/* ── Command center: attention feed + quick actions ───── */}
- {/* Quick Actions */} -
-
- Quick Actions -
-
- {quickActions.map((action) => ( - -
- {action.icon === "plus" && ( - - - - )} - {action.icon === "map" && ( - - - - - )} - {action.icon === "package" && ( - - - - )} - {action.icon === "mail" && ( - - - - )} -
- {action.label} - - - - - ))} -
-
- - {/* Usage Stats */} + {/* Attention feed (left, 2 cols) */}
-
- - {planTier.charAt(0).toUpperCase() + planTier.slice(1)} - - {brandId ? brandName : "All Brands"} -
- - Manage → - + What needs attention + + {attentionItems.length} item{attentionItems.length === 1 ? "" : "s"} +
-
- {[ - { label: "Users", value: `${usage.users}/${limits.max_users}`, pct: usagePct.users, icon: "users" }, - { label: "Stops", value: `${usage.stops_this_month}/${limits.max_stops_monthly}`, pct: usagePct.stops, icon: "map" }, - { label: "Products", value: `${usage.products}/${limits.max_products}`, pct: usagePct.products, icon: "package" }, - ].map(({ label, value, pct }) => ( -
-
- {label} - {value} -
-
-
90 ? "var(--admin-danger)" : pct > 75 ? "var(--admin-warning)" : "var(--admin-accent)", - }} - /> -
- {pct > 85 && ( - Near limit - )} + {attentionItems.length === 0 ? ( + } + title="All clear" + description="Nothing needs your attention right now. Enjoy the calm." + /> + ) : ( +
    + {attentionItems.map((item) => { + const Icon = item.icon; + const toneColor = + item.tone === "accent" ? "var(--admin-accent)" : + item.tone === "warning" ? "var(--admin-warning)" : + item.tone === "danger" ? "var(--admin-danger)" : + "var(--admin-primary)"; + const toneSoft = + item.tone === "accent" ? "var(--admin-accent-soft)" : + item.tone === "warning" ? "var(--admin-warning-soft)" : + item.tone === "danger" ? "var(--admin-danger-soft)" : + "var(--admin-primary-soft)"; + return ( +
  • + + +
    +

    + {item.title} +

    +

    + {item.detail} +

    +
    + + +
  • + ); + })} +
+ )} +
+ + {/* Quick actions (right, 1 col) */} +
+
+ Quick actions +
+
+ {quickActions.map((action) => { + const Icon = action.icon; + return ( + +
+ +
+ {action.label} + + + ); + })} +
+ + {/* Plan usage inside quick actions card */} +
+
+
+ + {planTier.charAt(0).toUpperCase() + planTier.slice(1)} + + + {brandId ? brandName : "All Brands"} +
- ))} +
+
+ {[ + { label: "Users", value: `${usage.users}/${limits.max_users}`, pct: usagePct.users }, + { label: "Stops", value: `${usage.stops_this_month}/${limits.max_stops_monthly}`, pct: usagePct.stops }, + { label: "Products", value: `${usage.products}/${limits.max_products}`, pct: usagePct.products }, + ].map(({ label, value, pct }) => ( +
+
+ {label} + {value} +
+
+
90 ? "var(--admin-danger)" : pct > 75 ? "var(--admin-warning)" : "var(--admin-primary)", + }} + /> +
+
+ ))} +
@@ -329,8 +353,8 @@ export default function DashboardClient({ {/* ── Recent Orders ─────────────────────────────────────── */}
- Recent Orders - + Recent orders + View all →
@@ -346,9 +370,7 @@ export default function DashboardClient({ >
- - - +
{order.customer_name} @@ -366,108 +388,86 @@ export default function DashboardClient({ })}
) : ( -
- - - -

No recent orders

- - Create your first order → - -
+ } + title="No recent orders" + description="When customers place orders, they'll show up here." + action={{ label: "Create your first order", href: "/admin/orders?new=true" }} + /> )}
- {/* ── Section Navigation Tabs ───────────────────────────── */} + {/* ── Section filter + grid ────────────────────────────── */}
- setActiveTab(value as Tab)} - tabs={TABS.map((tab) => ({ value: tab.id, label: tab.label, icon: tab.icon }))} - size="md" - showCounts={false} - /> -
+
+
+

+ All sections +

+

+ Jump to any part of the admin. Press ⌘K to search. +

+
+ setGroupFilter(value as GroupFilter)} + tabs={GROUP_FILTERS.map((f) => ({ value: f.id, label: f.label }))} + size="sm" + showCounts={false} + /> +
+
+ {visibleSections.map((section) => { + const Icon = section.icon; + const isAddon = Boolean(section.addonKey); + const isEnabled = section.addonKey ? (enabledAddons[section.addonKey] ?? false) : true; + const isProminent = section.prominent; - {/* ── Section Cards Grid ─────────────────────────────────── */} -
- {tabSections.map((section) => { - if (section.title === "Water Log" && !isWaterLogVisible) return null; - if (section.title === "Route Trace" && !enabledAddons["route_trace"]) return null; - - const isAddon = Boolean(section.addonKey); - const isEnabled = section.addonKey ? (enabledAddons[section.addonKey] ?? false) : true; - const isProminent = section.prominent; - - return ( - -
-
- {section.title === "Orders" && ( - - - - )} - {section.title === "Products" && ( - - - - )} - {section.title === "Tours & Stops" && ( - - - - - )} - {section.title === "Driver Pickup" && ( - - - - )} - {!["Orders", "Products", "Tours & Stops", "Driver Pickup"].includes(section.title) && ( - - - - )} + return ( + +
+
+ +
+
+ {isAddon && !isEnabled && Add-on} + {isAddon && isEnabled && Active} + {isProminent && Core} +
-
- {isAddon && !isEnabled && Add-on} - {isAddon && isEnabled && Active} - {isProminent && Core} + +
+

{section.title}

+

+ {section.description} +

-
-
-

{section.title}

-

- {section.description} -

-
+ {isAddon && !isEnabled && section.upgradeText && ( +

{section.upgradeText}

+ )} - {isAddon && !isEnabled && section.upgradeText && ( -

{section.upgradeText}

- )} - -
- - - -
- - ); - })} +
+ + + +
+ + ); + })} +
@@ -482,4 +482,4 @@ export default function DashboardClient({ )}
); -} \ No newline at end of file +}