'use client'; import { useEffect, useState, useCallback, type ReactNode } from 'react'; import { ArrowUpRight, ArrowDownRight, ArrowRight, RefreshCw, Calendar, ChevronDown, TrendingUp, Filter, Handshake, } from 'lucide-react'; import { ComposedChart, Bar, Area, XAxis, YAxis, Tooltip, ResponsiveContainer, CartesianGrid, } from 'recharts'; import { adminService, DashboardStats, SessionInfo, DashboardRevenuePoint, DashboardMonthlyPoint, DashboardRecentOrder, DashboardTopProduct, } from '@/services/api/adminService'; import { parseJwt, getAccessToken, apiFetch } from '@/services/api/client'; import { cn } from '@/lib/utils'; import Link from 'next/link'; import { withDashboardMock } from './mockData'; const PAGE_DATE_RANGE = 'Live Store & ERP Operations'; const CARD = 'bg-card crm-radius-section border border-border shadow-[0_1px_3px_rgba(16,24,40,0.06)] min-w-0'; const PIPELINE = [ { key: 'pending', label: 'Pending', color: '#5BA4C7' }, { key: 'processing', label: 'Processing', color: '#3B82F6' }, { key: 'shipped', label: 'Shipped', color: '#7C3AED' }, { key: 'delivered', label: 'Delivered', color: '#01B574' }, ] as const; const OVERVIEW = [ { key: 'delivered', label: 'Successful orders', color: '#01B574' }, { key: 'pending', label: 'Pending orders', color: '#5BA4C7' }, { key: 'cancelled', label: 'Cancelled', color: '#7C3AED' }, { key: 'shipped', label: 'In transit', color: '#3B82F6' }, ] as const; const DONUT_COLORS: Record = { delivered: '#01B574', shipped: '#3B82F6', processing: '#5BA4C7', pending: '#7C3AED', }; const PAYMENT_TAG: Record = { paid: 'border-[#01B574] text-[#01B574] bg-[#01B574]/8', unpaid: 'border-[#5BA4C7] text-[#5BA4C7] bg-[#5BA4C7]/10', refunded: 'border-primary text-primary bg-primary/8', }; const PRODUCT_TONES = ['#5BA4C7', '#3B82F6', '#01B574', '#1B2559', '#7C3AED']; function formatCompact(val: number) { if (val >= 1_000_000) return `₹${(val / 1_000_000).toFixed(2)}M`; if (val >= 1_000) return `₹${(val / 1_000).toFixed(val >= 10_000 ? 0 : 1)}K`; return `₹${val.toFixed(0)}`; } function formatInr(val: number) { return new Intl.NumberFormat('en-IN', { style: 'currency', currency: 'INR', maximumFractionDigits: 0, }).format(val); } function initials(name: string) { const parts = name.trim().split(/\s+/).filter(Boolean); return ((parts[0]?.[0] ?? '') + (parts[1]?.[0] ?? '')).toUpperCase() || '•'; } function samplePoints(items: T[], count: number) { if (items.length <= count) return items; const step = (items.length - 1) / (count - 1); return Array.from({ length: count }, (_, i) => items[Math.round(i * step)]); } function deltaFromWeek(week: number, month: number) { if (!month) return 2.5; const actual = week / month; const expected = 7 / 30; return ((actual - expected) / expected) * 100; } function CardTitle({ children }: { children: ReactNode }) { return (

{children}

); } function PeriodPills({ value, onChange, }: { value: 'weekly' | 'monthly' | 'yearly'; onChange: (next: 'weekly' | 'monthly' | 'yearly') => void; }) { return (
{(['weekly', 'monthly', 'yearly'] as const).map((mode) => ( ))}
); } function GhostSelect({ label }: { label: string }) { return ( {label} ); } function DeltaPill({ value }: { value: number }) { const up = value >= 0; return ( {up ? : } {up ? '+' : ''} {Math.abs(value).toFixed(1)}% ); } function StatusDonut({ slices, }: { slices: Array<{ label: string; value: number; color: string }>; }) { const total = slices.reduce((sum, slice) => sum + slice.value, 0) || 1; const cx = 90; const cy = 90; const r = 58; const stroke = 26; const circ = 2 * Math.PI * r; let offset = 0; return ( {slices.map((slice) => { const pct = slice.value / total; const dash = Math.max(pct * circ, 0); const currentOffset = offset; offset += pct; const mid = (currentOffset + pct / 2) * 2 * Math.PI - Math.PI / 2; const lx = cx + Math.cos(mid) * r; const ly = cy + Math.sin(mid) * r; return ( {pct >= 0.08 && ( {Math.round(pct * 100)}% )} ); })} ); } export default function DashboardPage() { const [stats, setStats] = useState(null); const [sessions, setSessions] = useState([]); const [loading, setLoading] = useState(true); const [userName, setUserName] = useState('Admin'); const [chartMode, setChartMode] = useState<'weekly' | 'monthly' | 'yearly'>('weekly'); const [refreshing, setRefreshing] = useState(false); useEffect(() => { const token = getAccessToken(); if (token) { const payload = parseJwt(token); if (payload) { setUserName((payload.email as string)?.split('@')[0] || 'Admin'); } } }, []); const fetchData = useCallback(async (silent = false) => { if (!silent) setLoading(true); else setRefreshing(true); try { const [statsRes, ordersRes, productsRes, categoriesRes, brandsRes, sessionsRes] = await Promise.allSettled([ adminService.getDashboardStats().catch(() => null), apiFetch('/api/v1/admin/orders').catch(() => []), apiFetch('/api/v1/catalog/products/all?limit=200').catch(() => []), apiFetch('/api/v1/catalog/categories/all').catch(() => []), apiFetch('/api/v1/catalog/brands/all').catch(() => []), adminService.listSessions(0, 50).catch(() => ({ sessions: [] })), ]); const liveStats = statsRes.status === 'fulfilled' ? statsRes.value : null; const liveOrders = ordersRes.status === 'fulfilled' && Array.isArray(ordersRes.value) ? ordersRes.value : []; const liveProductsRaw = productsRes.status === 'fulfilled' ? productsRes.value : []; const liveProducts = Array.isArray(liveProductsRaw) ? liveProductsRaw : (liveProductsRaw?.products || []); const liveCategories = categoriesRes.status === 'fulfilled' && Array.isArray(categoriesRes.value) ? categoriesRes.value : []; const liveBrands = brandsRes.status === 'fulfilled' && Array.isArray(brandsRes.value) ? brandsRes.value : []; const liveSessions = sessionsRes.status === 'fulfilled' && sessionsRes.value?.sessions ? sessionsRes.value.sessions : []; let mergedStats: DashboardStats; if (liveStats) { mergedStats = liveStats; } else if (liveOrders.length > 0 || liveProducts.length > 0) { const ordersByStatus: Record = { pending: 0, confirmed: 0, processing: 0, shipped: 0, delivered: 0, cancelled: 0, returned: 0, }; let totalRevenue = 0; const recentOrders: DashboardRecentOrder[] = []; liveOrders.forEach((o: any) => { const statusKey = (o.status || 'pending').toLowerCase(); ordersByStatus[statusKey] = (ordersByStatus[statusKey] || 0) + 1; if (['DELIVERED', 'COMPLETED', 'CONFIRMED', 'PROCESSING', 'SHIPPED', 'PAYMENT_CAPTURED', 'PAID'].includes((o.status || '').toUpperCase()) || (o.payment_status || '').toUpperCase() === 'PAID') { totalRevenue += Number(o.final_amount || 0); } if (recentOrders.length < 10) { recentOrders.push({ order_no: o.order_no || 'ORD', amount: Number(o.final_amount || 0), status: (o.status || 'pending').toLowerCase(), payment_status: (o.payment_status || 'paid').toLowerCase(), created_at: o.created_at || null, customer_name: o.customer_name || o.customer_email || 'Customer', customer_email: o.customer_email || null, }); } }); // Daily chart points from live orders const dailyMap = new Map(); liveOrders.forEach((o: any) => { if (!o.created_at) return; const d = new Date(o.created_at); const dayStr = `${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`; const existing = dailyMap.get(dayStr) || { revenue: 0, orders: 0 }; dailyMap.set(dayStr, { revenue: existing.revenue + Number(o.final_amount || 0), orders: existing.orders + 1, }); }); const dynamicDaily = Array.from(dailyMap.entries()).map(([date, val]) => ({ date, revenue: val.revenue, orders: val.orders, })); const dynamicTopProducts: DashboardTopProduct[] = liveProducts.slice(0, 5).map((p: any) => ({ name: p.name || 'Product', total_sold: p.variants?.length || 0, total_revenue: p.variants?.reduce((s: number, v: any) => s + Number(v.price || 0), 0) || 0, })); mergedStats = { total_users: liveOrders.length, total_orders: liveOrders.length, total_products: liveProducts.length, total_brands: liveBrands.length, total_device_models: 0, total_device_series: 0, total_categories: liveCategories.length, revenue_all_time: totalRevenue, revenue_last_30_days: totalRevenue, revenue_last_7_days: totalRevenue, orders_by_status: ordersByStatus, revenue_chart_daily: dynamicDaily, revenue_chart_monthly: [], top_products: dynamicTopProducts, recent_orders: recentOrders, low_stock_variants: 0, out_of_stock_variants: 0, total_stock_units: liveProducts.reduce((acc: number, p: any) => acc + (p.variants?.length || 0), 0), }; } else { mergedStats = { total_users: 0, total_orders: 0, total_products: 0, total_brands: 0, total_device_models: 0, total_device_series: 0, total_categories: 0, revenue_all_time: 0, revenue_last_30_days: 0, revenue_last_7_days: 0, orders_by_status: {}, revenue_chart_daily: [], revenue_chart_monthly: [], top_products: [], recent_orders: [], low_stock_variants: 0, out_of_stock_variants: 0, total_stock_units: 0, }; } setStats(mergedStats); setSessions(liveSessions); } finally { setLoading(false); setRefreshing(false); } }, []); useEffect(() => { fetchData(); }, [fetchData]); const { stats: displayStats } = withDashboardMock(stats, sessions); const byStatus = displayStats.orders_by_status ?? {}; const countOf = (key: string) => byStatus[key] ?? 0; const totalOrders = displayStats.total_orders ?? 0; const delivered = countOf('delivered'); const conversion = totalOrders > 0 ? (delivered / totalOrders) * 100 : 0; const revenueDelta = deltaFromWeek( displayStats.revenue_last_7_days ?? 0, displayStats.revenue_last_30_days ?? 0 ); const ordersDelta = conversion >= 40 ? 2.5 : -4.2; const conversionDelta = 15.5; const customersDelta = 2.5; const daily: DashboardRevenuePoint[] = displayStats.revenue_chart_daily ?? []; const monthly: DashboardMonthlyPoint[] = displayStats.revenue_chart_monthly ?? []; const sourceChart: Array = chartMode === 'weekly' ? samplePoints(daily, 12) : monthly; const rawPoints = sourceChart.map((row) => ({ name: 'date' in row ? row.date?.replace('08-', '') ?? '' : row.label?.slice(0, 3) ?? '', revenue: row.revenue, orders: row.orders, })); const maxRev = Math.max(...rawPoints.map((row) => row.revenue), 1); const maxOrd = Math.max(...rawPoints.map((row) => row.orders), 1); const salesScale = (maxRev * 0.72) / maxOrd; const chartPoints = rawPoints.map((row) => ({ ...row, sales: row.orders * salesScale, })); const pipelineRows = PIPELINE.map((stage) => ({ ...stage, count: countOf(stage.key), value: countOf(stage.key) * (displayStats.revenue_all_time / Math.max(totalOrders, 1)), })); const maxPipeline = Math.max(...pipelineRows.map((row) => row.count), 1); const overviewRows = OVERVIEW.map((row) => ({ ...row, count: countOf(row.key), })); const overviewTotal = overviewRows.reduce((sum, row) => sum + row.count, 0) || 1; const donutSlices = [ { label: 'Delivered', value: countOf('delivered'), color: DONUT_COLORS.delivered }, { label: 'Shipped', value: countOf('shipped'), color: DONUT_COLORS.shipped }, { label: 'Processing', value: countOf('processing') + countOf('confirmed'), color: DONUT_COLORS.processing }, { label: 'Pending', value: countOf('pending') + countOf('cancelled') + countOf('returned'), color: DONUT_COLORS.pending }, ].filter((slice) => slice.value > 0); const spark = daily.slice(-14).map((row) => row.orders); const sparkMax = Math.max(...spark, 1); const customerAvatars = (displayStats.recent_orders ?? []).slice(0, 4); if (loading) { return (
{[...Array(4)].map((_, i) => (
))}
); } return (

Dashboard

Welcome back, {userName}

{PAGE_DATE_RANGE}
Revenue Analytics

{formatCompact(displayStats.revenue_last_30_days).replace('₹', '')}

Revenue with orders (INR)

Revenue Orders
{chartPoints.length === 0 ? (
No order revenue data yet
) : ( (v >= 1000 ? `₹${(v / 1000).toFixed(0)}k` : `₹${v}`)} /> { if (!active || !payload?.length) return null; const row = payload[0]?.payload as { revenue: number; orders: number }; return (

{label}

{formatInr(row.revenue)}

{row.orders} orders

); }} />
)}
Order sources
{donutSlices.length === 0 ? (

No orders yet

) : ( )}
{donutSlices.map((slice) => (
{slice.label} {slice.value}
))}

Revenue

{formatInr(displayStats.revenue_all_time)}

From last week

Active orders

{totalOrders}

From last week

Fulfillment rate

{conversion.toFixed(1)}%

Delivered vs total

Total customers

{displayStats.total_users}

{customerAvatars.map((order, index) => ( {initials(order.customer_name)} ))} +{Math.max(displayStats.total_users - customerAvatars.length, 0)}
{spark.map((value, index) => ( ))}
Top products
{(displayStats.top_products ?? []).length === 0 ? (

No sales data yet

) : ( (displayStats.top_products ?? []).slice(0, 5).map((product, index) => (
{initials(product.name)}

{product.name}

{product.total_sold} units sold

{formatInr(product.total_revenue)}
)) )}
View All
Pipeline statistics
{pipelineRows.map((row) => (

{row.label}

{formatCompact(row.value)}

{row.count} orders

))}
{pipelineRows.map((row) => (
))}

Stock value{' '} {(displayStats.total_stock_units ?? 0).toLocaleString()} units

{spark.slice(-10).map((value, index) => ( ))}
Orders overview
{overviewRows.map((row) => ( ))}

{totalOrders}

compared to last week
{overviewRows.map((row) => (
{row.label} {row.count} orders
))}

Orders delivered {delivered}

{customerAvatars.slice(0, 4).map((order, index) => ( {initials(order.customer_name)} ))}
Recent orders View All
{(displayStats.recent_orders ?? []).length === 0 ? (

No orders yet

) : ( {(displayStats.recent_orders ?? []).map((order) => ( ))}
Order Stage Value Payment Customer
{order.order_no} {order.status} {formatInr(order.amount)} {order.payment_status || '—'} {initials(order.customer_name)} {order.customer_name}
)}
); }