Untitled

 avatar
unknown
plain_text
3 months ago
7.9 kB
7
Indexable
// components/overview-cards.tsx
"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, differenceInDays, subDays } from "date-fns"

export function OverviewCards() {
  const { metrics } = useMetrics()
  const { bins } = useBinConfig()

  const formatTimeRemaining = (hours: number) => {
    if (hours >= 24) {
      const days = Math.floor(hours / 24)
      const remainingHours = hours % 24
      let result = `${days} Tag${days !== 1 ? 'e' : ''}`
      if (remainingHours > 0) {
        result += ` ${remainingHours} Stunde${remainingHours !== 1 ? 'n' : ''}`
      }
      return result
    }
    return `${hours} Stunde${hours !== 1 ? 'n' : ''}`
  }

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

  const getBinDisplay = (binId: string) => {
    const binNumber = binId.replace('bin', '')
    const configuredBin = bins.find(b => b.id === binId)
    return {
      name: configuredBin?.name || `Mülleimer ${binNumber}`,
      fullLabel: `Mülleimer ${binNumber}`,
      type: configuredBin?.name || 'Nicht konfiguriert'
    }
  }

  // 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
  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)
        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
      } | 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 = ''
  let formattedTime = ''
  if (nextFullBin) {
    timeRemainingHours = Math.max(0, differenceInHours(nextFullBin.predictedTime, new Date()))
    formattedTime = formatTimeRemaining(timeRemainingHours)
    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">Proaktive Leerungen</CardTitle>
          <Trash2 className="h-4 w-4 text-muted-foreground" />
        </CardHeader>
        <CardContent>
          <div className="text-2xl font-bold">{proactiveEmptyings}</div>
          <p className="text-xs text-muted-foreground">
            {totalEmptyings} Gesamtleerungen ({proactivePercentage}%)
          </p>
        </CardContent>
      </Card>

      {/* Next Full Bin Card */}
      <Card className={nextFullBin ? `${timeColorClass} border-l-4` : ''}>
        <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
          <CardTitle className="text-sm font-medium">Nächste volle Tonne</CardTitle>
          <Clock className="h-4 w-4 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}
                </div>
                <div className="text-lg text-muted-foreground">
                  ({nextFullBin.type})
                </div>
              </div>
              
              <div className="space-y-2">
                <div className="text-sm text-muted-foreground">
                  Voraussichtlich voll:{" "}
                  <span className="font-medium">
                    {format(nextFullBin.predictedTime, "dd.MM.yyyy HH:mm")}
                  </span>
                </div>
                
                <div className="flex items-center gap-3">
                  <div className={`px-3 py-1 rounded-full ${timeColorClass} text-sm font-medium`}>
                    {formattedTime}
                  </div>
                  <div className="flex-1">
                    <div className="text-xs text-muted-foreground mb-1">
                      Aktueller Füllstand: {nextFullBin.currentLevel}%
                    </div>
                    <div className="w-full bg-gray-200 rounded-full h-2">
                      <div 
                        className="bg-blue-500 rounded-full h-2" 
                        style={{ width: `${nextFullBin.currentLevel}%` }}
                      />
                    </div>
                  </div>
                </div>
              </div>
            </div>
          ) : (
            <div className="text-sm text-muted-foreground">
              Alle Tonnen sind aktuell unterhalb der Kapazität
            </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">Durchschn. Füllstand</CardTitle>
          <Gauge className="h-4 w-4 text-muted-foreground" />
        </CardHeader>
        <CardContent>
          <div className="text-2xl font-bold">
            {averageFillLevel !== null ? `${averageFillLevel}%` : "N/A"}
          </div>
          <p className="text-xs text-muted-foreground">
            Bei Leerungen
          </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">Leerungen gesamt</CardTitle>
          <Trash2 className="h-4 w-4 text-muted-foreground" />
        </CardHeader>
        <CardContent>
          <div className="text-2xl font-bold">{totalEmptyings}</div>
          <p className="text-xs text-muted-foreground">
            +{totalEmptyingsLastWeek} letzte Woche
          </p>
        </CardContent>
      </Card>
    </div>
  )
}
Editor is loading...
Leave a Comment