Dashboard partial restyle

This commit is contained in:
2026-01-18 21:39:27 -05:00
parent 630945e901
commit d15360a7d4
12 changed files with 181 additions and 98 deletions

View File

@@ -223,7 +223,7 @@ const AircallDashboard = () => {
<CardContent className="p-6 pt-0 space-y-4"> <CardContent className="p-6 pt-0 space-y-4">
{/* Metric Cards */} {/* Metric Cards */}
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4"> <div className="grid grid-cols-2 lg:grid-cols-4 gap-4 dashboard-stagger">
{isLoading ? ( {isLoading ? (
[...Array(4)].map((_, i) => ( [...Array(4)].map((_, i) => (
<DashboardStatCardSkeleton key={i} hasSubtitle /> <DashboardStatCardSkeleton key={i} hasSubtitle />

View File

@@ -282,7 +282,7 @@ export const AnalyticsDashboard = () => {
{loading ? ( {loading ? (
<SkeletonStats /> <SkeletonStats />
) : summaryStats ? ( ) : summaryStats ? (
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4"> <div className="grid grid-cols-2 lg:grid-cols-4 gap-4 dashboard-stagger">
<DashboardStatCard <DashboardStatCard
title="Active Users" title="Active Users"
value={summaryStats.totals.activeUsers.toLocaleString()} value={summaryStats.totals.activeUsers.toLocaleString()}

View File

@@ -51,6 +51,7 @@ import {
DashboardEmptyState, DashboardEmptyState,
DashboardErrorState, DashboardErrorState,
TOOLTIP_STYLES, TOOLTIP_STYLES,
METRIC_COLORS,
} from "@/components/dashboard/shared"; } from "@/components/dashboard/shared";
type ComparisonValue = { type ComparisonValue = {
@@ -124,12 +125,13 @@ type ChartPoint = {
isFuture: boolean; isFuture: boolean;
}; };
// Chart colors mapped from semantic METRIC_COLORS tokens
const chartColors: Record<ChartSeriesKey, string> = { const chartColors: Record<ChartSeriesKey, string> = {
income: "#3b82f6", income: METRIC_COLORS.orders, // Blue - revenue/income streams
cogs: "#f97316", cogs: METRIC_COLORS.expense, // Orange - costs/expenses
cogsPercentage: "#fb923c", cogsPercentage: "#f97316", // Orange-500 - slightly brighter for percentage line
profit: "#10b981", profit: METRIC_COLORS.profit, // Green - profit metrics
margin: "#8b5cf6", margin: METRIC_COLORS.aov, // Violet - percentage/derived metrics
}; };
const SERIES_LABELS: Record<ChartSeriesKey, string> = { const SERIES_LABELS: Record<ChartSeriesKey, string> = {
@@ -1228,7 +1230,7 @@ const FinancialOverview = () => {
) : null; ) : null;
return ( return (
<Card className={`w-full ${CARD_STYLES.base}`}> <Card className={`w-full ${CARD_STYLES.elevated}`}>
<DashboardSectionHeader <DashboardSectionHeader
title="Profit & Loss Overview" title="Profit & Loss Overview"
size="large" size="large"
@@ -1450,7 +1452,7 @@ function FinancialStatGrid({
cards: FinancialStatCardConfig[]; cards: FinancialStatCardConfig[];
}) { }) {
return ( return (
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4 py-4 w-full"> <div className="grid grid-cols-2 lg:grid-cols-4 gap-4 py-4 w-full dashboard-stagger">
{cards.map((card) => ( {cards.map((card) => (
<DashboardStatCard <DashboardStatCard
key={card.key} key={card.key}

View File

@@ -288,10 +288,10 @@ const GorgiasOverview = () => {
/> />
<CardContent className="space-y-4"> <CardContent className="space-y-4">
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4"> <div className="grid grid-cols-2 sm:grid-cols-4 2xl:grid-cols-7 gap-4 dashboard-stagger">
{loading ? ( {loading ? (
[...Array(7)].map((_, i) => ( [...Array(7)].map((_, i) => (
<DashboardStatCardSkeleton key={i} size="compact" /> <DashboardStatCardSkeleton key={i} />
)) ))
) : ( ) : (
<> <>
@@ -302,9 +302,7 @@ const GorgiasOverview = () => {
value: stats.total_messages_received.delta, value: stats.total_messages_received.delta,
suffix: "", suffix: "",
} : undefined} } : undefined}
icon={Mail}
iconColor="blue"
size="compact"
/> />
<DashboardStatCard <DashboardStatCard
title="Messages Sent" title="Messages Sent"
@@ -313,9 +311,7 @@ const GorgiasOverview = () => {
value: stats.total_messages_sent.delta, value: stats.total_messages_sent.delta,
suffix: "", suffix: "",
} : undefined} } : undefined}
icon={Send}
iconColor="green"
size="compact"
/> />
<DashboardStatCard <DashboardStatCard
title="First Response" title="First Response"
@@ -325,9 +321,7 @@ const GorgiasOverview = () => {
suffix: "", suffix: "",
moreIsBetter: false, moreIsBetter: false,
} : undefined} } : undefined}
icon={Zap}
iconColor="purple"
size="compact"
/> />
<DashboardStatCard <DashboardStatCard
title="One-Touch Rate" title="One-Touch Rate"
@@ -337,9 +331,7 @@ const GorgiasOverview = () => {
value: stats.total_one_touch_tickets.delta, value: stats.total_one_touch_tickets.delta,
suffix: "%", suffix: "%",
} : undefined} } : undefined}
icon={BarChart3}
iconColor="indigo"
size="compact"
/> />
<DashboardStatCard <DashboardStatCard
title="Customer Satisfaction" title="Customer Satisfaction"
@@ -348,9 +340,7 @@ const GorgiasOverview = () => {
value: satisfactionStats.average_rating.delta, value: satisfactionStats.average_rating.delta,
suffix: "%", suffix: "%",
} : undefined} } : undefined}
icon={Star}
iconColor="orange"
size="compact"
/> />
<DashboardStatCard <DashboardStatCard
title="Survey Response Rate" title="Survey Response Rate"
@@ -360,9 +350,7 @@ const GorgiasOverview = () => {
value: satisfactionStats.response_rate.delta, value: satisfactionStats.response_rate.delta,
suffix: "%", suffix: "%",
} : undefined} } : undefined}
icon={ClipboardCheck}
iconColor="pink"
size="compact"
/> />
<DashboardStatCard <DashboardStatCard
title="Resolution Time" title="Resolution Time"
@@ -372,9 +360,7 @@ const GorgiasOverview = () => {
suffix: "", suffix: "",
moreIsBetter: false, moreIsBetter: false,
} : undefined} } : undefined}
icon={Timer}
iconColor="teal"
size="compact"
/> />
</> </>
)} )}

View File

@@ -514,7 +514,7 @@ const MetaCampaigns = () => {
} }
/> />
<CardHeader className="pt-0 pb-2"> <CardHeader className="pt-0 pb-2">
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6 gap-4"> <div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6 gap-4 dashboard-stagger">
<DashboardStatCard <DashboardStatCard
title="Active Campaigns" title="Active Campaigns"
value={formatNumber(summaryMetrics?.totalCampaigns)} value={formatNumber(summaryMetrics?.totalCampaigns)}

View File

@@ -417,7 +417,7 @@ export const RealtimeAnalytics = () => {
<DashboardErrorState error={error} className="mx-0 mb-4" /> <DashboardErrorState error={error} className="mx-0 mb-4" />
)} )}
<div className="grid grid-cols-2 gap-4 mt-1 mb-3"> <div className="grid grid-cols-2 gap-4 mt-1 mb-3 dashboard-stagger">
<DashboardStatCard <DashboardStatCard
title="Last 30 minutes" title="Last 30 minutes"
subtitle="Active users" subtitle="Active users"

View File

@@ -48,8 +48,20 @@ import {
ChartSkeleton, ChartSkeleton,
DashboardEmptyState, DashboardEmptyState,
DashboardErrorState, DashboardErrorState,
METRIC_COLORS,
} from "@/components/dashboard/shared"; } from "@/components/dashboard/shared";
// Chart color mapping using semantic tokens
const CHART_COLORS = {
revenue: METRIC_COLORS.revenue,
prevRevenue: METRIC_COLORS.comparison,
orders: METRIC_COLORS.orders,
prevOrders: METRIC_COLORS.secondary,
aov: METRIC_COLORS.aov,
prevAov: METRIC_COLORS.comparison,
movingAverage: METRIC_COLORS.comparison,
};
// Move formatCurrency to top and export it // Move formatCurrency to top and export it
export const formatCurrency = (value, minimumFractionDigits = 0) => { export const formatCurrency = (value, minimumFractionDigits = 0) => {
if (!value || isNaN(value)) return "$0"; if (!value || isNaN(value)) return "$0";
@@ -258,7 +270,7 @@ const SummaryStats = memo(({ stats = {}, projection = null, projectionLoading =
}; };
return ( return (
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4 py-4 max-w-3xl"> <div className="grid grid-cols-2 lg:grid-cols-4 gap-4 py-4 dashboard-stagger w-full">
<DashboardStatCard <DashboardStatCard
title="Total Revenue" title="Total Revenue"
value={formatCurrency(totalRevenue, false)} value={formatCurrency(totalRevenue, false)}
@@ -664,7 +676,7 @@ const SalesChart = ({ timeRange = "last30days", title = "Sales Overview" }) => {
) : null; ) : null;
return ( return (
<Card className={`w-full ${CARD_STYLES.base}`}> <Card className={`w-full ${CARD_STYLES.elevated}`}>
<DashboardSectionHeader <DashboardSectionHeader
title={title} title={title}
timeSelector={timeSelector} timeSelector={timeSelector}
@@ -828,7 +840,7 @@ const SalesChart = ({ timeRange = "last30days", title = "Sales Overview" }) => {
type="monotone" type="monotone"
dataKey="revenue" dataKey="revenue"
name="Revenue" name="Revenue"
stroke="#8b5cf6" stroke={CHART_COLORS.revenue}
strokeWidth={2} strokeWidth={2}
dot={false} dot={false}
/> />
@@ -839,7 +851,7 @@ const SalesChart = ({ timeRange = "last30days", title = "Sales Overview" }) => {
type="monotone" type="monotone"
dataKey="prevRevenue" dataKey="prevRevenue"
name="Previous Revenue" name="Previous Revenue"
stroke="#f97316" stroke={CHART_COLORS.prevRevenue}
strokeWidth={2} strokeWidth={2}
dot={false} dot={false}
strokeDasharray="5 5" strokeDasharray="5 5"
@@ -851,7 +863,7 @@ const SalesChart = ({ timeRange = "last30days", title = "Sales Overview" }) => {
type="monotone" type="monotone"
dataKey="orders" dataKey="orders"
name="Orders" name="Orders"
stroke="#10b981" stroke={CHART_COLORS.orders}
strokeWidth={2} strokeWidth={2}
dot={false} dot={false}
/> />
@@ -862,7 +874,7 @@ const SalesChart = ({ timeRange = "last30days", title = "Sales Overview" }) => {
type="monotone" type="monotone"
dataKey="prevOrders" dataKey="prevOrders"
name="Previous Orders" name="Previous Orders"
stroke="#0ea5e9" stroke={CHART_COLORS.prevOrders}
strokeWidth={2} strokeWidth={2}
dot={false} dot={false}
strokeDasharray="5 5" strokeDasharray="5 5"
@@ -874,7 +886,7 @@ const SalesChart = ({ timeRange = "last30days", title = "Sales Overview" }) => {
type="monotone" type="monotone"
dataKey="avgOrderValue" dataKey="avgOrderValue"
name="Avg Order Value" name="Avg Order Value"
stroke="#9333ea" stroke={CHART_COLORS.aov}
strokeWidth={2} strokeWidth={2}
dot={false} dot={false}
/> />
@@ -885,7 +897,7 @@ const SalesChart = ({ timeRange = "last30days", title = "Sales Overview" }) => {
type="monotone" type="monotone"
dataKey="prevAvgOrderValue" dataKey="prevAvgOrderValue"
name="Previous Avg Order" name="Previous Avg Order"
stroke="#f59e0b" stroke={CHART_COLORS.prevAov}
strokeWidth={2} strokeWidth={2}
dot={false} dot={false}
strokeDasharray="5 5" strokeDasharray="5 5"
@@ -897,7 +909,7 @@ const SalesChart = ({ timeRange = "last30days", title = "Sales Overview" }) => {
type="monotone" type="monotone"
dataKey="movingAverage" dataKey="movingAverage"
name="7-Day Average" name="7-Day Average"
stroke="#f59e0b" stroke={CHART_COLORS.movingAverage}
strokeWidth={2} strokeWidth={2}
dot={false} dot={false}
strokeDasharray="5 5" strokeDasharray="5 5"

View File

@@ -1583,11 +1583,10 @@ const StatCards = ({
<div className="flex flex-col space-y-2"> <div className="flex flex-col space-y-2">
<div className="flex justify-between items-start"> <div className="flex justify-between items-start">
<div> <div>
<CardTitle className="text-xl font-semibold text-gray-900 dark:text-gray-100"> <CardTitle className="text-xl font-semibold text-gray-900 dark:text-gray-100 flex items-baseline gap-1">
{title} {title}
</CardTitle>
{lastUpdate && !loading && ( {lastUpdate && !loading && (
<CardDescription className="text-xs"> <div className="text-xs text-muted-foreground font-medium">
Last updated {lastUpdate.toFormat("h:mm a")} Last updated {lastUpdate.toFormat("h:mm a")}
{projection?.confidence > 0 && !projectionLoading && ( {projection?.confidence > 0 && !projectionLoading && (
<TooltipProvider> <TooltipProvider>
@@ -1613,8 +1612,10 @@ const StatCards = ({
</Tooltip> </Tooltip>
</TooltipProvider> </TooltipProvider>
)} )}
</CardDescription> </div>
)} )}
</CardTitle>
</div> </div>
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
@@ -1635,9 +1636,9 @@ const StatCards = ({
</div> </div>
</CardHeader> </CardHeader>
<CardContent className="p-6 pt-0"> <CardContent className="p-6 pt-0">
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-3 2xl:grid-cols-4 gap-2 md:gap-3"> <div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-3 2xl:grid-cols-4 gap-2 md:gap-3 dashboard-stagger">
<DashboardStatCard <DashboardStatCard
title="Total Revenue" title="Revenue"
value={formatCurrency(stats?.revenue || 0)} value={formatCurrency(stats?.revenue || 0)}
subtitle={ subtitle={
stats?.periodProgress < 100 ? ( stats?.periodProgress < 100 ? (
@@ -1679,7 +1680,7 @@ const StatCards = ({
/> />
<DashboardStatCard <DashboardStatCard
title="Average Order Value" title="AOV"
value={stats?.averageOrderValue?.toFixed(2)} value={stats?.averageOrderValue?.toFixed(2)}
valuePrefix="$" valuePrefix="$"
subtitle={`${stats?.averageItemsPerOrder?.toFixed(1)} items per order`} subtitle={`${stats?.averageItemsPerOrder?.toFixed(1)} items per order`}
@@ -1691,7 +1692,7 @@ const StatCards = ({
/> />
<DashboardStatCard <DashboardStatCard
title="Brands & Categories" title="Brands"
value={stats?.brands?.total || 0} value={stats?.brands?.total || 0}
subtitle={`${stats?.categories?.total || 0} categories`} subtitle={`${stats?.categories?.total || 0} categories`}
icon={Tags} icon={Tags}

View File

@@ -40,7 +40,7 @@ import {
TooltipContent, TooltipContent,
TooltipTrigger, TooltipTrigger,
} from "@/components/ui/tooltip"; } from "@/components/ui/tooltip";
import { TrendingUp, TrendingDown, Minus, Info, type LucideIcon } from "lucide-react"; import { ArrowUp, ArrowDown, Minus, Info, type LucideIcon } from "lucide-react";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { import {
CARD_STYLES, CARD_STYLES,
@@ -124,13 +124,13 @@ const TrendIndicator: React.FC<TrendIndicatorProps> = ({
const IconComponent = const IconComponent =
direction === "up" direction === "up"
? TrendingUp ? ArrowUp
: direction === "down" : direction === "down"
? TrendingDown ? ArrowDown
: Minus; : Minus;
const iconSize = size === "compact" ? "h-3 w-3" : "h-4 w-4"; const iconSize = size === "compact" ? "h-3 w-3" : "h-3 w-3";
const textSize = size === "compact" ? "text-xs" : "text-sm"; const textSize = size === "compact" ? "text-xs" : "text-xs";
// Format the value - use fixed decimal for percentages, integer for absolute values // Format the value - use fixed decimal for percentages, integer for absolute values
const formattedValue = suffix === "%" const formattedValue = suffix === "%"
@@ -243,9 +243,9 @@ export const DashboardStatCard: React.FC<DashboardStatCardProps> = ({
// Format the display value with prefix/suffix // Format the display value with prefix/suffix
const formattedValue = ( const formattedValue = (
<> <>
{valuePrefix && <span className="text-muted-foreground">{valuePrefix}</span>} {valuePrefix && <span className="">{valuePrefix}</span>}
{typeof value === "number" ? value.toLocaleString() : value} {typeof value === "number" ? value.toLocaleString() : value}
{valueSuffix && <span className="text-muted-foreground text-lg">{valueSuffix}</span>} {valueSuffix && <span className="">{valueSuffix}</span>}
</> </>
); );

View File

@@ -154,6 +154,7 @@ export {
TYPOGRAPHY, TYPOGRAPHY,
SPACING, SPACING,
RADIUS, RADIUS,
MOTION,
SCROLL_STYLES, SCROLL_STYLES,
TREND_COLORS, TREND_COLORS,
EVENT_COLORS, EVENT_COLORS,

View File

@@ -63,18 +63,18 @@
--card-glass: 0 0% 100%; --card-glass: 0 0% 100%;
--card-glass-foreground: 222.2 84% 4.9%; --card-glass-foreground: 222.2 84% 4.9%;
/* Semantic chart colors - EXACT values for consistency */ /* Semantic chart colors - Professional palette with deeper tones */
--chart-revenue: 160.1 84.1% 39.4%; /* #10b981 - emerald-500 */ --chart-revenue: 161.4 93.5% 30.4%; /* #059669 - emerald-600 */
--chart-orders: 217.2 91.2% 59.8%; /* #3b82f6 - blue-500 */ --chart-orders: 221.2 83.2% 53.3%; /* #2563eb - blue-600 */
--chart-aov: 258.3 89.5% 66.3%; /* #8b5cf6 - violet-500 */ --chart-aov: 262.1 83.3% 57.8%; /* #7c3aed - violet-600 */
--chart-comparison: 37.7 92.1% 50.2%; /* #f59e0b - amber-500 */ --chart-comparison: 32.1 94.6% 43.7%; /* #d97706 - amber-600 */
--chart-expense: 24.6 95% 53.1%; /* #f97316 - orange-500 */ --chart-expense: 20.5 90.2% 48.2%; /* #ea580c - orange-600 */
--chart-profit: 142.1 76.2% 45.7%; /* #22c55e - green-500 */ --chart-profit: 142.1 76.2% 36.3%; /* #16a34a - green-600 */
--chart-secondary: 187.9 85.7% 53.3%; /* #06b6d4 - cyan-500 */ --chart-secondary: 188.7 94.5% 42.7%; /* #0891b2 - cyan-600 */
--chart-tertiary: 330.4 81.2% 60.4%; /* #ec4899 - pink-500 */ --chart-tertiary: 335.1 77.6% 50%; /* #db2777 - pink-600 */
/* Trend colors */ /* Trend colors - matches revenue for consistency */
--trend-positive: 160.1 84.1% 39.4%; /* emerald-500 */ --trend-positive: 161.4 93.5% 30.4%; /* emerald-600 */
--trend-positive-muted: 158.1 64.4% 91.6%; /* emerald-100 */ --trend-positive-muted: 158.1 64.4% 91.6%; /* emerald-100 */
--trend-negative: 346.8 77.2% 49.8%; /* rose-500 */ --trend-negative: 346.8 77.2% 49.8%; /* rose-500 */
--trend-negative-muted: 355.7 100% 94.7%; /* rose-100 */ --trend-negative-muted: 355.7 100% 94.7%; /* rose-100 */
@@ -131,18 +131,18 @@
--card-glass: 222.2 47.4% 11.2%; --card-glass: 222.2 47.4% 11.2%;
--card-glass-foreground: 210 40% 98%; --card-glass-foreground: 210 40% 98%;
/* Semantic chart colors - slightly brighter in dark mode for visibility */ /* Semantic chart colors - brighter in dark mode for visibility (one step lighter) */
--chart-revenue: 158.1 64.4% 51.6%; /* emerald-400 */ --chart-revenue: 160.1 84.1% 39.4%; /* #10b981 - emerald-500 */
--chart-orders: 213.1 93.9% 67.8%; /* blue-400 */ --chart-orders: 217.2 91.2% 59.8%; /* #3b82f6 - blue-500 */
--chart-aov: 255.1 91.7% 76.3%; /* violet-400 */ --chart-aov: 258.3 89.5% 66.3%; /* #8b5cf6 - violet-500 */
--chart-comparison: 43.3 96.4% 56.3%; /* amber-400 */ --chart-comparison: 37.7 92.1% 50.2%; /* #f59e0b - amber-500 */
--chart-expense: 27.0 96.0% 61.0%; /* orange-400 */ --chart-expense: 24.6 95% 53.1%; /* #f97316 - orange-500 */
--chart-profit: 141.9 69.2% 58%; /* green-400 */ --chart-profit: 142.1 76.2% 45.7%; /* #22c55e - green-500 */
--chart-secondary: 186.0 93.5% 55.7%; /* cyan-400 */ --chart-secondary: 187.9 85.7% 53.3%; /* #06b6d4 - cyan-500 */
--chart-tertiary: 328.6 85.5% 70.2%; /* pink-400 */ --chart-tertiary: 330.4 81.2% 60.4%; /* #ec4899 - pink-500 */
/* Trend colors - brighter in dark mode */ /* 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-positive-muted: 163.1 88.1% 19.6%; /* emerald-900 */
--trend-negative: 351.3 94.5% 71.4%; /* rose-400 */ --trend-negative: 351.3 94.5% 71.4%; /* rose-400 */
--trend-negative-muted: 343.1 87.7% 15.9%; /* rose-950 */ --trend-negative-muted: 343.1 87.7% 15.9%; /* rose-950 */
@@ -188,4 +188,57 @@
.dark .dashboard-scroll:hover { .dark .dashboard-scroll:hover {
scrollbar-color: rgb(75 85 99) transparent; 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; }

View File

@@ -19,18 +19,24 @@
export const CARD_STYLES = { export const CARD_STYLES = {
/** Base card appearance with glass effect (default for dashboard) */ /** Base card appearance with glass effect (default for dashboard) */
base: "card-glass", 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 */ /** 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 card without glass effect (use sparingly) */
solid: "bg-card border border-border/50 shadow-sm rounded-xl", solid: "bg-card border border-border/50 shadow-sm rounded-xl",
/** Card header layout */ /** 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 */ /** Compact header for stat cards */
headerCompact: "flex flex-row items-center justify-between px-4 pt-4 pb-2", headerCompact: "flex flex-row items-center justify-between px-4 pt-4 pb-2",
/** Card content area */ /** Card content area */
content: "p-4 pt-0", content: "p-3 pt-0",
/** Card content with extra bottom padding */ /** Card content with extra bottom padding */
contentPadded: "p-4 pt-0 pb-4", contentPadded: "p-3 pt-0 pb-4",
} as const; } as const;
/** /**
@@ -38,15 +44,17 @@ export const CARD_STYLES = {
*/ */
export const TYPOGRAPHY = { export const TYPOGRAPHY = {
/** Card/section titles */ /** 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 */ /** Primary metric values */
cardValue: "text-2xl font-semibold tracking-tight", cardValue: "text-lg font-semibold tracking-tight",
/** Large hero metrics (real-time, attention-grabbing) */ /** 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 */ /** Small metric values */
cardValueSmall: "text-xl font-semibold tracking-tight", cardValueSmall: "text-lg font-semibold tracking-tight",
/** Supporting descriptions */ /** Supporting descriptions */
cardDescription: "text-sm text-muted-foreground", cardDescription: "text-xs text-muted-foreground",
/** Section headings within cards */ /** Section headings within cards */
sectionTitle: "text-base font-semibold", sectionTitle: "text-base font-semibold",
/** Table headers */ /** Table headers */
@@ -55,6 +63,8 @@ export const TYPOGRAPHY = {
tableCell: "text-sm", tableCell: "text-sm",
/** Small labels */ /** Small labels */
label: "text-xs font-medium text-muted-foreground", 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; } as const;
/** /**
@@ -81,6 +91,24 @@ export const RADIUS = {
input: "rounded-md", input: "rounded-md",
} as const; } 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 * Scrollable container styling
* Uses the dashboard-scroll utility class defined in index.css * 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 * IMPORTANT: These MUST match the CSS variables in index.css
*/ */
export const METRIC_COLORS = { export const METRIC_COLORS = {
revenue: "#10b981", // Emerald - Primary positive metric (--chart-revenue) revenue: "#059669", // Emerald-600 - Primary positive metric, deeper professional tone
orders: "#3b82f6", // Blue - Count/volume metrics (--chart-orders) orders: "#2563eb", // Blue-600 - Count/volume metrics, richer blue
aov: "#8b5cf6", // Purple/Violet - Calculated/derived metrics (--chart-aov) aov: "#7c3aed", // Violet-600 - Calculated/derived metrics, deeper purple
comparison: "#f59e0b", // Amber - Previous period comparison (--chart-comparison) comparison: "#d97706", // Amber-600 - Previous period comparison, warmer amber
expense: "#f97316", // Orange - Costs/expenses (--chart-expense) expense: "#ea580c", // Orange-600 - Costs/expenses, less neon
profit: "#22c55e", // Green - Profit metrics (--chart-profit) profit: "#16a34a", // Green-600 - Profit metrics, professional green
secondary: "#06b6d4", // Cyan - Secondary metrics (--chart-secondary) secondary: "#0891b2", // Cyan-600 - Secondary metrics, deeper cyan
tertiary: "#ec4899", // Pink - Tertiary metrics (--chart-tertiary) tertiary: "#db2777", // Pink-600 - Tertiary metrics, richer pink
} as const; } as const;
/** /**
@@ -243,7 +271,7 @@ export const STATUS_COLORS = {
* Stat card icon styling * Stat card icon styling
*/ */
export const STAT_ICON_STYLES = { export const STAT_ICON_STYLES = {
container: "p-2 rounded-lg", container: "p-2 rounded-full",
icon: "h-4 w-4", icon: "h-4 w-4",
// Color variants for icons // Color variants for icons
colors: { colors: {