import { useQuery } from '@tanstack/react-query'; import { Package, Clock, CheckCircle, XCircle, DollarSign, Printer, Target, } from 'lucide-react'; import { api } from '../api/client'; import { PrintCalendar } from '../components/PrintCalendar'; import { FilamentTrends } from '../components/FilamentTrends'; import { Dashboard, type DashboardWidget } from '../components/Dashboard'; // Widget Components function QuickStatsWidget({ stats, currency, }: { stats: { total_prints: number; successful_prints: number; failed_prints: number; total_print_time_hours: number; total_filament_grams: number; total_cost: number; } | undefined; currency: string; }) { return (

Total Prints

{stats?.total_prints || 0}

Print Time

{stats?.total_print_time_hours.toFixed(1) || 0}h

Filament Used

{((stats?.total_filament_grams || 0) / 1000).toFixed(2)}kg

Total Cost

{currency} {stats?.total_cost.toFixed(2) || '0.00'}

); } function SuccessRateWidget({ stats, }: { stats: { total_prints: number; successful_prints: number; failed_prints: number; } | undefined; }) { const successRate = stats?.total_prints ? Math.round((stats.successful_prints / stats.total_prints) * 100) : 0; return (
{successRate}%
Successful: {stats?.successful_prints || 0}
Failed: {stats?.failed_prints || 0}
); } function TimeAccuracyWidget({ stats, printerMap, }: { stats: { average_time_accuracy: number | null; time_accuracy_by_printer: Record | null; } | undefined; printerMap: Map; }) { const accuracy = stats?.average_time_accuracy; if (accuracy === null || accuracy === undefined) { return (

No time accuracy data yet

); } // Normalize accuracy for display (100% = perfect, clamp between 50-150 for gauge) const displayValue = Math.min(150, Math.max(50, accuracy)); const normalizedForGauge = ((displayValue - 50) / 100) * 100; // 50-150 -> 0-100 // Color based on accuracy const getColor = (acc: number) => { if (acc >= 95 && acc <= 105) return '#00ae42'; // Green - within 5% if (acc > 105) return '#3b82f6'; // Blue - faster than expected return '#f97316'; // Orange - slower than expected }; const color = getColor(accuracy); const deviation = accuracy - 100; return (
{accuracy.toFixed(0)}% = 0 ? 'text-blue-400' : 'text-orange-400'}`}> {deviation >= 0 ? '+' : ''}{deviation.toFixed(0)}%
100% = perfect estimate
{stats?.time_accuracy_by_printer && Object.keys(stats.time_accuracy_by_printer).length > 0 && (
{Object.entries(stats.time_accuracy_by_printer).slice(0, 3).map(([printerId, acc]) => (
{printerMap.get(printerId) || `Printer ${printerId}`} = 95 && acc <= 105 ? 'text-bambu-green' : acc > 105 ? 'text-blue-400' : 'text-orange-400' }`}> {acc.toFixed(0)}%
))}
)}
); } function FilamentTypesWidget({ stats, }: { stats: { total_prints: number; prints_by_filament_type: Record; } | undefined; }) { if (!stats?.prints_by_filament_type || Object.keys(stats.prints_by_filament_type).length === 0) { return

No filament data available

; } // Sort by print count descending const sortedEntries = Object.entries(stats.prints_by_filament_type).sort( ([, a], [, b]) => b - a ); return (
{sortedEntries.map(([type, count]) => { const percentage = Math.round((count / (stats.total_prints || 1)) * 100); return (
{type} {count} prints
); })}
); } function PrintActivityWidget({ printDates }: { printDates: string[] }) { return ; } function PrintsByPrinterWidget({ stats, printerMap, }: { stats: { prints_by_printer: Record } | undefined; printerMap: Map; }) { if (!stats?.prints_by_printer || Object.keys(stats.prints_by_printer).length === 0) { return

No printer data available

; } return (
{Object.entries(stats.prints_by_printer).map(([printerId, count]) => (

{printerMap.get(printerId) || `Printer ${printerId}`}

{count} prints

))}
); } function FilamentTrendsWidget({ archives, currency, }: { archives: Parameters[0]['archives']; currency: string; }) { if (!archives || archives.length === 0) { return

No print data available

; } return ; } export function StatsPage() { const { data: stats, isLoading } = useQuery({ queryKey: ['archiveStats'], queryFn: api.getArchiveStats, }); const { data: printers } = useQuery({ queryKey: ['printers'], queryFn: api.getPrinters, }); const { data: archives } = useQuery({ queryKey: ['archives'], queryFn: () => api.getArchives(undefined, 1000, 0), }); const { data: settings } = useQuery({ queryKey: ['settings'], queryFn: api.getSettings, }); const currency = settings?.currency || '$'; const printerMap = new Map(printers?.map((p) => [String(p.id), p.name]) || []); const printDates = archives?.map((a) => a.created_at) || []; if (isLoading) { return (
Loading statistics...
); } // Define dashboard widgets // Sizes: 1 = quarter (1/4), 2 = half (1/2), 4 = full width const widgets: DashboardWidget[] = [ { id: 'quick-stats', title: 'Quick Stats', component: , defaultSize: 2, }, { id: 'success-rate', title: 'Success Rate', component: , defaultSize: 1, }, { id: 'time-accuracy', title: 'Time Accuracy', component: , defaultSize: 1, }, { id: 'filament-types', title: 'Filament Types', component: , defaultSize: 1, }, { id: 'print-activity', title: 'Print Activity', component: , defaultSize: 2, }, { id: 'prints-by-printer', title: 'Prints by Printer', component: , defaultSize: 2, }, { id: 'filament-trends', title: 'Filament Usage Trends', component: , defaultSize: 4, }, ]; return (

Dashboard

Drag widgets to rearrange. Click the eye icon to hide.

); }