'use client'; import React, { useEffect, useState } from 'react'; import { useRouter } from 'next/navigation'; import Sidebar from '@/app/admin/components/Sidebar/sidebar'; import { Line, Bar, Pie, Doughnut } from 'react-chartjs-2'; import { apiClient } from '@/app/utils/apiClient'; import Cookies from 'js-cookie'; import { Chart as ChartJS, CategoryScale, LinearScale, PointElement, LineElement, BarElement, ArcElement, Title, Tooltip, Legend, } from 'chart.js'; import { Box, Card, CardContent, Grid, Typography, CircularProgress, useTheme, Tabs, Tab } from '@mui/material'; import TrendingUpIcon from '@mui/icons-material/TrendingUp'; import TrendingDownIcon from '@mui/icons-material/TrendingDown'; import AttachMoneyIcon from '@mui/icons-material/AttachMoney'; import LocalShippingIcon from '@mui/icons-material/LocalShipping'; import ShoppingBasketIcon from '@mui/icons-material/ShoppingBasket'; import PeopleIcon from '@mui/icons-material/People'; ChartJS.register( CategoryScale, LinearScale, PointElement, LineElement, BarElement, ArcElement, Title, Tooltip, Legend ); interface DashboardStats { totalUsers: number; totalProducts: number; totalCategories: number; totalOrders: number; totalRevenue: number; timeSeriesData: Array<{ date: string; users: number; products: number; orders: number; revenue: number; }>; growth: { users: number; products: number; orders: number; revenue: number; }; } interface OrderStatistics { dailyOrders: Array<{ date: string; count: number; revenue: number; }>; ordersByStatus: Array<{ status: string; count: number; }>; monthlyRevenue: Array<{ month: string; revenue: number; count: number; }>; topProducts: Array<{ product_id: number; product_name: string; total_quantity: number; total_revenue: number; 'product.product_primary_image': string; }>; averageOrderValue: Array<{ month: string; average: number; }>; customerStats: { totalCustomers: number; ordersPerCustomer: number; }; paymentMethodStats: Array<{ payment_method: string; count: number; total: number; }>; } interface FulfillmentStats { processingTimeStats: Array<{ month: string; processing_hours: number; }>; fulfillmentStats: { totalOrdersShipped: number; fastProcessingOrders: number; fulfillmentRate: number; }; } interface CustomerInsights { newCustomers: number; ordersByCustomerType: Array<{ customer_type: string; order_count: number; total_revenue: number; }>; topCustomers: Array<{ user_id: number; order_count: number; total_spent: number; user: { name: string; email: string; } }>; } const Dashboard = () => { const router = useRouter(); const [adminUser, setAdminUser] = useState(null); const [stats, setStats] = useState(null); const [orderStats, setOrderStats] = useState(null); const [fulfillmentStats, setFulfillmentStats] = useState(null); const [customerInsights, setCustomerInsights] = useState(null); const [loading, setLoading] = useState(true); const [tabValue, setTabValue] = useState(0); const theme = useTheme(); const handleTabChange = (event: React.SyntheticEvent, newValue: number) => { setTabValue(newValue); }; useEffect(() => { const user = localStorage.getItem('adminUser'); const token = localStorage.getItem('adminToken'); if (!user || !token) { router.push('/admin/login'); return; } const syncCookie = Cookies.get('adminTokenSync'); if (token && !syncCookie) { Cookies.set('adminTokenSync', token, { expires: 1/6, path: '/', sameSite: 'Strict' }); } setAdminUser(JSON.parse(user)); fetchDashboardStats(); fetchOrderStatistics(); }, [router]); const fetchDashboardStats = async () => { try { const response = await apiClient('/admin/dashboard/stats'); if (response.success) { setStats(response.data); } } catch (error) { console.error('Error fetching stats:', error); } finally { setLoading(false); } }; const fetchOrderStatistics = async () => { try { const [advancedStats, fulfillment, customers] = await Promise.all([ apiClient('/admin/orders/statistics/advanced'), apiClient('/admin/orders/statistics/fulfillment'), apiClient('/admin/orders/statistics/customer-insights') ]); if (advancedStats.success) { setOrderStats(advancedStats.data); } if (fulfillment.success) { setFulfillmentStats(fulfillment.data); } if (customers.success) { setCustomerInsights(customers.data); } } catch (error) { console.error('Error fetching order statistics:', error); } }; const lineChartOptions = { responsive: true, interaction: { mode: 'index' as const, intersect: false, axis: 'x' as const }, plugins: { legend: { position: 'top' as const, labels: { usePointStyle: true, padding: 20, font: { family: "'Inter', sans-serif", size: 12, weight: '500' } } }, tooltip: { backgroundColor: 'rgba(255, 255, 255, 0.95)', titleColor: '#1a1a1a', bodyColor: '#666666', bodyFont: { family: "'Inter', sans-serif" }, borderColor: 'rgba(0,0,0,0.1)', borderWidth: 1, padding: 12, boxPadding: 6, usePointStyle: true, callbacks: { label: function(context: any) { return `${context.dataset.label}: ${context.parsed.y}`; } } }, title: { display: false } }, scales: { x: { grid: { display: false }, ticks: { font: { family: "'Inter', sans-serif", size: 11 }, maxRotation: 45, minRotation: 45 } }, y: { grid: { borderDash: [4, 4], color: 'rgba(0,0,0,0.05)' }, ticks: { font: { family: "'Inter', sans-serif", size: 11 }, padding: 10 }, beginAtZero: true } }, elements: { line: { tension: 0.4, borderWidth: 2, fill: true }, point: { radius: 0, hoverRadius: 6, hitRadius: 30 } } }; const doughnutOptions = { responsive: true, plugins: { legend: { position: 'bottom' as const, labels: { usePointStyle: true, padding: 20, font: { family: "'Inter', sans-serif", size: 12 } } } }, cutout: '70%' }; if (!adminUser || loading) return ; return (
Welcome, {adminUser.name} {tabValue === 0 && ( } /> } /> } /> } /> Growth Overview { const date = new Date(d.date); return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); }) || [], datasets: [ { label: 'Users', data: stats?.timeSeriesData.map(d => d.users) || [], borderColor: '#6366f1', backgroundColor: 'rgba(99, 102, 241, 0.1)', fill: true, cubicInterpolationMode: 'monotone' }, { label: 'Products', data: stats?.timeSeriesData.map(d => d.products) || [], borderColor: '#f43f5e', backgroundColor: 'rgba(244, 63, 94, 0.1)', fill: true, cubicInterpolationMode: 'monotone' }, { label: 'Orders', data: stats?.timeSeriesData.map(d => d.orders) || [], borderColor: '#0ea5e9', backgroundColor: 'rgba(14, 165, 233, 0.1)', fill: true, cubicInterpolationMode: 'monotone' }, { label: 'Revenue ($)', data: stats?.timeSeriesData.map(d => d.revenue) || [], borderColor: '#10b981', backgroundColor: 'rgba(16, 185, 129, 0.1)', fill: true, cubicInterpolationMode: 'monotone' } ] }} /> )} {tabValue === 1 && ( } /> } /> Fulfillment Rate {fulfillmentStats?.fulfillmentStats.fulfillmentRate || 0}% {fulfillmentStats?.fulfillmentStats.fastProcessingOrders || 0} orders processed in less than 24h Orders & Revenue Trends { const date = new Date(d.date); return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); }) || [], datasets: [ { label: 'Orders', data: orderStats?.dailyOrders.map(d => d.count) || [], borderColor: '#0ea5e9', backgroundColor: 'rgba(14, 165, 233, 0.1)', fill: true }, { label: 'Revenue ($)', data: orderStats?.dailyOrders.map(d => d.revenue) || [], borderColor: '#10b981', backgroundColor: 'rgba(16, 185, 129, 0.1)', fill: true } ] }} /> Order Status Distribution item.status.charAt(0).toUpperCase() + item.status.slice(1) ) || [], datasets: [ { data: orderStats?.ordersByStatus.map(item => item.count) || [], backgroundColor: [ '#3b82f6', '#f59e0b', '#10b981', '#6366f1', '#ef4444' ], borderWidth: 0 } ] }} /> Monthly Revenue item.month) || [], datasets: [ { label: 'Revenue', data: orderStats?.monthlyRevenue.map(item => item.revenue) || [], backgroundColor: '#10b981', borderRadius: 4 } ] }} /> Payment Method Analysis item.payment_method ) || [], datasets: [ { data: orderStats?.paymentMethodStats.map(item => { // Safely handle the total value whether it's a string or number const total = item.total; return typeof total === 'string' ? parseFloat(total) : total; }) || [], backgroundColor: [ '#3b82f6', '#f59e0b', '#10b981', '#6366f1', '#ef4444' ], borderWidth: 0 } ] }} /> Top Selling Products {orderStats?.topProducts.map((product, index) => ( ))}
Product Units Sold Revenue
{product['product.product_primary_image'] && ( {product.product_name} )}
{product.product_name}
{product.total_quantity} ${typeof product.total_revenue === 'string' ? parseFloat(product.total_revenue).toFixed(2) : product.total_revenue.toFixed(2)}
)} {tabValue === 2 && ( Total Customers {orderStats?.customerStats?.totalCustomers || 0} {customerInsights?.newCustomers || 0} new customers in the past 6 months Orders Per Customer {orderStats?.customerStats?.ordersPerCustomer || '0'} Average number of orders per customer Average Order Value ${orderStats?.averageOrderValue && orderStats.averageOrderValue.length > 0 ? Number(orderStats.averageOrderValue[orderStats.averageOrderValue.length - 1].average).toFixed(2) : '0.00'} Average value of each order New vs Returning Customers item.customer_type === 'new' ? 'New Customers' : 'Returning Customers' ) || [], datasets: [ { data: customerInsights?.ordersByCustomerType.map(item => Number(item.order_count)) || [], backgroundColor: ['#3b82f6', '#10b981'], borderWidth: 0 } ] }} /> Average Order Value Trend item.month) || [], datasets: [ { label: 'Avg. Order Value', data: orderStats?.averageOrderValue.map(item => Number(item.average)) || [], borderColor: '#6366f1', backgroundColor: 'rgba(99, 102, 241, 0.1)', fill: true } ] }} /> Top Customers {customerInsights?.topCustomers.map((customer, index) => ( ))}
Customer Orders Total Spent
{customer.user.name} {customer.user.email}
{customer.order_count} ${typeof customer.total_spent === 'string' ? parseFloat(customer.total_spent).toFixed(2) : customer.total_spent.toFixed(2)}
)}
); }; const StatsCard = ({ title, value, growth, icon }: { title: string, value: number | string, growth?: number, icon?: React.ReactNode }) => ( {title} {icon} {value} {growth !== undefined && ( {growth >= 0 ? ( ) : ( )} = 0 ? 'success.main' : 'error.main'} sx={{ ml: 1 }} > {Math.abs(growth).toFixed(1)}% {growth >= 0 ? 'increase' : 'decrease'} )} ); export default Dashboard;