Untitled

 avatar
unknown
plain_text
12 days ago
6.0 kB
2
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'

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: "Manage Bin"
  }
}: 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 handleClick = () => {
    setIsFlipped(!isFlipped)
  }

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

  return (
    <div 
      className="perspective-1000 w-[460px] min-w-[460px] h-[460px] cursor-pointer"
      onClick={handleClick}
    >
      <div className={cn(
        "relative w-full h-full transition-transform duration-700 transform-style-preserve-3d",
        isFlipped && "rotate-y-180"
      )}>
        {/* Front side */}
        <Card className="absolute w-full h-full border-0 bg-transparent shadow-none backface-hidden">
          <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>

        {/* Back side */}
        <Card className="absolute w-full h-full border-0 shadow-none backface-hidden rotate-y-180 bg-card">
          <CardContent className="h-full flex flex-col items-center justify-center p-6 space-y-6">
            <div className="text-xl text-center font-medium">
              {backContent.text}
            </div>
            <Button 
              onClick={handleRedirect}
              className="w-48"
            >
              {backContent.buttonText}
            </Button>
          </CardContent>
        </Card>
      </div>
    </div>
  )
}

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 >= 60) return '#ff9f0a'
    if (level >= 40) return '#ffd60a'
    return '#34c759'
  }
}

const DEFAULT_BINS = ['Biomüll', 'Gelber Sack', 'Papier', 'Restmüll']
Leave a Comment