Untitled
unknown
plain_text
18 days ago
7.6 kB
3
Indexable
'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 { Button } from "@/components/ui/button" import { ChartContainer, ChartConfig } from "@/components/ui/chart" import { cn } from "@/lib/utils" import { useSettings } from "@/hooks/useSettings" import { useTranslation, type SupportedLanguages } from "@/utils/translations" import { useRouter } from 'next/navigation' import { Clock } from 'lucide-react' import { useMetrics } from '@/hooks/useMetrics' interface CircularProgressProps { binId: string fillLevel: number color: string darkColor: string name: string backContent?: { text: string buttonText: string } } export function CircularProgress({ binId, fillLevel, color, darkColor, name, backContent = { text: "Click to manage this bin", buttonText: "Details" } }: CircularProgressProps) { const { theme } = useTheme() const [progress, setProgress] = useState(0) const [isFlipped, setIsFlipped] = useState(false) const { settings } = useSettings() const { t } = useTranslation(settings?.language as SupportedLanguages || 'EN') const router = useRouter() const { metrics } = useMetrics() const prediction = metrics?.fill_predictions?.[binId] 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 getPredictionColor = (predictedDate: string) => { const timeUntilFull = (new Date(predictedDate).getTime() - new Date().getTime()) / (1000 * 60 * 60) // hours if (timeUntilFull <= 0.5) return 'text-red-500' // Less than 30 minutes if (timeUntilFull <= 2) return 'text-orange-500' // Less than 2 hours if (timeUntilFull <= 24) return 'text-yellow-500' // Less than 24 hours return 'text-green-500' // More than 24 hours } const formatPredictionTime = (dateString: string) => { const date = new Date(dateString) return date.toLocaleString('en-US', { day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit', hour12: false }) } 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 handleClick = () => { setIsFlipped(!isFlipped) } const handleRedirect = (e: React.MouseEvent) => { e.stopPropagation() router.push('/bins') } return ( <Card className="relative border-0 bg-transparent shadow-none w-[460px] min-w-[460px]"> <CardContent className="p-0"> <ChartContainer config={chartConfig} className="relative w-full aspect-square"> <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" /> <RadialBar background={false} dataKey="value" cornerRadius={15} className="stroke-none" /> </RadialBarChart> <div className="absolute inset-0 flex items-center justify-center cursor-pointer" onClick={handleClick} > <div className="relative perspective-1000 w-[57%] h-[57%]"> <div className={cn( "absolute w-full h-full rounded-full transition-transform duration-700 transform-style-preserve-3d", isFlipped && "rotate-y-180" )}> <div className="absolute w-full h-full rounded-full backface-hidden"> <div className={cn( "absolute inset-0 rounded-full", isDarkMode ? "opacity-[0.15]" : "opacity-[0.55]" )} style={{ backgroundColor: bgColor }} /> <div className="absolute inset-0 flex items-center justify-center"> <span className="text-2xl font-medium text-foreground"> {displayName} </span> </div> </div> <div className={cn( "absolute w-full h-full rounded-full backface-hidden rotate-y-180 flex flex-col items-center justify-center p-4", isDarkMode ? "bg-zinc-800/90 text-zinc-100" : "bg-zinc-100/90 text-zinc-800" )} > {prediction ? ( <div className="flex flex-col items-center space-y-2"> <Clock className={cn( "w-6 h-6", getPredictionColor(prediction.predicted_full_time) )} /> <p className={cn( "text-sm font-medium text-center", getPredictionColor(prediction.predicted_full_time) )}> Full by {formatPredictionTime(prediction.predicted_full_time)} </p> <Button onClick={handleRedirect} size="sm" className="w-32 text-xs mt-2" variant="outline" > {backContent.buttonText} </Button> </div> ) : ( <div className="flex flex-col items-center space-y-2"> <p className="text-sm text-center mb-2">No prediction available</p> <Button onClick={handleRedirect} size="sm" className="w-32 text-xs" variant="outline" > {backContent.buttonText} </Button> </div> )} </div> </div> </div> </div> </ChartContainer> </CardContent> </Card> ) } 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' } } const DEFAULT_BINS = ['Biomüll', 'Gelber Sack', 'Papier', 'Restmüll']
Editor is loading...
Leave a Comment