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 (
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 (
);
})}
);
}
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 (
);
}
// 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.
);
}