Untitled
unknown
plain_text
10 months ago
6.9 kB
9
Indexable
// /components/circular-progress.tsx
'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 { cn } from "@/lib/utils"
import { useSettings } from "@/hooks/useSettings"
import { useTranslation, type SupportedLanguages } from "@/utils/translations"
import { Timer, AlertTriangle, Clock } from 'lucide-react'
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip"
import { useMetrics } from '@/hooks/useMetrics'
interface CircularProgressProps {
binId: string
fillLevel: number
color: string
darkColor: string
name: string
}
const DEFAULT_BINS = ['Biomüll', 'Gelber Sack', 'Papier', 'Restmüll'];
export function CircularProgress({ binId, fillLevel, color, darkColor, name }: CircularProgressProps) {
const { theme } = useTheme()
const [progress, setProgress] = useState(0)
const { settings } = useSettings()
const { t } = useTranslation(settings?.language as SupportedLanguages || 'EN')
const { metrics } = useMetrics()
const [isHovered, setIsHovered] = useState(false)
// Get prediction for this bin
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 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 getPredictionInfo = () => {
if (!prediction) return null
const predictedTime = new Date(prediction.predicted_full_time)
const now = new Date()
const hoursUntilFull = (predictedTime.getTime() - now.getTime()) / (1000 * 60 * 60)
const color = hoursUntilFull < 1 ? 'text-red-500'
: hoursUntilFull < 12 ? 'text-orange-500'
: hoursUntilFull < 24 ? 'text-yellow-500'
: 'text-green-500'
const icon = hoursUntilFull < 1 ? AlertTriangle
: hoursUntilFull < 24 ? Timer
: Clock
const formattedTime = new Intl.DateTimeFormat(
settings?.language === 'DE' ? 'de-DE' : 'en-US',
{ day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit' }
).format(predictedTime)
return { color, Icon: icon, formattedTime, fillRate: prediction.fill_rate }
}
const predictionInfo = getPredictionInfo()
return (
<TooltipProvider>
<Tooltip open={isHovered}>
<TooltipTrigger asChild>
<div
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
<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">
{/* ... rest of your chart code ... */}
<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>
</div>
</TooltipTrigger>
{predictionInfo && (
<TooltipContent
side="top"
align="center"
className="bg-card p-3"
>
<div className="flex items-center gap-3">
<div className={cn("flex items-center gap-2", predictionInfo.color)}>
<predictionInfo.Icon className="h-4 w-4" />
<span className="font-medium">
{t('predictions.fullBy', { time: predictionInfo.formattedTime })}
</span>
</div>
<span className="text-sm text-muted-foreground">
({Math.round(predictionInfo.fillRate * 10) / 10}%/h)
</span>
</div>
</TooltipContent>
)}
</Tooltip>
</TooltipProvider>
)
}Editor is loading...
Leave a Comment