diff --git a/inventory/src/components/dashboard/AircallDashboard.jsx b/inventory/src/components/dashboard/AircallDashboard.jsx index 0e11ab6..f879b32 100644 --- a/inventory/src/components/dashboard/AircallDashboard.jsx +++ b/inventory/src/components/dashboard/AircallDashboard.jsx @@ -223,7 +223,7 @@ const AircallDashboard = () => { {/* Metric Cards */} -
+
{isLoading ? ( [...Array(4)].map((_, i) => ( diff --git a/inventory/src/components/dashboard/AnalyticsDashboard.jsx b/inventory/src/components/dashboard/AnalyticsDashboard.jsx index 76b274b..8999ea1 100644 --- a/inventory/src/components/dashboard/AnalyticsDashboard.jsx +++ b/inventory/src/components/dashboard/AnalyticsDashboard.jsx @@ -282,7 +282,7 @@ export const AnalyticsDashboard = () => { {loading ? ( ) : summaryStats ? ( -
+
= { - income: "#3b82f6", - cogs: "#f97316", - cogsPercentage: "#fb923c", - profit: "#10b981", - margin: "#8b5cf6", + income: METRIC_COLORS.orders, // Blue - revenue/income streams + cogs: METRIC_COLORS.expense, // Orange - costs/expenses + cogsPercentage: "#f97316", // Orange-500 - slightly brighter for percentage line + profit: METRIC_COLORS.profit, // Green - profit metrics + margin: METRIC_COLORS.aov, // Violet - percentage/derived metrics }; const SERIES_LABELS: Record = { @@ -1228,7 +1230,7 @@ const FinancialOverview = () => { ) : null; return ( - + +
{cards.map((card) => ( { /> -
+
{loading ? ( [...Array(7)].map((_, i) => ( - + )) ) : ( <> @@ -302,9 +302,7 @@ const GorgiasOverview = () => { value: stats.total_messages_received.delta, suffix: "", } : undefined} - icon={Mail} - iconColor="blue" - size="compact" + /> { value: stats.total_messages_sent.delta, suffix: "", } : undefined} - icon={Send} - iconColor="green" - size="compact" + /> { suffix: "", moreIsBetter: false, } : undefined} - icon={Zap} - iconColor="purple" - size="compact" + /> { value: stats.total_one_touch_tickets.delta, suffix: "%", } : undefined} - icon={BarChart3} - iconColor="indigo" - size="compact" + /> { value: satisfactionStats.average_rating.delta, suffix: "%", } : undefined} - icon={Star} - iconColor="orange" - size="compact" + /> { value: satisfactionStats.response_rate.delta, suffix: "%", } : undefined} - icon={ClipboardCheck} - iconColor="pink" - size="compact" + /> { suffix: "", moreIsBetter: false, } : undefined} - icon={Timer} - iconColor="teal" - size="compact" + /> )} diff --git a/inventory/src/components/dashboard/MetaCampaigns.jsx b/inventory/src/components/dashboard/MetaCampaigns.jsx index 7eb1984..42f6dbd 100644 --- a/inventory/src/components/dashboard/MetaCampaigns.jsx +++ b/inventory/src/components/dashboard/MetaCampaigns.jsx @@ -514,7 +514,7 @@ const MetaCampaigns = () => { } /> -
+
{ )} -
+
{ if (!value || isNaN(value)) return "$0"; @@ -258,7 +270,7 @@ const SummaryStats = memo(({ stats = {}, projection = null, projectionLoading = }; return ( -
+
{ ) : null; return ( - + { type="monotone" dataKey="revenue" name="Revenue" - stroke="#8b5cf6" + stroke={CHART_COLORS.revenue} strokeWidth={2} dot={false} /> @@ -839,7 +851,7 @@ const SalesChart = ({ timeRange = "last30days", title = "Sales Overview" }) => { type="monotone" dataKey="prevRevenue" name="Previous Revenue" - stroke="#f97316" + stroke={CHART_COLORS.prevRevenue} strokeWidth={2} dot={false} strokeDasharray="5 5" @@ -851,7 +863,7 @@ const SalesChart = ({ timeRange = "last30days", title = "Sales Overview" }) => { type="monotone" dataKey="orders" name="Orders" - stroke="#10b981" + stroke={CHART_COLORS.orders} strokeWidth={2} dot={false} /> @@ -862,7 +874,7 @@ const SalesChart = ({ timeRange = "last30days", title = "Sales Overview" }) => { type="monotone" dataKey="prevOrders" name="Previous Orders" - stroke="#0ea5e9" + stroke={CHART_COLORS.prevOrders} strokeWidth={2} dot={false} strokeDasharray="5 5" @@ -874,7 +886,7 @@ const SalesChart = ({ timeRange = "last30days", title = "Sales Overview" }) => { type="monotone" dataKey="avgOrderValue" name="Avg Order Value" - stroke="#9333ea" + stroke={CHART_COLORS.aov} strokeWidth={2} dot={false} /> @@ -885,7 +897,7 @@ const SalesChart = ({ timeRange = "last30days", title = "Sales Overview" }) => { type="monotone" dataKey="prevAvgOrderValue" name="Previous Avg Order" - stroke="#f59e0b" + stroke={CHART_COLORS.prevAov} strokeWidth={2} dot={false} strokeDasharray="5 5" @@ -897,7 +909,7 @@ const SalesChart = ({ timeRange = "last30days", title = "Sales Overview" }) => { type="monotone" dataKey="movingAverage" name="7-Day Average" - stroke="#f59e0b" + stroke={CHART_COLORS.movingAverage} strokeWidth={2} dot={false} strokeDasharray="5 5" diff --git a/inventory/src/components/dashboard/StatCards.jsx b/inventory/src/components/dashboard/StatCards.jsx index d9cc60d..12cac14 100644 --- a/inventory/src/components/dashboard/StatCards.jsx +++ b/inventory/src/components/dashboard/StatCards.jsx @@ -1583,11 +1583,10 @@ const StatCards = ({
- + {title} - - {lastUpdate && !loading && ( - + {lastUpdate && !loading && ( +
Last updated {lastUpdate.toFormat("h:mm a")} {projection?.confidence > 0 && !projectionLoading && ( @@ -1613,8 +1612,10 @@ const StatCards = ({ )} - +
)} +
+
@@ -1635,9 +1636,9 @@ const StatCards = ({
-
+
= ({ const IconComponent = direction === "up" - ? TrendingUp + ? ArrowUp : direction === "down" - ? TrendingDown + ? ArrowDown : Minus; - const iconSize = size === "compact" ? "h-3 w-3" : "h-4 w-4"; - const textSize = size === "compact" ? "text-xs" : "text-sm"; + const iconSize = size === "compact" ? "h-3 w-3" : "h-3 w-3"; + const textSize = size === "compact" ? "text-xs" : "text-xs"; // Format the value - use fixed decimal for percentages, integer for absolute values const formattedValue = suffix === "%" @@ -243,9 +243,9 @@ export const DashboardStatCard: React.FC = ({ // Format the display value with prefix/suffix const formattedValue = ( <> - {valuePrefix && {valuePrefix}} + {valuePrefix && {valuePrefix}} {typeof value === "number" ? value.toLocaleString() : value} - {valueSuffix && {valueSuffix}} + {valueSuffix && {valueSuffix}} ); diff --git a/inventory/src/components/dashboard/shared/index.ts b/inventory/src/components/dashboard/shared/index.ts index 615c6e5..a30bbac 100644 --- a/inventory/src/components/dashboard/shared/index.ts +++ b/inventory/src/components/dashboard/shared/index.ts @@ -154,6 +154,7 @@ export { TYPOGRAPHY, SPACING, RADIUS, + MOTION, SCROLL_STYLES, TREND_COLORS, EVENT_COLORS, diff --git a/inventory/src/index.css b/inventory/src/index.css index c1e631d..d8cace5 100644 --- a/inventory/src/index.css +++ b/inventory/src/index.css @@ -63,18 +63,18 @@ --card-glass: 0 0% 100%; --card-glass-foreground: 222.2 84% 4.9%; - /* Semantic chart colors - EXACT values for consistency */ - --chart-revenue: 160.1 84.1% 39.4%; /* #10b981 - emerald-500 */ - --chart-orders: 217.2 91.2% 59.8%; /* #3b82f6 - blue-500 */ - --chart-aov: 258.3 89.5% 66.3%; /* #8b5cf6 - violet-500 */ - --chart-comparison: 37.7 92.1% 50.2%; /* #f59e0b - amber-500 */ - --chart-expense: 24.6 95% 53.1%; /* #f97316 - orange-500 */ - --chart-profit: 142.1 76.2% 45.7%; /* #22c55e - green-500 */ - --chart-secondary: 187.9 85.7% 53.3%; /* #06b6d4 - cyan-500 */ - --chart-tertiary: 330.4 81.2% 60.4%; /* #ec4899 - pink-500 */ + /* Semantic chart colors - Professional palette with deeper tones */ + --chart-revenue: 161.4 93.5% 30.4%; /* #059669 - emerald-600 */ + --chart-orders: 221.2 83.2% 53.3%; /* #2563eb - blue-600 */ + --chart-aov: 262.1 83.3% 57.8%; /* #7c3aed - violet-600 */ + --chart-comparison: 32.1 94.6% 43.7%; /* #d97706 - amber-600 */ + --chart-expense: 20.5 90.2% 48.2%; /* #ea580c - orange-600 */ + --chart-profit: 142.1 76.2% 36.3%; /* #16a34a - green-600 */ + --chart-secondary: 188.7 94.5% 42.7%; /* #0891b2 - cyan-600 */ + --chart-tertiary: 335.1 77.6% 50%; /* #db2777 - pink-600 */ - /* Trend colors */ - --trend-positive: 160.1 84.1% 39.4%; /* emerald-500 */ + /* Trend colors - matches revenue for consistency */ + --trend-positive: 161.4 93.5% 30.4%; /* emerald-600 */ --trend-positive-muted: 158.1 64.4% 91.6%; /* emerald-100 */ --trend-negative: 346.8 77.2% 49.8%; /* rose-500 */ --trend-negative-muted: 355.7 100% 94.7%; /* rose-100 */ @@ -131,18 +131,18 @@ --card-glass: 222.2 47.4% 11.2%; --card-glass-foreground: 210 40% 98%; - /* Semantic chart colors - slightly brighter in dark mode for visibility */ - --chart-revenue: 158.1 64.4% 51.6%; /* emerald-400 */ - --chart-orders: 213.1 93.9% 67.8%; /* blue-400 */ - --chart-aov: 255.1 91.7% 76.3%; /* violet-400 */ - --chart-comparison: 43.3 96.4% 56.3%; /* amber-400 */ - --chart-expense: 27.0 96.0% 61.0%; /* orange-400 */ - --chart-profit: 141.9 69.2% 58%; /* green-400 */ - --chart-secondary: 186.0 93.5% 55.7%; /* cyan-400 */ - --chart-tertiary: 328.6 85.5% 70.2%; /* pink-400 */ + /* Semantic chart colors - brighter in dark mode for visibility (one step lighter) */ + --chart-revenue: 160.1 84.1% 39.4%; /* #10b981 - emerald-500 */ + --chart-orders: 217.2 91.2% 59.8%; /* #3b82f6 - blue-500 */ + --chart-aov: 258.3 89.5% 66.3%; /* #8b5cf6 - violet-500 */ + --chart-comparison: 37.7 92.1% 50.2%; /* #f59e0b - amber-500 */ + --chart-expense: 24.6 95% 53.1%; /* #f97316 - orange-500 */ + --chart-profit: 142.1 76.2% 45.7%; /* #22c55e - green-500 */ + --chart-secondary: 187.9 85.7% 53.3%; /* #06b6d4 - cyan-500 */ + --chart-tertiary: 330.4 81.2% 60.4%; /* #ec4899 - pink-500 */ /* Trend colors - brighter in dark mode */ - --trend-positive: 158.1 64.4% 51.6%; /* emerald-400 */ + --trend-positive: 160.1 84.1% 39.4%; /* emerald-500 */ --trend-positive-muted: 163.1 88.1% 19.6%; /* emerald-900 */ --trend-negative: 351.3 94.5% 71.4%; /* rose-400 */ --trend-negative-muted: 343.1 87.7% 15.9%; /* rose-950 */ @@ -188,4 +188,57 @@ .dark .dashboard-scroll:hover { scrollbar-color: rgb(75 85 99) transparent; } + + /* Dashboard motion utilities */ + .dashboard-card-hover { + @apply transition-all duration-200 ease-out; + } + .dashboard-card-hover:hover { + @apply shadow-md; + transform: translateY(-1px); + } + + .dashboard-value-change { + @apply transition-[color,transform] duration-300; + } + + .dashboard-fade-in { + animation: dashboardFadeIn 0.3s ease-out; + } + + .dashboard-slide-up { + animation: dashboardSlideUp 0.3s ease-out; + } } + +/* Dashboard animation keyframes */ +@keyframes dashboardFadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes dashboardSlideUp { + from { + opacity: 0; + transform: translateY(8px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +/* Staggered animation for grid items */ +.dashboard-stagger > * { + animation: dashboardSlideUp 0.3s ease-out both; +} +.dashboard-stagger > *:nth-child(1) { animation-delay: 0ms; } +.dashboard-stagger > *:nth-child(2) { animation-delay: 50ms; } +.dashboard-stagger > *:nth-child(3) { animation-delay: 100ms; } +.dashboard-stagger > *:nth-child(4) { animation-delay: 150ms; } +.dashboard-stagger > *:nth-child(5) { animation-delay: 200ms; } +.dashboard-stagger > *:nth-child(6) { animation-delay: 250ms; } diff --git a/inventory/src/lib/dashboard/designTokens.ts b/inventory/src/lib/dashboard/designTokens.ts index 5ffcb98..ee32950 100644 --- a/inventory/src/lib/dashboard/designTokens.ts +++ b/inventory/src/lib/dashboard/designTokens.ts @@ -19,18 +19,24 @@ export const CARD_STYLES = { /** Base card appearance with glass effect (default for dashboard) */ base: "card-glass", + /** Elevated card for primary/featured sections (Sales, Financial) */ + elevated: "card-glass shadow-lg", + /** Subtle card for secondary/supporting content */ + subtle: "card-glass bg-card-glass/40", + /** Accent card with subtle ring highlight */ + accent: "card-glass ring-1 ring-primary/10", /** Card with subtle hover effect */ - interactive: "card-glass transition-shadow hover:shadow-md", + interactive: "card-glass transition-all duration-200 ease-out hover:shadow-md hover:scale-[1.01]", /** Solid card without glass effect (use sparingly) */ solid: "bg-card border border-border/50 shadow-sm rounded-xl", /** Card header layout */ - header: "flex flex-row items-center justify-between p-4 pb-2", + header: "flex flex-row items-center justify-between p-4 pb-0", /** Compact header for stat cards */ headerCompact: "flex flex-row items-center justify-between px-4 pt-4 pb-2", /** Card content area */ - content: "p-4 pt-0", + content: "p-3 pt-0", /** Card content with extra bottom padding */ - contentPadded: "p-4 pt-0 pb-4", + contentPadded: "p-3 pt-0 pb-4", } as const; /** @@ -38,15 +44,17 @@ export const CARD_STYLES = { */ export const TYPOGRAPHY = { /** Card/section titles */ - cardTitle: "text-sm font-medium text-muted-foreground", + cardTitle: "text-xs font-medium text-muted-foreground uppercase tracking-wide", /** Primary metric values */ - cardValue: "text-2xl font-semibold tracking-tight", + cardValue: "text-lg font-semibold tracking-tight", /** Large hero metrics (real-time, attention-grabbing) */ - cardValueLarge: "text-3xl font-bold tracking-tight", + cardValueLarge: "text-lg font-bold tracking-tight", + /** Hero stat value (extra large prominent numbers) */ + cardValueHero: "text-xl font-bold tracking-tighter", /** Small metric values */ - cardValueSmall: "text-xl font-semibold tracking-tight", + cardValueSmall: "text-lg font-semibold tracking-tight", /** Supporting descriptions */ - cardDescription: "text-sm text-muted-foreground", + cardDescription: "text-xs text-muted-foreground", /** Section headings within cards */ sectionTitle: "text-base font-semibold", /** Table headers */ @@ -55,6 +63,8 @@ export const TYPOGRAPHY = { tableCell: "text-sm", /** Small labels */ label: "text-xs font-medium text-muted-foreground", + /** Micro labels for dense data displays */ + microLabel: "text-[10px] font-medium text-muted-foreground uppercase tracking-widest", } as const; /** @@ -81,6 +91,24 @@ export const RADIUS = { input: "rounded-md", } as const; +/** + * Motion and animation tokens for polished interactions + */ +export const MOTION = { + /** Standard card hover transition */ + cardHover: "transition-all duration-200 ease-out", + /** Value change animation (numbers, colors) */ + valueChange: "transition-[color,transform] duration-300", + /** Fade in animation */ + fadeIn: "animate-in fade-in duration-300", + /** Slide up and fade in */ + slideUp: "animate-in slide-in-from-bottom-2 fade-in duration-300", + /** Quick micro-interaction */ + quick: "transition-all duration-150 ease-out", + /** Smooth state change */ + smooth: "transition-all duration-300 ease-in-out", +} as const; + /** * Scrollable container styling * Uses the dashboard-scroll utility class defined in index.css @@ -180,14 +208,14 @@ export const FINANCIAL_COLORS = { * IMPORTANT: These MUST match the CSS variables in index.css */ export const METRIC_COLORS = { - revenue: "#10b981", // Emerald - Primary positive metric (--chart-revenue) - orders: "#3b82f6", // Blue - Count/volume metrics (--chart-orders) - aov: "#8b5cf6", // Purple/Violet - Calculated/derived metrics (--chart-aov) - comparison: "#f59e0b", // Amber - Previous period comparison (--chart-comparison) - expense: "#f97316", // Orange - Costs/expenses (--chart-expense) - profit: "#22c55e", // Green - Profit metrics (--chart-profit) - secondary: "#06b6d4", // Cyan - Secondary metrics (--chart-secondary) - tertiary: "#ec4899", // Pink - Tertiary metrics (--chart-tertiary) + revenue: "#059669", // Emerald-600 - Primary positive metric, deeper professional tone + orders: "#2563eb", // Blue-600 - Count/volume metrics, richer blue + aov: "#7c3aed", // Violet-600 - Calculated/derived metrics, deeper purple + comparison: "#d97706", // Amber-600 - Previous period comparison, warmer amber + expense: "#ea580c", // Orange-600 - Costs/expenses, less neon + profit: "#16a34a", // Green-600 - Profit metrics, professional green + secondary: "#0891b2", // Cyan-600 - Secondary metrics, deeper cyan + tertiary: "#db2777", // Pink-600 - Tertiary metrics, richer pink } as const; /** @@ -243,7 +271,7 @@ export const STATUS_COLORS = { * Stat card icon styling */ export const STAT_ICON_STYLES = { - container: "p-2 rounded-lg", + container: "p-2 rounded-full", icon: "h-4 w-4", // Color variants for icons colors: {