Untitled
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