diff --git a/src/app/(workspace)/commerce/page.tsx b/src/app/(workspace)/commerce/page.tsx new file mode 100644 index 0000000..f3eadbd --- /dev/null +++ b/src/app/(workspace)/commerce/page.tsx @@ -0,0 +1,10 @@ +import {CommerceWorkspace} from '@/features/commerce/components/CommerceWorkspace'; + +export const metadata = { + title: 'Commerce | Loyaly Merchant OS', + description: 'AI-first commerce workspace for sales, orders, revenue and business performance telemetry.', +}; + +export default function CommercePage() { + return ; +} diff --git a/src/features/commerce/components/CommerceKpiRow.tsx b/src/features/commerce/components/CommerceKpiRow.tsx new file mode 100644 index 0000000..0e9f847 --- /dev/null +++ b/src/features/commerce/components/CommerceKpiRow.tsx @@ -0,0 +1,70 @@ +'use client'; + +import {Grid} from '@astryxdesign/core/Grid'; +import {MetricCard} from '@/shared/components/patterns/MetricCard'; +import {ICONS} from '@/shared/utils/icons'; +import {formatCount, formatInrCompact, formatPct} from '@/shared/utils/format'; + +export function CommerceKpiRow() { + const kpiList = [ + { + label: 'Total Revenue', + value: 340000, + format: formatInrCompact, + icon: ICONS.revenue, + deltaPct: 14.2, + }, + { + label: 'Total Orders', + value: 1284, + format: formatCount, + icon: ICONS.purchases, + deltaPct: 8.5, + }, + { + label: 'Avg Order Value', + value: 1420, + format: formatInrCompact, + icon: ICONS.purchases, + deltaPct: 5.1, + }, + { + label: 'Net Sales', + value: 318000, + format: formatInrCompact, + icon: ICONS.revenue, + deltaPct: 13.9, + }, + { + label: 'Refunds', + value: 22000, + format: formatInrCompact, + icon: ICONS.alert, + deltaPct: -2.4, + isRiseGood: false, + }, + { + label: 'Conversion', + value: 23.8, + format: (v: number) => formatPct(v, 1), + icon: ICONS.conversion, + deltaPct: 1.4, + }, + ]; + + return ( + + {kpiList.map((kpi) => ( + + ))} + + ); +} diff --git a/src/features/commerce/components/CommerceRecentOrders.tsx b/src/features/commerce/components/CommerceRecentOrders.tsx new file mode 100644 index 0000000..f22ae98 --- /dev/null +++ b/src/features/commerce/components/CommerceRecentOrders.tsx @@ -0,0 +1,53 @@ +'use client'; + +import {Heading, Text} from '@astryxdesign/core/Text'; +import {Badge} from '@astryxdesign/core/Badge'; +import {Icon} from '@astryxdesign/core/Icon'; +import {ICONS} from '@/shared/utils/icons'; + +const RECENT_ORDERS = [ + {id: 'o1', code: '#LYT-9842', customer: 'Aarav Sharma', store: 'Indiranagar Flagship', amount: '₹2,450', method: 'UPI', status: 'completed', time: '2m ago'}, + {id: 'o2', code: '#LYT-9841', customer: 'Priya Patel', store: 'Koramangala 80ft', amount: '₹1,890', method: 'Credit Card', status: 'completed', time: '5m ago'}, + {id: 'o3', code: '#LYT-9840', customer: 'Rohan Mehta', store: 'Jayanagar 4th Block', amount: '₹3,120', method: 'Shopify', status: 'completed', time: '12m ago'}, + {id: 'o4', code: '#LYT-9839', customer: 'Ananya Roy', store: 'Whitefield Main', amount: '₹950', method: 'UPI', status: 'completed', time: '18m ago'}, + {id: 'o5', code: '#LYT-9838', customer: 'Kabir Verma', store: 'Indiranagar Flagship', amount: '₹4,200', method: 'Card', status: 'completed', time: '24m ago'}, +]; + +export function CommerceRecentOrders() { + return ( +
+
+
+ + + Recent Orders + + + Real-time completed orders across your network + +
+ +
+ +
+ {RECENT_ORDERS.map((ord) => ( +
+
+
+ {ord.code} + • {ord.customer} +
+
+ {ord.store} ({ord.method}) +
+
+
+
{ord.amount}
+
{ord.time}
+
+
+ ))} +
+
+ ); +} diff --git a/src/features/commerce/components/CommerceRevenueSummary.tsx b/src/features/commerce/components/CommerceRevenueSummary.tsx new file mode 100644 index 0000000..703152d --- /dev/null +++ b/src/features/commerce/components/CommerceRevenueSummary.tsx @@ -0,0 +1,48 @@ +'use client'; + +import {Heading, Text} from '@astryxdesign/core/Text'; +import {MetricCard} from '@/shared/components/patterns/MetricCard'; +import {formatInrCompact} from '@/shared/utils/format'; + +export function CommerceRevenueSummary() { + return ( +
+
+ + Revenue Summary + + + Financial performance rollup for the selected period + +
+ +
+ + + + +
+
+ ); +} diff --git a/src/features/commerce/components/CommerceSalesInsights.tsx b/src/features/commerce/components/CommerceSalesInsights.tsx new file mode 100644 index 0000000..b1e38c7 --- /dev/null +++ b/src/features/commerce/components/CommerceSalesInsights.tsx @@ -0,0 +1,72 @@ +'use client'; + +import {Heading, Text} from '@astryxdesign/core/Text'; +import {Badge} from '@astryxdesign/core/Badge'; +import {Icon} from '@astryxdesign/core/Icon'; +import {ICONS} from '@/shared/utils/icons'; + +const SALES_INSIGHTS = [ + { + id: 'si1', + title: 'Evening Peak Surge', + description: 'Evening hours (17:30 - 19:30) generated 38% of total daily revenue.', + intent: 'positive', + badge: 'Growth Opportunity', + }, + { + id: 'si2', + title: 'Stock Reorder Warning', + description: 'Artisanal Cold Brew Pack (18 left) is approaching stockout threshold.', + intent: 'warning', + badge: 'Action Needed', + }, + { + id: 'si3', + title: 'Indiranagar Conversion Leader', + description: 'Indiranagar delivered +18.4% MoM conversion growth after digital pick-up lanes.', + intent: 'positive', + badge: 'Best Performer', + }, +]; + +export function CommerceSalesInsights() { + return ( +
+
+
+ + + Sales Insights + + + Automated intelligence highlights & telemetry warnings + +
+
+ +
+ {SALES_INSIGHTS.map((item) => ( +
+
+ {item.title} + +
+ + {item.description} + +
+ ))} +
+
+ ); +} diff --git a/src/features/commerce/components/CommerceWorkspace.tsx b/src/features/commerce/components/CommerceWorkspace.tsx new file mode 100644 index 0000000..c0998ff --- /dev/null +++ b/src/features/commerce/components/CommerceWorkspace.tsx @@ -0,0 +1,61 @@ +'use client'; + +import {VStack} from '@astryxdesign/core/Layout'; +import {Grid} from '@astryxdesign/core/Grid'; +import {PageHeader} from '@/shared/components/primitives/PageHeader'; +import {ScopeControls} from '@/shared/components/scope/ScopeControls'; +import {useScopeLabel} from '@/features/stores/hooks/useStoreDirectory'; +import {CommerceKpiRow} from './CommerceKpiRow'; +import {RevenueChart} from './analytics/RevenueChart'; +import {OrdersChart} from './analytics/OrdersChart'; +import {PaymentBreakdown} from './analytics/PaymentBreakdown'; +import {StorePerformanceChart} from './analytics/StorePerformanceChart'; +import {TopProductsTable} from './analytics/TopProductsTable'; +import {CommerceSalesInsights} from './CommerceSalesInsights'; +import {CommerceRevenueSummary} from './CommerceRevenueSummary'; +import {CommerceRecentOrders} from './CommerceRecentOrders'; + +export function CommerceWorkspace() { + const scopeLabel = useScopeLabel(); + + return ( + + {/* Sticky Header matching Dashboard visual language */} +
+ } + /> +
+ + {/* 1 — Headline KPI Cards Row */} + + + {/* 2 — Primary Sales Trends (Revenue Trend & Orders Trend) */} + + + + + + {/* 3 — Revenue Breakdown & Store Performance */} + + + + + + {/* 4 — Top Selling Products Leaderboard & Automated Sales Insights */} + + + + + + {/* 5 — Financial Revenue Rollup & Recent Orders Live Feed */} + + + + +
+ ); +} diff --git a/src/features/commerce/components/analytics/BusinessAlerts.tsx b/src/features/commerce/components/analytics/BusinessAlerts.tsx new file mode 100644 index 0000000..461f4a7 --- /dev/null +++ b/src/features/commerce/components/analytics/BusinessAlerts.tsx @@ -0,0 +1,77 @@ +'use client'; + +import {Heading, Text} from '@astryxdesign/core/Text'; +import {Badge} from '@astryxdesign/core/Badge'; +import {Icon} from '@astryxdesign/core/Icon'; +import {ICONS} from '@/shared/utils/icons'; +import {BUSINESS_ALERTS} from '../../services/commerceService'; + +export function BusinessAlerts() { + return ( +
+ + Real-Time Business & Operational Alerts + + +
+ {BUSINESS_ALERTS.map((alert) => { + const isCritical = alert.severity === 'critical'; + const isWarning = alert.severity === 'warning'; + const isSuccess = alert.severity === 'success'; + + const borderClass = isCritical + ? 'border-rose-500/30 bg-rose-500/[0.03]' + : isWarning + ? 'border-amber-500/30 bg-amber-500/[0.03]' + : isSuccess + ? 'border-emerald-500/30 bg-emerald-500/[0.03]' + : 'border-blue-500/30 bg-blue-500/[0.03]'; + + return ( +
+
+
+ +
+ {alert.title} + + {alert.description} + +
+
+ +
+ +
+ +
+
+ ); + })} +
+
+ ); +} diff --git a/src/features/commerce/components/analytics/BusinessPulsePanel.tsx b/src/features/commerce/components/analytics/BusinessPulsePanel.tsx new file mode 100644 index 0000000..cedeea4 --- /dev/null +++ b/src/features/commerce/components/analytics/BusinessPulsePanel.tsx @@ -0,0 +1,127 @@ +'use client'; + +import {Heading, Text} from '@astryxdesign/core/Text'; +import {Badge} from '@astryxdesign/core/Badge'; +import {Icon} from '@astryxdesign/core/Icon'; +import {ICONS} from '@/shared/utils/icons'; +import {COMMERCE_KPIS, PAYMENT_BREAKDOWN_DATA} from '../../services/commerceService'; + +export function BusinessPulsePanel() { + return ( +
+ {/* Pulse Header */} +
+
+ + + Business Pulse + + + Live telemetry stream + +
+ +
+ + {/* Pulse Cards */} +
+ {/* Today's Revenue */} +
+
+
Today's Revenue
+
{COMMERCE_KPIS.revenue.value}
+
+ + {COMMERCE_KPIS.revenue.change} + +
+ + {/* Today's Orders */} +
+
+
Today's Orders
+
{COMMERCE_KPIS.orders.value}
+
+ + {COMMERCE_KPIS.orders.change} + +
+ + {/* Active Promotions */} +
+
+ + + Active Promotions + + +
+
+ Weekend Double LYT & Espresso Bundle driving +14% lift +
+
+ + {/* Inventory Alerts */} +
+
+ + + Inventory Alert + + +
+
+ Cold Brew Pack (18 left) & Barista Mug (8 left) +
+
+ + {/* Payment Split */} +
+
+ Payment Method Split + UPI 58% +
+
+ {PAYMENT_BREAKDOWN_DATA.map((p) => ( +
+ ))} +
+
+ + {/* Top Store */} +
+
Top Performing Store
+
{COMMERCE_KPIS.topStore?.value ?? 'Indiranagar Flagship'}
+
{COMMERCE_KPIS.topStore?.subtitle ?? '₹9.1L Revenue (23.8% Conv)'}
+
+ + {/* Recent Transactions */} +
+
Recent Transactions
+
+ {[ + {id: 't1', code: '#LYT-9842', name: 'Aarav Sharma', amount: '₹2,450', time: '2m ago'}, + {id: 't2', code: '#LYT-9841', name: 'Priya Patel', amount: '₹1,890', time: '5m ago'}, + {id: 't3', code: '#LYT-9840', name: 'Rohan Mehta', amount: '₹3,120', time: '12m ago'}, + ].map((t) => ( +
+
+
{t.code}
+
{t.name}
+
+
+
{t.amount}
+
{t.time}
+
+
+ ))} +
+
+
+
+ ); +} diff --git a/src/features/commerce/components/analytics/ForecastChart.tsx b/src/features/commerce/components/analytics/ForecastChart.tsx new file mode 100644 index 0000000..ba634f7 --- /dev/null +++ b/src/features/commerce/components/analytics/ForecastChart.tsx @@ -0,0 +1,38 @@ +'use client'; + +import {Heading, Text} from '@astryxdesign/core/Text'; +import {LineChartView} from '@/shared/components/charts/LineChartView'; +import {FORECAST_TREND_DATA} from '../../services/commerceService'; + +export function ForecastChart() { + return ( +
+
+
+ + Predictive Revenue & Demand Forecast + + + AI-driven 7-day revenue, demand and stock requirement projections + +
+ + 96% Confidence Band + +
+ +
+ +
+
+ ); +} diff --git a/src/features/commerce/components/analytics/HeatmapChart.tsx b/src/features/commerce/components/analytics/HeatmapChart.tsx new file mode 100644 index 0000000..697b3ad --- /dev/null +++ b/src/features/commerce/components/analytics/HeatmapChart.tsx @@ -0,0 +1,72 @@ +'use client'; + +import {Heading, Text} from '@astryxdesign/core/Text'; +import {HEATMAP_DAYS, HEATMAP_HOURS} from '../../services/commerceService'; + +function getIntensityBg(day: string, hour: string): string { + if (hour === '18:00') return 'bg-emerald-500 text-black font-bold'; + if (hour === '12:00' || hour === '14:00') return 'bg-emerald-600/80 text-white font-semibold'; + if (day === 'Sat' || day === 'Sun') return 'bg-emerald-700/60 text-white font-medium'; + return 'bg-zinc-800/80 text-zinc-400'; +} + +export function HeatmapChart() { + return ( +
+
+
+ + Hourly Sales & Footfall Heatmap + + + Sales volume intensity matrix mapped across days and operating hours + +
+
+ Low +
+ + + + +
+ Peak +
+
+ +
+ + + + + {HEATMAP_HOURS.map((h) => ( + + ))} + + + + {HEATMAP_DAYS.map((day) => ( + + + {HEATMAP_HOURS.map((hour) => { + const styleClass = getIntensityBg(day, hour); + return ( + + ); + })} + + ))} + +
Day / Hour + {h} +
{day} +
+ {hour === '18:00' ? '98%' : hour === '14:00' ? '74%' : '42%'} +
+
+
+
+ ); +} diff --git a/src/features/commerce/components/analytics/OperationalFlowcharts.tsx b/src/features/commerce/components/analytics/OperationalFlowcharts.tsx new file mode 100644 index 0000000..d9d12b4 --- /dev/null +++ b/src/features/commerce/components/analytics/OperationalFlowcharts.tsx @@ -0,0 +1,109 @@ +'use client'; + +import {Heading, Text} from '@astryxdesign/core/Text'; + +const ORDER_FLOW = [ + {step: 'Customer', detail: 'Identified / Guest'}, + {step: 'Purchase', detail: 'Cart Checkout'}, + {step: 'Payment', detail: 'UPI / Card Auth'}, + {step: 'Order', detail: 'KDS & POS Created'}, + {step: 'Packing', detail: 'Fulfillment Prep'}, + {step: 'Delivery', detail: 'Dispatch / Pickup'}, + {step: 'Completed', detail: 'Ledger Settled'}, +]; + +const REVENUE_FLOW = [ + {step: 'Visitor', detail: 'Store / Web Traffic'}, + {step: 'Conversion', detail: 'Checkout Rate'}, + {step: 'Order', detail: 'Gross Volume'}, + {step: 'Revenue', detail: 'Net Payment'}, + {step: 'Profit', detail: 'Margin Settled'}, +]; + +const INVENTORY_FLOW = [ + {step: 'Supplier', detail: 'PO Placed'}, + {step: 'Warehouse', detail: 'Stock Received'}, + {step: 'Store', detail: 'Shelf Allocation'}, + {step: 'Customer', detail: 'Item Fulfilled'}, +]; + +export function OperationalFlowcharts() { + return ( +
+
+ + Operational Flow Diagrams + + + Visual operational pipeline lifecycle maps for orders, revenue settlements and inventory movement + +
+ + {/* Order Flow Diagram */} +
+ + 1. Order Lifecycle Flow + +
+
+ {ORDER_FLOW.map((node, i) => ( +
+
+
{node.step}
+
{node.detail}
+
+ {i < ORDER_FLOW.length - 1 ? ( + + ) : null} +
+ ))} +
+
+
+ + {/* Revenue Flow Diagram */} +
+ + 2. Revenue Settlement Flow + +
+
+ {REVENUE_FLOW.map((node, i) => ( +
+
+
{node.step}
+
{node.detail}
+
+ {i < REVENUE_FLOW.length - 1 ? ( + + ) : null} +
+ ))} +
+
+
+ + {/* Inventory Flow Diagram */} +
+ + 3. Inventory & Supply Chain Flow + +
+
+ {INVENTORY_FLOW.map((node, i) => ( +
+
+
{node.step}
+
{node.detail}
+
+ {i < INVENTORY_FLOW.length - 1 ? ( + + ) : null} +
+ ))} +
+
+
+
+ ); +} diff --git a/src/features/commerce/components/analytics/OrdersChart.tsx b/src/features/commerce/components/analytics/OrdersChart.tsx new file mode 100644 index 0000000..e23cac8 --- /dev/null +++ b/src/features/commerce/components/analytics/OrdersChart.tsx @@ -0,0 +1,34 @@ +'use client'; + +import {Heading, Text} from '@astryxdesign/core/Text'; +import {BarChartView} from '@/shared/components/charts/BarChartView'; +import {ORDERS_TREND_DATA} from '../../services/commerceService'; + +export function OrdersChart() { + return ( +
+
+
+ + Orders Volume Trend + + + Completed order count grouped by hourly buckets + +
+ + 1,284 Orders Today + +
+ +
+ +
+
+ ); +} diff --git a/src/features/commerce/components/analytics/PaymentBreakdown.tsx b/src/features/commerce/components/analytics/PaymentBreakdown.tsx new file mode 100644 index 0000000..5fa870d --- /dev/null +++ b/src/features/commerce/components/analytics/PaymentBreakdown.tsx @@ -0,0 +1,162 @@ +'use client'; + +import {useState} from 'react'; +import {Heading, Text} from '@astryxdesign/core/Text'; +import {Badge} from '@astryxdesign/core/Badge'; +import {PAYMENT_BREAKDOWN_DATA} from '../../services/commerceService'; + +export function PaymentBreakdown() { + const [selectedMethod, setSelectedMethod] = useState(null); + + const activeItem = selectedMethod + ? PAYMENT_BREAKDOWN_DATA.find((p) => p.method === selectedMethod) + : null; + + return ( +
+
+
+ + Payment Methods Breakdown + + + Revenue share distribution by payment channel + +
+ + {/* Interactive Channel Filter Buttons */} +
+ + {PAYMENT_BREAKDOWN_DATA.map((item) => ( + + ))} +
+
+ + {/* Visual Ring / Stack Bar */} +
+ {PAYMENT_BREAKDOWN_DATA.map((item) => { + const isSelected = selectedMethod === null || selectedMethod === item.method; + return ( +
+ setSelectedMethod(selectedMethod === item.method ? null : item.method) + } + style={{ + width: `${item.percentage}%`, + backgroundColor: item.color, + opacity: isSelected ? 1 : 0.25, + }} + className="h-full cursor-pointer transition-all duration-300 first:rounded-l-full last:rounded-r-full hover:brightness-125" + title={`${item.method}: ${item.percentage}% (${item.amount}) — Click to filter`} + /> + ); + })} +
+ + {/* Interactive Breakdown Cards */} +
+ {PAYMENT_BREAKDOWN_DATA.map((item) => { + const isSelected = selectedMethod === item.method; + return ( +
+ setSelectedMethod(isSelected ? null : item.method) + } + className={`flex items-center justify-between p-3 rounded-xl cursor-pointer transition-all border ${ + isSelected + ? 'bg-zinc-800/90 shadow-md ring-1' + : selectedMethod !== null + ? 'bg-zinc-900/40 border-zinc-800/60 opacity-60 hover:opacity-100' + : 'bg-zinc-900/60 border-zinc-800 hover:border-zinc-700' + }`} + style={{ + borderColor: isSelected ? item.color : undefined, + }} + > +
+ +
+ + {item.method} + + + {item.percentage}% share + +
+
+ +
+
+ {item.amount} +
+ {isSelected ? ( + + ) : null} +
+
+ ); + })} +
+ + {/* Selected Channel Deep-Dive Banner */} + {activeItem ? ( +
+
+ + {activeItem.method} Channel Details: + + {activeItem.amount} collected today across all active store POS terminals. + +
+ +
+ ) : null} +
+ ); +} diff --git a/src/features/commerce/components/analytics/RevenueChart.tsx b/src/features/commerce/components/analytics/RevenueChart.tsx new file mode 100644 index 0000000..dab0453 --- /dev/null +++ b/src/features/commerce/components/analytics/RevenueChart.tsx @@ -0,0 +1,37 @@ +'use client'; + +import {Heading, Text} from '@astryxdesign/core/Text'; +import {AreaChartView} from '@/shared/components/charts/AreaChartView'; +import {REVENUE_TREND_DATA} from '../../services/commerceService'; + +export function RevenueChart() { + return ( +
+
+
+ + Revenue Trend + + + Hourly revenue performance against targets + +
+ + +14.2% Above Target + +
+ +
+ +
+
+ ); +} diff --git a/src/features/commerce/components/analytics/SalesFunnel.tsx b/src/features/commerce/components/analytics/SalesFunnel.tsx new file mode 100644 index 0000000..a03a066 --- /dev/null +++ b/src/features/commerce/components/analytics/SalesFunnel.tsx @@ -0,0 +1,62 @@ +'use client'; + +import {Heading, Text} from '@astryxdesign/core/Text'; +import {SALES_FUNNEL_STAGES} from '../../services/commerceService'; + +export function SalesFunnel() { + const maxCount = SALES_FUNNEL_STAGES[0]?.count || 1; + + return ( +
+
+
+ + Sales & Conversion Funnel + + + Customer journey from initial store visit to completed transaction + +
+
+ 23.8% Overall Conversion +
2,951 of 12,400 converted
+
+
+ + {/* Funnel Visual Stack */} +
+ {SALES_FUNNEL_STAGES.map((stage, idx) => { + const widthPercent = Math.max(25, (stage.count / maxCount) * 100); + return ( +
+
+ + + {idx + 1} + + {stage.stage} + + + {stage.count.toLocaleString()} ({stage.conversionRate}) + +
+ +
+
+ {stage.label} + {stage.count.toLocaleString()} +
+
+
+ ); + })} +
+
+ ); +} diff --git a/src/features/commerce/components/analytics/StorePerformanceChart.tsx b/src/features/commerce/components/analytics/StorePerformanceChart.tsx new file mode 100644 index 0000000..e37c357 --- /dev/null +++ b/src/features/commerce/components/analytics/StorePerformanceChart.tsx @@ -0,0 +1,43 @@ +'use client'; + +import {Heading, Text} from '@astryxdesign/core/Text'; +import {STORE_PERFORMANCE_DATA} from '../../services/commerceService'; + +export function StorePerformanceChart() { + const maxRevenue = Math.max(...STORE_PERFORMANCE_DATA.map((s) => s.Revenue)) || 1; + + return ( +
+
+ + Top Performing Outlets + + + Store revenue comparison across your network + +
+ +
+ {STORE_PERFORMANCE_DATA.map((store) => { + const widthPercent = (store.Revenue / maxRevenue) * 100; + return ( +
+
+ {store.store} + + ₹{(store.Revenue / 100000).toFixed(1)}L + +
+
+
+
+
+ ); + })} +
+
+ ); +} diff --git a/src/features/commerce/components/analytics/TopProductsTable.tsx b/src/features/commerce/components/analytics/TopProductsTable.tsx new file mode 100644 index 0000000..9c8895e --- /dev/null +++ b/src/features/commerce/components/analytics/TopProductsTable.tsx @@ -0,0 +1,62 @@ +'use client'; + +import {Heading, Text} from '@astryxdesign/core/Text'; +import {Badge} from '@astryxdesign/core/Badge'; +import {TOP_PRODUCTS_DATA} from '../../services/commerceService'; + +export function TopProductsTable() { + return ( +
+
+ + Top Selling Products Leaderboard + + + Product sales volume, revenue growth and real-time inventory counts + +
+ +
+ + + + + + + + + + + + + {TOP_PRODUCTS_DATA.map((prod) => ( + + + + + + + + + ))} + +
Product NameCategoryUnits SoldTotal RevenueGrowthStock Status
{prod.name}{prod.category}{prod.unitsSold}{prod.revenue} + + {prod.growth} + + + +
+
+
+ ); +} diff --git a/src/features/commerce/components/filters/CommerceFilterToolbar.tsx b/src/features/commerce/components/filters/CommerceFilterToolbar.tsx new file mode 100644 index 0000000..334bb0a --- /dev/null +++ b/src/features/commerce/components/filters/CommerceFilterToolbar.tsx @@ -0,0 +1,82 @@ +'use client'; + +import {HStack} from '@astryxdesign/core/Layout'; +import {Button} from '@astryxdesign/core/Button'; +import {Icon} from '@astryxdesign/core/Icon'; +import {DownloadDropdown} from '@/shared/components/patterns/DownloadDropdown'; +import {ICONS} from '@/shared/utils/icons'; +import type {CommerceFilterState} from '../../types/commerce'; + +export function CommerceFilterToolbar({ + filters, + onFilterChange, + onExport, + onCompare, +}: { + filters: CommerceFilterState; + onFilterChange: (f: CommerceFilterState) => void; + onExport: (format: 'pdf' | 'excel' | 'csv') => void; + onCompare?: () => void; +}) { + return ( + + {/* Date Range Selector */} +
+ {(['today', '7d', '30d', '90d'] as const).map((r) => ( + + ))} +
+ + {/* Store Filter */} + + + {/* Compare Button */} + {onCompare ? ( +