Dashboard partial restyle
This commit is contained in:
@@ -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 />
|
||||||
|
|||||||
@@ -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()}
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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"
|
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -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)}
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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>}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -154,6 +154,7 @@ export {
|
|||||||
TYPOGRAPHY,
|
TYPOGRAPHY,
|
||||||
SPACING,
|
SPACING,
|
||||||
RADIUS,
|
RADIUS,
|
||||||
|
MOTION,
|
||||||
SCROLL_STYLES,
|
SCROLL_STYLES,
|
||||||
TREND_COLORS,
|
TREND_COLORS,
|
||||||
EVENT_COLORS,
|
EVENT_COLORS,
|
||||||
|
|||||||
@@ -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; }
|
||||||
|
|||||||
@@ -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: {
|
||||||
|
|||||||
Reference in New Issue
Block a user