Untitled

 avatar
unknown
plain_text
2 months ago
8.0 kB
5
Indexable
"use client"

import { useMetrics } from "@/hooks/useMetrics"
import { useBinConfig } from "@/hooks/use-bin-config"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Gauge, Clock, Trash2 } from "lucide-react"
import { format, differenceInHours, subDays } from "date-fns"
import { useTranslation } from "@/utils/translations"
import type { SupportedLanguages } from "@/utils/translations"
import { useSettings } from "@/hooks/useSettings"

export function OverviewCards() {
  const { metrics } = useMetrics()
  const { bins } = useBinConfig()
  const { settings } = useSettings()
  const { t } = useTranslation(settings?.language as SupportedLanguages || 'EN')

  const formatTimeRemaining = (hours: number) => {
    if (hours >= 24) {
      const days = Math.floor(hours / 24)
      const remainingHours = hours % 24
      let result = `${days} ${days !== 1 ? t('overviewCards.time.days') : t('overviewCards.time.day')}`
      if (remainingHours > 0) {
        result += ` ${remainingHours} ${remainingHours !== 1 ? t('overviewCards.time.hours') : t('overviewCards.time.hour')}`
      }
      return result
    }
    return `${hours} ${hours !== 1 ? t('overviewCards.time.hours') : t('overviewCards.time.hour')}`
  }

  const getTimeColor = (hours: number) => {
    if (hours <= 24) return 'bg-red-500/20'
    if (hours <= 48) return 'bg-orange-500/20'
    return 'bg-green-500/20'
  }

  const getBinDisplay = (binId: string) => {
    const binNumber = binId.replace('bin', '')
    const configuredBin = bins.find(b => b.id === binId)
    return {
      name: configuredBin?.name || t('bins.Restmüll') + ` ${binNumber}`,
      fullLabel: t('bins.Restmüll') + ` ${binNumber}`,
      type: configuredBin?.name || t('common.notConfigured')
    }
  }

  // Card 1: Proactive emptying count
  const proactiveEmptyings = metrics?.proactive_emptying?.count || 0
  const totalEmptyings = metrics?.basic_metrics?.bin_emptying_counts 
    ? Object.values(metrics.basic_metrics.bin_emptying_counts).reduce((a, b) => a + b, 0)
    : 0
  const proactivePercentage = totalEmptyings > 0 
    ? Math.round((proactiveEmptyings / totalEmptyings) * 100)
    : 0

  // Card 2: Next full bin prediction (only consider future predictions)
  const predictions = metrics?.fill_predictions || {}
  const nextFullBin = metrics?.fill_predictions 
    ? Object.entries(metrics.fill_predictions).reduce((acc, [binId, prediction]) => {
        const predictedTime = new Date(prediction.predicted_full_time)
        const now = new Date()
        
        // Only consider future predictions
        if (predictedTime <= now) return acc

        if (!acc || predictedTime < acc.predictedTime) {
          return {
            binId,
            predictedTime,
            currentLevel: prediction.current_level,
            ...getBinDisplay(binId)
          }
        }
        return acc
      }, null as {
        binId: string
        predictedTime: Date
        currentLevel: number
        name: string
        fullLabel: string
        type: string
      } | null)
    : null

  // Card 3: Average fill level at emptying
  const fillLevels = metrics?.fill_level_history 
    ? Object.values(metrics.fill_level_history).flatMap(h => h.fill_levels_at_empty)
    : []
  const averageFillLevel = fillLevels.length > 0
    ? Math.round(fillLevels.reduce((a, b) => a + b, 0) / fillLevels.length)
    : null

  // Card 4: Total emptyings count with weekly calculation
  const totalEmptyingsLastWeek = metrics?.fill_level_history 
    ? Object.values(metrics.fill_level_history).reduce((total, binHistory) => {
        const lastWeek = subDays(new Date(), 7)
        return binHistory.emptying_timestamps.filter(t => 
          new Date(t) > lastWeek
        ).length + total
      }, 0)
    : 0

  // Calculate time remaining and color for next full bin
  let timeRemainingHours = 0
  let timeColorClass = ''
  if (nextFullBin) {
    timeRemainingHours = differenceInHours(nextFullBin.predictedTime, new Date())
    timeColorClass = getTimeColor(timeRemainingHours)
  }

  return (
    <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
      {/* Proactive Emptying Card */}
      <Card>
        <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
          <CardTitle className="text-sm font-medium">{t('overviewCards.proactiveEmptyings.title')}</CardTitle>
          <Trash2 className="h-5 w-5 text-muted-foreground" />
        </CardHeader>
        <CardContent>
          <div className="text-2xl font-bold">{proactiveEmptyings}</div>
          <p className="text-xs text-muted-foreground">
            {totalEmptyings} {t('overviewCards.proactiveEmptyings.total')} ({proactivePercentage}%)
          </p>
        </CardContent>
      </Card>

      {/* Next Full Bin Card */}
      <Card className={nextFullBin ? `${timeColorClass}` : ''}>
        <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
          <CardTitle className="text-sm font-medium">{t('overviewCards.nextFullBin.title')}</CardTitle>
          <Clock className="h-5 w-5 text-muted-foreground" />
        </CardHeader>
        <CardContent>
          {nextFullBin ? (
            <div className="space-y-3">
              <div className="flex items-baseline gap-2">
                <div className="text-2xl font-bold">
                  {nextFullBin.fullLabel}{" "}
                  <span className="text-lg text-muted-foreground">
                    ({t('overviewCards.nextFullBin.type')}: {nextFullBin.type})
                  </span>
                </div>
              </div>
              
              <div className="space-y-2">
                <div className="text-sm text-muted-foreground">
                  {t('overviewCards.nextFullBin.expectedFull')}{" "}
                  <span className="font-medium">
                    {format(nextFullBin.predictedTime, "dd.MM.yyyy, HH:mm")}
                  </span>
                </div>
                
                <div className={`px-3 py-1 rounded-full ${timeColorClass} text-sm font-medium w-fit`}>
                  {formatTimeRemaining(timeRemainingHours)} {t('overviewCards.nextFullBin.timeRemaining')}
                </div>
              </div>
            </div>
          ) : (
            <div className="text-sm text-muted-foreground">
              {t('overviewCards.nextFullBin.noBinsFull')}
            </div>
          )}
        </CardContent>
      </Card>

      {/* Average Fill Level Card */}
      <Card>
        <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
          <CardTitle className="text-sm font-medium">{t('overviewCards.averageFillLevel.title')}</CardTitle>
          <Gauge className="h-5 w-5 text-muted-foreground" />
        </CardHeader>
        <CardContent>
          <div className="text-2xl font-bold">
            {averageFillLevel !== null ? `${averageFillLevel}%` : "N/A"}
          </div>
          <p className="text-xs text-muted-foreground">
            {t('overviewCards.averageFillLevel.atEmptying')}
          </p>
        </CardContent>
      </Card>

      {/* Total Emptyings Card */}
      <Card>
        <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
          <CardTitle className="text-sm font-medium">{t('overviewCards.totalEmptyings.title')}</CardTitle>
          <Trash2 className="h-5 w-5 text-muted-foreground" />
        </CardHeader>
        <CardContent>
          <div className="text-2xl font-bold">{totalEmptyings}</div>
          <p className="text-xs text-muted-foreground">
            +{totalEmptyingsLastWeek} {t('overviewCards.totalEmptyings.lastWeek')}
          </p>
        </CardContent>
      </Card>
    </div>
  )
}
Editor is loading...
Leave a Comment