import { useState } from "react"; import { Cell, Label, Pie, PieChart, ResponsiveContainer, Tooltip, } from "recharts"; import type { SetupItemWithCategory } from "../hooks/useSetups"; import { useWeightUnit } from "../hooks/useWeightUnit"; import { formatWeight, type WeightUnit } from "../lib/formatters"; import { LucideIcon } from "../lib/iconData"; const CATEGORY_COLORS = [ "#374151", "#4b5563", "#6b7280", "#7f8a94", "#9ca3af", "#b0b7bf", "#c4c9cf", "#d1d5db", "#dfe2e6", "#e5e7eb", ]; const CLASSIFICATION_COLORS: Record = { base: "#6b7280", worn: "#9ca3af", consumable: "#d1d5db", }; const CLASSIFICATION_LABELS: Record = { base: "Base Weight", worn: "Worn", consumable: "Consumable", }; type ViewMode = "category" | "classification"; const VIEW_MODES: ViewMode[] = ["category", "classification"]; interface ChartDatum { name: string; weight: number; percent: number; classificationKey?: string; } interface WeightSummaryCardProps { items: SetupItemWithCategory[]; } function buildCategoryChartData(items: SetupItemWithCategory[]): ChartDatum[] { const groups = new Map(); for (const item of items) { const current = groups.get(item.categoryName) ?? 0; groups.set(item.categoryName, current + (item.weightGrams ?? 0)); } const total = items.reduce((sum, i) => sum + (i.weightGrams ?? 0), 0); return Array.from(groups.entries()) .filter(([, weight]) => weight > 0) .map(([name, weight]) => ({ name, weight, percent: total > 0 ? weight / total : 0, })); } function buildClassificationChartData( items: SetupItemWithCategory[], ): ChartDatum[] { const groups: Record = { base: 0, worn: 0, consumable: 0, }; for (const item of items) { groups[item.classification] += item.weightGrams ?? 0; } const total = Object.values(groups).reduce((a, b) => a + b, 0); return Object.entries(groups) .filter(([, weight]) => weight > 0) .map(([key, weight]) => ({ name: CLASSIFICATION_LABELS[key] ?? key, weight, percent: total > 0 ? weight / total : 0, classificationKey: key, })); } function CustomTooltip({ active, payload, unit, }: { active?: boolean; payload?: Array<{ payload: ChartDatum }>; unit: WeightUnit; }) { if (!active || !payload?.length) return null; const { name, weight, percent } = payload[0].payload; return (

{name}

{formatWeight(weight, unit)} ({(percent * 100).toFixed(1)}%)

); } function LegendRow({ color, label, weight, unit, percent, }: { color: string; label: string; weight: number; unit: WeightUnit; percent?: number; }) { return (
{label} {formatWeight(weight, unit)} {percent != null && ( {(percent * 100).toFixed(0)}% )}
); } export function WeightSummaryCard({ items }: WeightSummaryCardProps) { const unit = useWeightUnit(); const [viewMode, setViewMode] = useState("category"); const baseWeight = items.reduce( (sum, i) => i.classification === "base" ? sum + (i.weightGrams ?? 0) : sum, 0, ); const wornWeight = items.reduce( (sum, i) => i.classification === "worn" ? sum + (i.weightGrams ?? 0) : sum, 0, ); const consumableWeight = items.reduce( (sum, i) => i.classification === "consumable" ? sum + (i.weightGrams ?? 0) : sum, 0, ); const totalWeight = baseWeight + wornWeight + consumableWeight; const chartData = viewMode === "category" ? buildCategoryChartData(items) : buildClassificationChartData(items); const colors = viewMode === "category" ? CATEGORY_COLORS : chartData.map( (d) => CLASSIFICATION_COLORS[d.classificationKey ?? ""] ?? "#6366f1", ); if (totalWeight === 0) { return (

Weight Summary

No weight data to display

); } return (
{/* Header with pill toggle */}

Weight Summary

{VIEW_MODES.map((mode) => ( ))}
{/* Main content: chart + subtotals side by side */}
{/* Donut chart */}
{chartData.map((entry, index) => ( ))} } />
{/* Weight legend */}
0 ? baseWeight / totalWeight : undefined} /> 0 ? wornWeight / totalWeight : undefined} /> 0 ? consumableWeight / totalWeight : undefined} />
Total {formatWeight(totalWeight, unit)}
); }