import React, { useState, useEffect } from 'react'; import { useNumberCards, useDashboardChart } from '../hooks/useApi'; import { useWorkOrders } from '../hooks/useWorkOrder'; import { useAssetMaintenanceLogs } from '../hooks/useAssetMaintenance'; import { useAssets } from '../hooks/useAsset'; import { FaShoppingCart, FaChartLine, FaBoxes, FaTools, FaCheckCircle, FaClock, FaExclamationTriangle, FaArrowUp, FaArrowDown } from 'react-icons/fa'; const ModernDashboard: React.FC = () => { const { data: numberCards, loading: cardsLoading } = useNumberCards(); const { workOrders } = useWorkOrders({}, 1000, 0); const { logs: maintenanceLogs } = useAssetMaintenanceLogs({}, 1000, 0); const { assets } = useAssets({}, 1000, 0); const [workOrderChartData, setWorkOrderChartData] = useState(null); const [workOrderGroupedChartData, setWorkOrderGroupedChartData] = useState(null); const [maintenanceAssetChartData, setMaintenanceAssetChartData] = useState(null); const [upDownTimeChartData, setUpDownTimeChartData] = useState(null); const [assigneesGroupedChartData, setAssigneesGroupedChartData] = useState(null); const [maintenanceFrequencyChartData, setMaintenanceFrequencyChartData] = useState(null); const [realWorkOrderCounts, setRealWorkOrderCounts] = useState({ open: 0, inProgress: 0, completed: 0, total: 0 }); const [insights, setInsights] = useState({ assetUptime: 0, avgResponseTime: 0, maintenanceEfficiency: 0, overdueTasks: 0, plannedMaintenance: 0 }); // Fetch all charts const { data: upDownChart } = useDashboardChart('Up & Down Time Chart'); const { data: workOrderChart } = useDashboardChart('Work Order Status Chart'); const { data: assetWiseChart } = useDashboardChart('Maintenance - Asset wise Count'); const { data: assigneesChart } = useDashboardChart('Asset Maintenance Assignees Status Count'); const { data: frequencyChart } = useDashboardChart('Asset Maintenance Frequency Chart'); const { data: ppmStatusChart } = useDashboardChart('PPM Status'); // Generate Up & Down Time Chart data from assets useEffect(() => { if (assets && assets.length > 0) { let totalUpTime = 0; let totalDownTime = 0; assets.forEach(asset => { // Sum up time and down time values const upTime = asset.custom_up_time || 0; const downTime = asset.custom_down_time || 0; totalUpTime += typeof upTime === 'number' ? upTime : 0; totalDownTime += typeof downTime === 'number' ? downTime : 0; }); // Create pie chart data const labels: string[] = []; const values: number[] = []; if (totalUpTime > 0) { labels.push('Up Time'); values.push(totalUpTime); } if (totalDownTime > 0) { labels.push('Down Time'); values.push(totalDownTime); } // If we have data, create the chart if (labels.length > 0 && values.length > 0) { // Use blue and purple color palette const pieColors: string[] = []; if (totalUpTime > 0) pieColors.push('#6366F1'); // Indigo for Up Time if (totalDownTime > 0) pieColors.push('#8B5CF6'); // Purple for Down Time setUpDownTimeChartData({ labels: labels, datasets: [{ name: 'Time', values: values, colors: pieColors }], type: 'Pie' }); } else { setUpDownTimeChartData(null); } } else { setUpDownTimeChartData(null); } }, [assets]); // Generate Work Order Status Chart data and counts useEffect(() => { if (workOrders && workOrders.length > 0) { const statusCounts: Record = {}; let openCount = 0; let inProgressCount = 0; let completedCount = 0; workOrders.forEach(wo => { const status = wo.repair_status || 'Unknown'; statusCounts[status] = (statusCounts[status] || 0) + 1; if (status.toLowerCase() === 'open') openCount++; if (status.toLowerCase() === 'in progress') inProgressCount++; if (status.toLowerCase() === 'completed') completedCount++; }); const labels = Object.keys(statusCounts); const values = Object.values(statusCounts); const colorMap: Record = { 'Open': '#F59E0B', 'In Progress': '#3B82F6', 'Pending': '#8B5CF6', 'Completed': '#10B981', 'Cancelled': '#EC4899', 'Unknown': '#6B7280' }; const colors = labels.map(label => colorMap[label] || '#6366F1'); setWorkOrderChartData({ labels, datasets: [{ name: 'Work Orders', values, colors }], type: 'bar' }); setRealWorkOrderCounts({ open: openCount, inProgress: inProgressCount, completed: completedCount, total: workOrders.length }); // Generate grouped chart data by work_order_type and repair_status const groupedData: Record> = {}; const allStatuses = new Set(); // Normalize status names to handle variations const normalizeStatus = (status: string): string => { const statusLower = status.toLowerCase().trim(); if (statusLower.includes('open')) return 'Open'; if (statusLower.includes('work in progress') || statusLower.includes('in progress') || statusLower.includes('wip')) return 'Work In Progress'; if (statusLower.includes('pending review') || statusLower.includes('pending')) return 'Pending Review'; if (statusLower.includes('completed') || statusLower.includes('complete')) return 'Completed'; if (statusLower.includes('closed')) return 'Closed'; return status; // Return original if no match }; workOrders.forEach(wo => { const workOrderType = wo.work_order_type || 'null'; const repairStatus = normalizeStatus(wo.repair_status || 'Unknown'); allStatuses.add(repairStatus); if (!groupedData[workOrderType]) { groupedData[workOrderType] = {}; } if (!groupedData[workOrderType][repairStatus]) { groupedData[workOrderType][repairStatus] = 0; } groupedData[workOrderType][repairStatus]++; }); // Create labels (work_order_type values) const workOrderTypeLabels = Object.keys(groupedData); // Create datasets for each status (order them consistently) const statusOrder = ['Open', 'Work In Progress', 'Pending Review', 'Completed', 'Closed']; const statusList = Array.from(allStatuses).sort((a, b) => { const aIndex = statusOrder.indexOf(a); const bIndex = statusOrder.indexOf(b); if (aIndex !== -1 && bIndex !== -1) return aIndex - bIndex; if (aIndex !== -1) return -1; if (bIndex !== -1) return 1; return a.localeCompare(b); }); const datasets = statusList.map(status => { const values = workOrderTypeLabels.map(type => groupedData[type][status] || 0); return { name: status, values: values }; }); setWorkOrderGroupedChartData({ labels: workOrderTypeLabels, datasets: datasets, type: 'Bar' }); } else { // Reset if no work orders setWorkOrderGroupedChartData(null); } }, [workOrders]); // Generate maintenance charts and insights useEffect(() => { if (maintenanceLogs && maintenanceLogs.length > 0) { const assetCounts: Record = {}; let completedMaintenance = 0; let plannedMaintenance = 0; let overdueMaintenance = 0; const today = new Date(); maintenanceLogs.forEach(log => { const assetName = log.asset_name || 'Unknown'; assetCounts[assetName] = (assetCounts[assetName] || 0) + 1; // Count statuses if (log.maintenance_status?.toLowerCase() === 'completed') completedMaintenance++; if (log.maintenance_status?.toLowerCase() === 'planned') plannedMaintenance++; // Check if overdue if (log.due_date && new Date(log.due_date) < today && log.maintenance_status !== 'Completed') { overdueMaintenance++; } }); const sorted = Object.entries(assetCounts).sort(([, a], [, b]) => b - a).slice(0, 10); const labels = sorted.map(([name]) => name); const values = sorted.map(([, count]) => count); const barColors = generateColors(labels.length); setMaintenanceAssetChartData({ labels, datasets: [{ name: 'Maintenance Count', values, colors: barColors }], type: 'bar' }); // Calculate maintenance efficiency const maintenanceEfficiency = maintenanceLogs.length > 0 ? ((completedMaintenance / maintenanceLogs.length) * 100) : 0; setInsights(prev => ({ ...prev, maintenanceEfficiency, overdueTasks: overdueMaintenance, plannedMaintenance })); // Generate Asset Maintenance Assignees Status Count chart data const groupedData: Record> = {}; const allStatuses = new Set(); // Normalize status names to match expected chart statuses const normalizeStatus = (status: string, log: any): string => { const statusLower = status.toLowerCase().trim(); const today = new Date(); const dueDate = log.due_date ? new Date(log.due_date) : null; // Check for overdue first if (dueDate && dueDate < today && statusLower !== 'completed' && statusLower !== 'cancelled') { return 'Overdue'; } // Map status variations if (statusLower.includes('completed on time') || statusLower === 'completed on time') { return 'Completed On Time'; } if (statusLower.includes('completed within') || statusLower.includes('within sla') || statusLower === 'completed') { return 'Completed Within SLA'; } if (statusLower.includes('delay') || statusLower.includes('late')) { return 'Delay In Completion'; } if (statusLower.includes('pending') || statusLower === 'planned') { return 'Pending'; } if (statusLower.includes('overdue')) { return 'Overdue'; } if (statusLower.includes('cancelled') || statusLower === 'cancelled') { return 'Cancelled'; } return status; // Return original if no match }; maintenanceLogs.forEach(log => { const assignee = log.assign_to_name || 'null'; const maintenanceStatus = log.maintenance_status || 'Unknown'; const normalizedStatus = normalizeStatus(maintenanceStatus, log); allStatuses.add(normalizedStatus); if (!groupedData[assignee]) { groupedData[assignee] = {}; } if (!groupedData[assignee][normalizedStatus]) { groupedData[assignee][normalizedStatus] = 0; } groupedData[assignee][normalizedStatus]++; }); // Create labels (assignee names) const assigneeLabels = Object.keys(groupedData); // Create datasets for each status (order them consistently) const statusOrder = ['Completed On Time', 'Completed Within SLA', 'Delay In Completion', 'Pending', 'Overdue', 'Cancelled']; const statusList = Array.from(allStatuses).sort((a, b) => { const aIndex = statusOrder.indexOf(a); const bIndex = statusOrder.indexOf(b); if (aIndex !== -1 && bIndex !== -1) return aIndex - bIndex; if (aIndex !== -1) return -1; if (bIndex !== -1) return 1; return a.localeCompare(b); }); const datasets = statusList.map(status => { const values = assigneeLabels.map(assignee => groupedData[assignee][status] || 0); return { name: status, values: values }; }); setAssigneesGroupedChartData({ labels: assigneeLabels, datasets: datasets, type: 'Bar' }); // Generate Maintenance Frequency Chart data const frequencyCounts: Record = {}; maintenanceLogs.forEach(log => { // Use periodicity if available, otherwise fall back to maintenance_type, or 'Other' const frequency = log.periodicity || log.maintenance_type || 'Other'; frequencyCounts[frequency] = (frequencyCounts[frequency] || 0) + 1; }); // Sort by count (descending) for better visualization const sortedFrequencies = Object.entries(frequencyCounts) .sort(([, a], [, b]) => b - a); const frequencyLabels = sortedFrequencies.map(([name]) => name); const frequencyValues = sortedFrequencies.map(([, count]) => count); const frequencyColors = generateColors(frequencyLabels.length); setMaintenanceFrequencyChartData({ labels: frequencyLabels, datasets: [{ name: 'Frequency', values: frequencyValues, colors: frequencyColors }], type: 'bar' }); } else { setAssigneesGroupedChartData(null); setMaintenanceFrequencyChartData(null); } }, [maintenanceLogs]); // Calculate asset uptime and response time from work orders useEffect(() => { if (workOrders && workOrders.length > 0) { let totalResponseHours = 0; let responseCount = 0; workOrders.forEach(wo => { // Calculate response time if we have creation and first_responded_on if (wo.creation && wo.first_responded_on) { const created = new Date(wo.creation); const responded = new Date(wo.first_responded_on); const hours = (responded.getTime() - created.getTime()) / (1000 * 60 * 60); if (hours >= 0) { totalResponseHours += hours; responseCount++; } } }); const avgResponseTime = responseCount > 0 ? totalResponseHours / responseCount : 0; // Calculate uptime (assets operational vs under maintenance) const operationalCount = workOrders.filter(wo => wo.repair_status?.toLowerCase() === 'completed' ).length; const assetUptime = workOrders.length > 0 ? ((operationalCount / workOrders.length) * 100) : 0; setInsights(prev => ({ ...prev, assetUptime, avgResponseTime })); } }, [workOrders]); if (cardsLoading) { return (
Loading dashboard...
); } return (
{/* Top Section: Side-by-Side Layout */}
{/* LEFT SIDE: 7 columns - Two cards stacked */}
{/* TOP: All 6 Stats in ONE Container */}
{/* Inside: 12-column grid, each stat takes 2 columns */}
{/* Each box takes col-span-2 (6 boxes × 2 = 12 columns) */}
} value={numberCards?.total_assets ?? 0} label="TOTAL NO. OF ASSETS" bgColor="bg-indigo-50 dark:bg-indigo-900/20" />
} value={realWorkOrderCounts.open || (numberCards?.work_orders_open ?? 0)} label="OPEN WORK ORDERS" bgColor="bg-purple-50 dark:bg-purple-900/20" />
} value={realWorkOrderCounts.inProgress || (numberCards?.work_orders_in_progress ?? 0)} label="WORK ORDERS IN PROGRESS" bgColor="bg-blue-50 dark:bg-blue-900/20" />
} value={realWorkOrderCounts.completed || (numberCards?.work_orders_completed ?? 0)} label="COMPLETED WORK ORDERS" bgColor="bg-pink-50 dark:bg-pink-900/20" />
} value={maintenanceLogs?.length || 0} label="MAINTENANCE LOGS" bgColor="bg-cyan-50 dark:bg-cyan-900/20" />
} value={realWorkOrderCounts.total} label="TOTAL WORK ORDERS" bgColor="bg-purple-50 dark:bg-purple-900/20" />
{/* BOTTOM: Completion Rate Card */}
{/* RIGHT SIDE: Asset Maintenance Assignees Status Count Chart (5 columns - Full height) */}
{/* Insights Row - Key Metrics (All Dynamic Data) */}
} title="Asset Uptime" value={`${insights.assetUptime.toFixed(1)}%`} trend={insights.assetUptime >= 90 ? "Excellent" : insights.assetUptime >= 75 ? "Good" : "Needs Attention"} trendUp={insights.assetUptime >= 75} bgColor="bg-green-50 dark:bg-green-900/20" /> } title="Avg Response Time" value={insights.avgResponseTime > 0 ? `${insights.avgResponseTime.toFixed(1)} hrs` : 'N/A'} trend={insights.avgResponseTime > 0 && insights.avgResponseTime < 4 ? "Fast Response" : "Monitor"} trendUp={insights.avgResponseTime > 0 && insights.avgResponseTime < 4} bgColor="bg-blue-50 dark:bg-blue-900/20" /> } title="Maintenance Efficiency" value={`${insights.maintenanceEfficiency.toFixed(1)}%`} trend={insights.maintenanceEfficiency >= 70 ? "On Track" : "Needs Attention"} trendUp={insights.maintenanceEfficiency >= 70} bgColor="bg-orange-50 dark:bg-orange-900/20" /> } title="Overdue Maintenance" value={insights.overdueTasks} trend={insights.overdueTasks === 0 ? "All Clear" : insights.overdueTasks <= 3 ? "Low" : "High Priority"} trendUp={insights.overdueTasks <= 3} bgColor="bg-red-50 dark:bg-red-900/20" />
{/* Second Row - Up & Down Time Chart */}
{/* Up & Down Time - Pie Chart */}
{/* Metric Cards */} {/* } label="Total Assets" value={numberCards?.total_assets?.toLocaleString() || '0'} iconColor="text-purple-600" bgColor="bg-gradient-to-br from-purple-50 to-indigo-50 dark:from-purple-900/20 dark:to-indigo-900/20" /> } label="Total Work Orders" value={realWorkOrderCounts.total.toLocaleString()} iconColor="text-white" bgColor="bg-gradient-to-br from-indigo-500 to-purple-600" textColor="text-white" /> } label="Completed Tasks" value={realWorkOrderCounts.completed.toLocaleString()} iconColor="text-white" bgColor="bg-gradient-to-br from-purple-500 to-pink-600" textColor="text-white" /> } label="Pending Tasks" value={realWorkOrderCounts.open.toLocaleString()} iconColor="text-blue-600" bgColor="bg-gradient-to-br from-blue-50 to-cyan-50 dark:from-blue-900/20 dark:to-cyan-900/20" /> */}
{/* Placeholder for additional content */}
{/* Additional Charts Section */}
{/* */} {/* Work Order Status Distribution */} {/* Work Order Status Chart */}
); }; // Mini Stat Box Component with border-right (Compact) const StatCard: React.FC<{ icon: React.ReactNode; value: string | number; label: string; bgColor: string }> = ({ icon, value, label, bgColor }) => (
{icon}
{value}
{label}
); // Insight Card Component - Shows key metrics with trends const InsightCard: React.FC<{ icon: React.ReactNode; title: string; value: string | number; trend: string; trendUp: boolean; bgColor: string; }> = ({ icon, title, value, trend, trendUp, bgColor }) => (
{icon}
{trendUp ? : } {trend}
{value}
{title}
); // Completion Rate Card with Mini Area Chart const CompletionRateCard: React.FC<{ totalWorkOrders: number; completedWorkOrders: number; inProgressWorkOrders: number; }> = ({ totalWorkOrders, completedWorkOrders, inProgressWorkOrders }) => { const completionRate = totalWorkOrders > 0 ? ((completedWorkOrders / totalWorkOrders) * 100).toFixed(2) : '0.00'; return (
{completionRate}%
Completion Rate
{/* Mini Stats */}
{totalWorkOrders}
Total
{inProgressWorkOrders}
In Progress
{completedWorkOrders}
Completed
); }; // Department Sales Card (Full Height to match left side total height) const DepartmentSalesCard: React.FC<{ data: any; totalWorkOrders: number; completedWorkOrders: number }> = ({ data, totalWorkOrders, completedWorkOrders }) => { return (

