Untitled
'use client' import { useEffect, useState, useMemo } from 'react' import { useTheme } from 'next-themes' import { Label, PolarGrid, PolarRadiusAxis, RadialBar, RadialBarChart } from "recharts" import { Card, CardContent } from "@/components/ui/card" import { ChartContainer, ChartConfig } from "@/components/ui/chart" import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip" import { cn } from "@/lib/utils" import { useSettings } from "@/hooks/useSettings" import { useTranslation, type SupportedLanguages } from "@/utils/translations" import { Timer, AlertTriangle, CheckCircle2 } from "lucide-react" import { format, formatDistanceToNow } from 'date-fns' interface CircularProgressProps { binId: string fillLevel: number color: string darkColor: string name: string prediction?: { predicted_full_time: string current_level: number fill_rate: number updated_at: string } } const DEFAULT_BINS = ['Biomüll', 'Gelber Sack', 'Papier', 'Restmüll'] export function CircularProgress({ binId, fillLevel, color, darkColor, name, prediction }: CircularProgressProps) { const { theme } = useTheme() const [progress, setProgress] = useState(0) const { settings } = useSettings() const { t } = useTranslation(settings?.language as SupportedLanguages || 'EN') useEffect(() => { const timer = setTimeout(() => setProgress(fillLevel), 100) return () => clearTimeout(timer) }, [fillLevel]) const isDarkMode = theme === 'dark' const bgColor = isDarkMode ? darkColor : color const progressColor = getProgressColor(fillLevel, isDarkMode) const displayName = useMemo(() => DEFAULT_BINS.includes(name) ? t(`bins.${name}`) : name, [name, t]) const chartConfig: ChartConfig = useMemo(() => ({ [binId]: { label: displayName, color: progressColor, }, }), [binId, displayName, progressColor]) const chartData = useMemo(() => ([{ name: `${binId}-progress`, value: progress, fill: progressColor }]), [binId, progress, progressColor]) const getPredictionStatus = (predictionDate: string) => { const timeUntilFull = new Date(predictionDate).getTime() - new Date().getTime() const hoursUntilFull = timeUntilFull / (1000 * 60 * 60) if (hoursUntilFull <= 1) return { color: 'text-red-500', icon: AlertTriangle } if (hoursUntilFull <= 24) return { color: 'text-amber-500', icon: Timer } return { color: 'text-green-500', icon: CheckCircle2 } } const tooltipContent = prediction ? ( <div className="flex flex-col gap-2 p-2"> <div className="flex items-center gap-2"> {(() => { const { color, icon: Icon } = getPredictionStatus(prediction.predicted_full_time) return <Icon className={cn("h-4 w-4", color)} /> })()} <span className="font-medium"> Full by {format(new Date(prediction.predicted_full_time), 'MMM d, HH:mm')} </span> </div> <div className="text-sm text-muted-foreground"> ({formatDistanceToNow(new Date(prediction.predicted_full_time), { addSuffix: true })}) </div> <div className="text-xs text-muted-foreground"> Fill rate: {prediction.fill_rate.toFixed(1)}%/hour </div> </div> ) : null return ( <TooltipProvider> <Tooltip> <TooltipTrigger asChild> <Card className="border-0 bg-transparent shadow-none w-[460px] min-w-[460px]"> <CardContent className="p-0"> <ChartContainer config={chartConfig} className="relative w-full aspect-square"> <div className="absolute inset-0 flex items-center justify-center"> <div className={cn( "rounded-full", isDarkMode ? "opacity-[0.15]" : "opacity-[0.55]" )} style={{ backgroundColor: bgColor, width: "57%", height: "57%", position: "absolute", left: "50%", top: "50%", transform: "translate(-50%, -50%)" }} /> </div> <RadialBarChart width="100%" height="100%" data={chartData} startAngle={90} endAngle={90 - (progress * 3.6)} innerRadius="65%" outerRadius="90%" barSize={12} > <PolarRadiusAxis tick={false} axisLine={false} tickLine={false} stroke="none"> <Label content={({ viewBox }) => { if (viewBox && "cx" in viewBox && "cy" in viewBox) { return ( <text x={viewBox.cx} y={viewBox.cy} textAnchor="middle" dominantBaseline="middle" className="fill-foreground text-2xl font-medium" > {displayName} </text> ); } }} /> </PolarRadiusAxis> <RadialBar background={false} dataKey="value" cornerRadius={15} className="stroke-none" /> </RadialBarChart> </ChartContainer> </CardContent> </Card> </TooltipTrigger> {tooltipContent && ( <TooltipContent> {tooltipContent} </TooltipContent> )} </Tooltip> </TooltipProvider> ) } function getProgressColor(level: number, isDark: boolean) { if (isDark) { if (level >= 80) return '#ff3b30' if (level >= 60) return '#ff9f0a' if (level >= 40) return '#ffd60a' return '#30d158' } else { if (level >= 80) return '#ff453a' if (level >= 60) return '#ff9f0a' if (level >= 40) return '#ffd60a' return '#34c759' } }
Leave a Comment