Untitled

 avatar
unknown
plain_text
14 days ago
5.5 kB
4
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 { ChartContainer, ChartConfig } from "@/components/ui/chart"
import { cn } from "@/lib/utils"
import { useSettings } from "@/hooks/useSettings"
import { useTranslation, type SupportedLanguages } from "@/utils/translations"
import { Button } from "@/components/ui/button"
import { useRouter } from "next/navigation"

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 [isFlipped, setIsFlipped] = useState(false)
  const { settings } = useSettings()
  const { t } = useTranslation(settings?.language as SupportedLanguages || 'EN')
  const router = useRouter()

  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 handleFlip = () => {
    setIsFlipped(!isFlipped);
  };

  const handleButtonClick = (e: React.MouseEvent) => {
    e.stopPropagation();
    router.push('/bins');
  };

  return (
    <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">
          {/* Static progress ring */}
          <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>

          {/* Flippable inner circle */}
          <div 
            className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-[57%] h-[57%] perspective-1000 cursor-pointer"
            onClick={handleFlip}
          >
            <div
              className={cn(
                'relative w-full h-full transition-transform duration-700 transform-style-3d',
                isFlipped ? '[transform:rotateY(180deg)]' : ''
              )}
            >
              {/* Front side */}
              <div
                className={cn(
                  "absolute w-full h-full rounded-full backface-hidden",
                  isDarkMode ? "opacity-[0.15]" : "opacity-[0.55]"
                )}
                style={{ backgroundColor: bgColor }}
              />

              {/* Back side */}
              <div
                className={cn(
                  'absolute w-full h-full rounded-full backface-hidden bg-card/50 backdrop-blur-sm [transform:rotateY(180deg)]',
                  'flex flex-col items-center justify-center p-4 text-center gap-3'
                )}
              >
                <p className="text-sm font-medium text-foreground">
                  {t('bins.details')}
                </p>
                <Button 
                  variant="outline" 
                  size="sm"
                  onClick={handleButtonClick}
                  className="relative z-10"
                >
                  {t('bins.viewDetails')}
                </Button>
              </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'
  }
}
Leave a Comment