Work Order Status Distribution

{totalWorkOrders} Total
{completedWorkOrders} Completed ({totalWorkOrders > 0 ? ((completedWorkOrders / totalWorkOrders) * 100).toFixed(1) : 0}%)
); }; // Pie Chart Card with Custom Title (Compact Version) const CustomerSatisfactionCard: React.FC<{ data: any; title: string; description: string }> = ({ data, title, description }) => { return (

{title}

{description}

{data ? : (
No data available
)}
); }; // Metric Card Component (Bottom Row - Smaller & Compact) const MetricCard: React.FC<{ icon: React.ReactNode; label: string; value: string; iconColor?: string; bgColor: string; textColor?: string; }> = ({ icon, label, value, iconColor = "text-white", bgColor, textColor = "text-gray-900 dark:text-white" }) => (
{icon}
{label}
{value}
); // Mini Area Chart Component const MiniAreaChart: React.FC = () => { const data = [10, 15, 13, 17, 14, 18, 16]; const max = Math.max(...data); const width = 400; const height = 80; const points = data.map((value, i) => { const x = (i / (data.length - 1)) * width; const y = height - (value / max) * height; return `${x},${y}`; }).join(' '); const areaPoints = `0,${height} ${points} ${width},${height}`; return ( ); }; // Grouped Bar Chart Component for multiple datasets const GroupedBarChart: React.FC<{ data: any }> = ({ data }) => { const labels = data?.labels || []; const datasets = data?.datasets || []; if (!datasets.length) { return
No data available
; } const allValues = datasets.flatMap((ds: any) => ds.values || []); const max = Math.max(...allValues, 1); const chartHeight = 240; const width = Math.max(800, labels.length * 100); const groupWidth = Math.min(120, (width - 100) / labels.length); const barSpacing = 4; const numBars = datasets.length; const barWidth = Math.max(12, (groupWidth - barSpacing * (numBars + 1)) / numBars); // Color mapping for status types - Blue and Purple palette const statusColors: Record = { 'Completed On Time': '#6366F1', // Indigo 'Completed Within SLA': '#3B82F6', // Blue 'Delay In Completion': '#8B5CF6', // Purple 'Pending': '#A855F7', // Purple variant 'Overdue': '#EC4899', // Pink (for contrast/alert) 'Cancelled': '#0EA5E9', // Sky blue 'Open': '#6366F1', // Indigo 'Work In Progress': '#3B82F6', // Blue 'Pending Review': '#8B5CF6', // Purple 'Completed': '#6366F1', // Indigo 'Closed': '#A855F7', // Purple variant }; // Generate colors for datasets const getDatasetColor = (datasetName: string, index: number): string => { // Try to match by name first for (const [key, color] of Object.entries(statusColors)) { if (datasetName.toLowerCase().includes(key.toLowerCase())) { return color; } } // Fallback to generated colors return generateColors(numBars)[index]; }; return (
{datasets.map((ds: any, i: number) => { const color = getDatasetColor(ds.name || '', i); return ( ); })} {/* Grid lines */} {[0, 0.25, 0.5, 0.75, 1].map((ratio, i) => ( ))} {/* Y-axis labels */} {[0, 0.25, 0.5, 0.75, 1].map((ratio, i) => ( {(max * ratio).toFixed(ratio === 0 ? 0 : max < 5 ? 1 : 0)} ))} {/* Y-axis label */} Count {/* Grouped Bars */} {labels.map((label: string, labelIndex: number) => { const groupX = 80 + (labelIndex * groupWidth); return ( {datasets.map((dataset: any, dsIndex: number) => { const value = dataset.values?.[labelIndex] || 0; const barHeight = (value / max) * chartHeight; const x = groupX + barSpacing + (dsIndex * (barWidth + barSpacing)); const y = chartHeight - barHeight; const color = getDatasetColor(dataset.name || '', dsIndex); return ( ); })} {/* X-axis label */} {label && label.length > 12 ? label.substring(0, 10) + '...' : label || 'null'} ); })} {/* Legend */}
{datasets.map((dataset: any, i: number) => { const color = getDatasetColor(dataset.name || '', i); return (
{dataset.name && dataset.name.length > 20 ? dataset.name.substring(0, 18) + '...' : dataset.name || `Series ${i + 1}`}
); })}
); }; // Bar Chart Component with Line Overlay (Like in Image) const BarChart: React.FC<{ data: any }> = ({ data }) => { const labels = data?.labels || []; const datasets = data?.datasets || []; if (!datasets.length) { return
No data available
; } const allValues = datasets.flatMap((ds: any) => ds.values || []); const max = Math.max(...allValues, 1); const chartHeight = 250; // Increase width based on number of labels to prevent overlap const minBarSpacing = 60; // Minimum spacing between bars const calculatedWidth = Math.max(800, labels.length * minBarSpacing + 100); const width = calculatedWidth; // Generate smooth line data (Average line overlay) const lineData = datasets[0]?.values || []; return (
{/* Grid lines */} {[0, 0.25, 0.5, 0.75, 1].map((ratio, i) => ( ))} {/* Y-axis labels */} {[0, 0.25, 0.5, 0.75, 1].map((ratio, i) => ( {(max * ratio).toFixed(0)} ))} {/* Bars */} {labels.map((_label: string, i: number) => { const value = datasets[0]?.values?.[i] || 0; const barHeight = (value / max) * chartHeight; const barWidth = Math.min(40, (width - 100) / labels.length - 10); const x = 80 + (i * ((width - 100) / labels.length)); return ( ); })} {/* Line Chart Overlay */} {lineData.length > 0 && ( { const x = 80 + (i * ((width - 100) / labels.length)) + 20; const y = chartHeight - ((value / max) * chartHeight); return `${x},${y}`; }).join(' ')} fill="none" stroke="#60A5FA" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round" className="drop-shadow-md" /> )} {/* Line data points */} {lineData.map((value: number, i: number) => { const x = 80 + (i * ((width - 100) / labels.length)) + 20; const y = chartHeight - ((value / max) * chartHeight); return ( ); })} {/* X-axis labels */} {labels.map((label: string, i: number) => { const barSpacing = (width - 100) / labels.length; const x = 80 + (i * barSpacing) + (barSpacing / 2); const truncatedLabel = label && label.length > 15 ? label.substring(0, 13) + '...' : label || 'null'; return ( {truncatedLabel} ); })} {/* Legend - Inside the card */}
Total Sales
Average
); }; // Pie Chart Component const PieChart: React.FC<{ data: any }> = ({ data }) => { const labels = data?.labels || []; const values = data?.datasets?.[0]?.values || []; // Check if this is an Up & Down Time chart and ALWAYS apply blue/purple colors const isUpDownTimeChart = labels.some((label: string) => label.toLowerCase().includes('up time') || label.toLowerCase().includes('down time') || label.toLowerCase().includes('uptime') || label.toLowerCase().includes('downtime') ); let colors: string[] = []; // Always override colors for Up & Down Time charts to use blue/purple palette if (isUpDownTimeChart && labels.length >= 1) { // Apply blue/purple colors based on label order and content colors = labels.map((label: string) => { const labelLower = label.toLowerCase(); if (labelLower.includes('up time') || labelLower.includes('uptime')) { return '#6366F1'; // Indigo for Up Time } if (labelLower.includes('down time') || labelLower.includes('downtime')) { return '#8B5CF6'; // Purple for Down Time } // If label doesn't match, assign based on position (first = up, second = down) const index = labels.indexOf(label); return index === 0 ? '#6366F1' : '#8B5CF6'; }); } else { // For other charts, use custom colors if provided, otherwise generate const datasetColors = data?.datasets?.[0]?.colors; colors = datasetColors && datasetColors.length === values.length ? datasetColors : generateColors(values.length); } const total = values.reduce((sum: number, val: number) => sum + val, 0); const radius = 100; const cx = radius + 10; const cy = radius + 10; let cumulative = 0; const slices = values.map((value: number, i: number) => { const startAngle = (cumulative / total) * 2 * Math.PI - Math.PI / 2; cumulative += value; const endAngle = (cumulative / total) * 2 * Math.PI - Math.PI / 2; const largeArc = endAngle - startAngle > Math.PI ? 1 : 0; const x1 = cx + radius * Math.cos(startAngle); const y1 = cy + radius * Math.sin(startAngle); const x2 = cx + radius * Math.cos(endAngle); const y2 = cy + radius * Math.sin(endAngle); return { path: `M ${cx} ${cy} L ${x1} ${y1} A ${radius} ${radius} 0 ${largeArc} 1 ${x2} ${y2} Z`, color: colors[i], label: labels[i], value: value, percentage: ((value / total) * 100).toFixed(1) }; }); return (
{slices.map((slice: any, i: number) => ( ))}
{slices.map((slice: any, i: number) => (
{slice.label} {slice.value}
))}
); }; // Grouped Chart Card Component for charts with multiple datasets const GroupedChartCard: React.FC<{ title: string; data: any }> = ({ title, data }) => { return (

{title}

{!data || !data.datasets || !data.datasets.length ? (
No chart data available
) : ( )}
); }; // Chart Card Component (Compact Version) const ChartCard: React.FC<{ title: string; data: any; type: 'bar' | 'pie' }> = ({ title, data, type }) => { return (

{title}

{!data || !data.datasets || !data.datasets.length ? (
No chart data available
) : type === 'pie' ? ( ) : ( )}
); }; // Helper: Generate colors function generateColors(count: number): string[] { const colors = [ '#8B5CF6', // Purple '#6366F1', // Indigo '#3B82F6', // Blue '#06B6D4', // Cyan '#14B8A6', // Teal '#EC4899', // Pink '#A855F7', // Purple variant '#0EA5E9', // Sky blue '#10B981', // Emerald '#F472B6', // Pink variant '#7C3AED', // Violet '#2DD4BF', // Teal variant ]; return Array.from({ length: count }, (_, i) => colors[i % colors.length]); } export default ModernDashboard;