Untitled

 avatar
unknown
plain_text
a month ago
55 kB
3
Indexable
"use client"

import { useEffect, useState } from "react"
import { 
  Bar, 
  BarChart, 
  Line, 
  LineChart, 
  XAxis, 
  YAxis, 
  CartesianGrid, 
  Tooltip, 
  Legend, 
  ResponsiveContainer, 
  PieChart, 
  Pie, 
  Cell 
} from "recharts"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { AppSidebar } from "@/components/app-sidebar"
import { SidebarInset, SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar"
import { useTranslation, type SupportedLanguages } from "@/utils/translations"
import { useSettings } from "@/hooks/useSettings"
import { useBinConfig } from "@/hooks/use-bin-config"
import { useMetrics } from "@/hooks/useMetrics"
import { useFillPredictions } from "@/hooks/useFillPredictions"
import {
  Breadcrumb,
  BreadcrumbItem,
  BreadcrumbLink,
  BreadcrumbList,
  BreadcrumbPage,
  BreadcrumbSeparator,
} from "@/components/ui/breadcrumb"
import { Separator } from "@/components/ui/separator"
import { 
  ChartContainer, 
  ChartTooltip, 
  ChartTooltipContent, 
  ChartLegend,
  ChartLegendContent,
  type ChartConfig
} from "@/components/ui/chart"
import { Badge } from "@/components/ui/badge"
import { CalendarIcon, BarChart2Icon, LineChartIcon, PieChartIcon, RefreshCwIcon } from "lucide-react"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { format, parseISO, startOfMonth, endOfMonth, sub, isWithinInterval } from "date-fns"

export default function Page() {
  const { settings } = useSettings()
  const { t } = useTranslation((settings?.language as SupportedLanguages) || "EN")
  const { metrics, loading, error, helpers } = useMetrics()
  const { bins } = useBinConfig()
  const { predictions } = useFillPredictions()
  
  const [timeRange, setTimeRange] = useState("all")
  const [activeTab, setActiveTab] = useState("distribution")

  if (loading || !metrics) {
    return (
      <SidebarProvider>
        <div className="flex h-screen w-full">
          <AppSidebar />
          <div className="flex-1 overflow-auto">
            <header className="flex shrink-0 items-center gap-2 border-b px-4 py-6">
              <SidebarTrigger />
              <Separator orientation="vertical" className="mx-2 h-4" />
              <Breadcrumb>
                <BreadcrumbList>
                  <BreadcrumbItem className="hidden sm:block">
                    <BreadcrumbLink href="/">{t('navigation.home')}</BreadcrumbLink>
                  </BreadcrumbItem>
                  <BreadcrumbSeparator className="hidden sm:block" />
                  <BreadcrumbItem>
                    <BreadcrumbPage>{t('navigation.patterns')}</BreadcrumbPage>
                  </BreadcrumbItem>
                </BreadcrumbList>
              </Breadcrumb>
            </header>
            <main className="container mx-auto p-4">
              <div className="flex items-center justify-center h-64">
                <div className="flex flex-col items-center gap-4">
                  <RefreshCwIcon className="h-12 w-12 animate-spin text-muted-foreground" />
                  <p className="text-lg text-muted-foreground">Loading metrics...</p>
                </div>
              </div>
            </main>
          </div>
        </div>
      </SidebarProvider>
    )
  }

  if (error) {
    return (
      <SidebarProvider>
        <div className="flex h-screen w-full">
          <AppSidebar />
          <div className="flex-1 overflow-auto">
            <header className="flex shrink-0 items-center gap-2 border-b px-4 py-6">
              <SidebarTrigger />
              <Separator orientation="vertical" className="mx-2 h-4" />
              <Breadcrumb>
                <BreadcrumbList>
                  <BreadcrumbItem className="hidden sm:block">
                    <BreadcrumbLink href="/">{t('navigation.home')}</BreadcrumbLink>
                  </BreadcrumbItem>
                  <BreadcrumbSeparator className="hidden sm:block" />
                  <BreadcrumbItem>
                    <BreadcrumbPage>{t('navigation.patterns')}</BreadcrumbPage>
                  </BreadcrumbItem>
                </BreadcrumbList>
              </Breadcrumb>
            </header>
            <main className="container mx-auto p-4">
              <div className="flex items-center justify-center h-64">
                <div className="flex flex-col items-center gap-4">
                  <p className="text-lg text-destructive">Error loading metrics: {error}</p>
                </div>
              </div>
            </main>
          </div>
        </div>
      </SidebarProvider>
    )
  }

  // Prepare data for different charts
  const getFilteredData = () => {
    let startDate, endDate
    const now = new Date()
    
    switch (timeRange) {
      case "week":
        startDate = sub(now, { days: 7 })
        endDate = now
        break
      case "month":
        startDate = sub(now, { months: 1 })
        endDate = now
        break
      case "quarter":
        startDate = sub(now, { months: 3 })
        endDate = now
        break
      case "year":
        startDate = sub(now, { years: 1 })
        endDate = now
        break
      default:
        // All time - use first date in data
        const firstTimestamp = metrics.basic_metrics.classification_results[0]?.timestamp
        startDate = firstTimestamp ? parseISO(firstTimestamp) : sub(now, { years: 10 })
        endDate = now
    }
    
    return { startDate, endDate }
  }

  // Distribution data
  const getItemsPerBin = () => {
    const data = []
    Object.entries(metrics.basic_metrics.items_sorted_per_bin).forEach(([binId, count]) => {
      const bin = bins.find(b => b.id === binId)
      data.push({
        bin: bin?.name || binId,
        count,
        fill: bin ? (settings?.theme === "dark" ? bin.color_dark : bin.color) : "#888888"
      })
    })
    return data.sort((a, b) => b.count - a.count)
  }

  // Prepare chart config for items per bin
  const getItemsPerBinConfig = () => {
    const config = {}
    Object.entries(metrics.basic_metrics.items_sorted_per_bin).forEach(([binId, count]) => {
      const bin = bins.find(b => b.id === binId)
      config[binId] = {
        label: bin?.name || binId,
        color: bin ? (settings?.theme === "dark" ? bin.color_dark : bin.color) : "#888888"
      }
    })
    return config
  }

  // Classification methods data
  const getClassificationMethods = () => {
    const { api, local } = metrics.basic_metrics.api_vs_local_usage
    return [
      { name: "API", value: api, fill: settings?.theme === "dark" ? "#3b82f6" : "#2563eb" },
      { name: "Local", value: local, fill: settings?.theme === "dark" ? "#10b981" : "#059669" }
    ]
  }

  // Create config for classification methods
  const getClassificationMethodsConfig = () => {
    return {
      api: {
        label: "API",
        color: settings?.theme === "dark" ? "#3b82f6" : "#2563eb"
      },
      local: {
        label: "Local",
        color: settings?.theme === "dark" ? "#10b981" : "#059669"
      }
    }
  }

  // Time trends data
  const getTimeTrendsData = () => {
    const { startDate, endDate } = getFilteredData()
    const timeRange = { start: startDate, end: endDate }
    
    // Filter classification results by time range
    const results = metrics.basic_metrics.classification_results.filter(result => {
      const date = parseISO(result.timestamp)
      return isWithinInterval(date, { start: startDate, end: endDate })
    })
    
    // Group by day
    const dailyData = {}
    results.forEach(result => {
      const day = format(parseISO(result.timestamp), 'yyyy-MM-dd')
      dailyData[day] = (dailyData[day] || 0) + 1
    })
    
    // Convert to array format for chart
    return Object.entries(dailyData).map(([date, count]) => ({
      date,
      count,
      formattedDate: format(parseISO(date), 'MMM dd')
    })).sort((a, b) => parseISO(a.date).getTime() - parseISO(b.date).getTime())
  }

  // Config for time trends
  const timeTrendsConfig = {
    count: {
      label: "Items Sorted",
      color: settings?.theme === "dark" ? "#3b82f6" : "#2563eb"
    }
  }

  // Time of day patterns
  const getTimeOfDayData = () => {
    const hourData = metrics.time_metrics.time_of_day_patterns
    return hourData.map((count, hour) => ({
      hour: `${hour}:00`,
      count,
      fill: count > 0 ? (settings?.theme === "dark" ? "#3b82f6" : "#2563eb") : "#d1d5db"
    }))
  }

  // Config for time of day
  const timeOfDayConfig = {
    count: {
      label: "Items Sorted",
      color: settings?.theme === "dark" ? "#3b82f6" : "#2563eb"
    }
  }

  // Most common waste types
  const getWasteTypesData = () => {
    const wasteTypes = {}
    
    // Collect all waste types from bin specialization
    Object.values(metrics.bin_specialization).forEach(bin => {
      Object.entries(bin.items_by_type).forEach(([type, count]) => {
        wasteTypes[type] = (wasteTypes[type] || 0) + count
      })
    })
    
    // Convert to array and sort by count
    return Object.entries(wasteTypes)
      .map(([type, count]) => ({ type, count }))
      .sort((a, b) => b.count - a.count)
  }

  // Config for waste types
  const wasteTypesConfig = {
    count: {
      label: "Items",
      color: settings?.theme === "dark" ? "#3b82f6" : "#2563eb"
    }
  }

  // Fill predictions data
  const getBinFillPredictions = () => {
    if (!predictions || predictions.length === 0) return []
    
    return predictions.map(pred => {
      const bin = bins.find(b => b.id === pred.binId)
      return {
        bin: bin?.name || pred.binId,
        currentLevel: pred.currentLevel,
        timeUntilFull: Math.round(pred.timeUntilFull * 10) / 10,
        predictedDate: format(pred.predictedFullTime, 'MMM dd'),
        fillRate: Math.round(pred.fillRate * 100) / 100,
        color: bin ? (settings?.theme === "dark" ? bin.color_dark : bin.color) : "#888888"
      }
    })
  }

  // Monthly usage data
  const getMonthlyData = () => {
    return Object.entries(metrics.time_metrics.monthly_usage_counts)
      .map(([month, count]) => ({
        month: month.substring(5),
        count,
        fill: settings?.theme === "dark" ? "#3b82f6" : "#2563eb"
      }))
      .sort((a, b) => a.month.localeCompare(b.month))
  }

  // Config for monthly data
  const monthlyConfig = {
    count: {
      label: "Items Sorted",
      color: settings?.theme === "dark" ? "#3b82f6" : "#2563eb"
    }
  }

  // Fill level history data for line chart
  const getFillLevelHistoryConfig = () => {
    const config = {}
    
    // Get all unique bin IDs from classification results
    const binIds = [...new Set(metrics.basic_metrics.classification_results
      .filter(result => result.fill_level > 0)
      .map(r => r.bin_id)
    )]
    
    // Create config entry for each bin
    binIds.forEach(binId => {
      const bin = bins.find(b => b.id === binId)
      config[binId] = {
        label: bin?.name || binId,
        color: bin ? (settings?.theme === "dark" ? bin.color_dark : bin.color) : "#888888"
      }
    })
    
    return config
  }

  // Environmental impact chart config
  const environmentalImpactConfig = {
    paper: {
      label: "Paper",
      color: "#4f46e5"
    },
    plastic: {
      label: "Plastic",
      color: "#06b6d4"
    },
    organic: {
      label: "Organic",
      color: "#10b981"
    }
  }

  const COLORS = ['#0088FE', '#00C49F', '#FFBB28', '#FF8042', '#8884D8', '#82ca9d'];

  return (
    <SidebarProvider>
      <div className="flex h-screen w-full">
        <AppSidebar />
        <div className="flex-1 overflow-auto">
          <header className="flex shrink-0 items-center gap-2 border-b px-4 py-6">
            <SidebarTrigger />
            <Separator orientation="vertical" className="mx-2 h-4" />
            <Breadcrumb>
              <BreadcrumbList>
                <BreadcrumbItem className="hidden sm:block">
                  <BreadcrumbLink href="/">{t('navigation.home')}</BreadcrumbLink>
                </BreadcrumbItem>
                <BreadcrumbSeparator className="hidden sm:block" />
                <BreadcrumbItem>
                  <BreadcrumbPage>{t('navigation.patterns')}</BreadcrumbPage>
                </BreadcrumbItem>
              </BreadcrumbList>
            </Breadcrumb>
          </header>
          
          <main className="container mx-auto py-6 px-4">
            <div className="flex flex-col gap-6">
              <div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4">
                <div>
                  <h1 className="text-3xl font-bold tracking-tight">Sorting Analytics</h1>
                  <p className="text-muted-foreground">Analyze sorting trends and patterns in detail</p>
                </div>
                <div className="flex items-center gap-2">
                  <CalendarIcon className="h-4 w-4" />
                  <Select value={timeRange} onValueChange={setTimeRange}>
                    <SelectTrigger className="w-36">
                      <SelectValue placeholder="Time Range" />
                    </SelectTrigger>
                    <SelectContent>
                      <SelectItem value="all">All Time</SelectItem>
                      <SelectItem value="week">Last Week</SelectItem>
                      <SelectItem value="month">Last Month</SelectItem>
                      <SelectItem value="quarter">Last Quarter</SelectItem>
                      <SelectItem value="year">Last Year</SelectItem>
                    </SelectContent>
                  </Select>
                </div>
              </div>
              
              <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
                <Card>
                  <CardHeader className="pb-2">
                    <CardTitle className="text-lg">Total Items Sorted</CardTitle>
                  </CardHeader>
                  <CardContent>
                    <div className="flex items-center gap-4">
                      <BarChart2Icon className="h-10 w-10 text-primary" />
                      <div>
                        <div className="text-3xl font-bold">{metrics.basic_metrics.total_items_sorted}</div>
                        <p className="text-xs text-muted-foreground">
                          Across {Object.keys(metrics.basic_metrics.items_sorted_per_bin).length} bins
                        </p>
                      </div>
                    </div>
                  </CardContent>
                </Card>
                
                <Card>
                  <CardHeader className="pb-2">
                    <CardTitle className="text-lg">Environmental Impact</CardTitle>
                  </CardHeader>
                  <CardContent>
                    <div className="flex items-center gap-4">
                      <div className="h-10 w-10 flex items-center justify-center rounded-full bg-green-100 dark:bg-green-900">
                        <span className="text-green-700 dark:text-green-300 text-xl">🌱</span>
                      </div>
                      <div>
                        <div className="text-2xl font-bold">
                          {metrics.environmental_impact.co2_saved.toFixed(1)} kg COâ‚‚
                        </div>
                        <p className="text-xs text-muted-foreground">
                          {metrics.environmental_impact.trees_saved.toFixed(1)} trees saved
                        </p>
                      </div>
                    </div>
                  </CardContent>
                </Card>
                
                <Card>
                  <CardHeader className="pb-2">
                    <CardTitle className="text-lg">Next Bin to Fill</CardTitle>
                  </CardHeader>
                  <CardContent>
                    {predictions && predictions.length > 0 ? (
                      <div className="flex items-center gap-4">
                        <div 
                          className="h-10 w-10 flex items-center justify-center rounded-full"
                          style={{ 
                            backgroundColor: getBinFillPredictions()[0]?.color || '#888888',
                            color: 'white'
                          }}
                        >
                          <span className="text-xl font-bold">
                            {getBinFillPredictions()[0]?.bin.charAt(0).toUpperCase()}
                          </span>
                        </div>
                        <div>
                          <div className="text-xl font-bold">
                            {getBinFillPredictions()[0]?.bin}
                          </div>
                          <p className="text-xs text-muted-foreground">
                            {getBinFillPredictions()[0]?.timeUntilFull.toFixed(1)} hours remaining
                          </p>
                        </div>
                      </div>
                    ) : (
                      <div className="text-muted-foreground">No predictions available</div>
                    )}
                  </CardContent>
                </Card>
              </div>
              
              <Tabs value={activeTab} onValueChange={setActiveTab} className="w-full">
                <TabsList className="mb-4">
                  <TabsTrigger value="distribution">
                    <BarChart2Icon className="h-4 w-4 mr-2" />
                    <span className="hidden sm:inline">Sorting Distribution</span>
                    <span className="sm:hidden">Distribution</span>
                  </TabsTrigger>
                  <TabsTrigger value="trends">
                    <LineChartIcon className="h-4 w-4 mr-2" />
                    <span className="hidden sm:inline">Sorting Trends</span>
                    <span className="sm:hidden">Trends</span>
                  </TabsTrigger>
                  <TabsTrigger value="classifications">
                    <PieChartIcon className="h-4 w-4 mr-2" />
                    <span className="hidden sm:inline">Top Classifications</span>
                    <span className="sm:hidden">Classifications</span>
                  </TabsTrigger>
                </TabsList>
                
                <TabsContent value="distribution" className="space-y-6">
                  <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
                    <Card>
                      <CardHeader>
                        <CardTitle>Items per Bin Type</CardTitle>
                        <CardDescription>Distribution of sorted items across different bins</CardDescription>
                      </CardHeader>
                      <CardContent>
                        <ChartContainer config={getItemsPerBinConfig()} className="min-h-[300px]">
                          <BarChart accessibilityLayer data={getItemsPerBin()}>
                            <CartesianGrid vertical={false} />
                            <XAxis 
                              dataKey="bin" 
                              axisLine={false} 
                              tickLine={false}
                              tickMargin={8}
                            />
                            <YAxis 
                              axisLine={false} 
                              tickLine={false}
                              tickMargin={8}
                            />
                            <ChartTooltip content={<ChartTooltipContent />} />
                            <Bar 
                              dataKey="count"
                              nameKey="bin"
                              radius={[4, 4, 0, 0]}
                              fill="fill"
                            />
                          </BarChart>
                        </ChartContainer>
                      </CardContent>
                    </Card>
                    
                    <Card>
                      <CardHeader>
                        <CardTitle>Classification Methods</CardTitle>
                        <CardDescription>Distribution between API and local classification</CardDescription>
                      </CardHeader>
                      <CardContent>
                        <ChartContainer config={getClassificationMethodsConfig()} className="min-h-[300px]">
                          <PieChart>
                            <Pie
                              data={getClassificationMethods()}
                              cx="50%"
                              cy="50%"
                              labelLine={false}
                              outerRadius={80}
                              dataKey="value"
                              nameKey="name"
                              label={({ name, percent }) => `${name}: ${(percent * 100).toFixed(0)}%`}
                            >
                              {getClassificationMethods().map((entry, index) => (
                                <Cell key={`cell-${index}`} fill={entry.fill} />
                              ))}
                            </Pie>
                            <Tooltip
                              formatter={(value, name) => [`${value} items`, name]}
                              contentStyle={{ 
                                backgroundColor: 'var(--background)', 
                                borderColor: 'var(--border)',
                                borderRadius: '0.5rem'
                              }}
                            />
                          </PieChart>
                        </ChartContainer>
                      </CardContent>
                    </Card>
                    
                    <Card className="lg:col-span-2">
                      <CardHeader>
                        <CardTitle>Bin Predictions</CardTitle>
                        <CardDescription>Current fill levels and time until full</CardDescription>
                      </CardHeader>
                      <CardContent>
                        <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
                          {getBinFillPredictions().map((prediction, index) => (
                            <div key={index} className="flex flex-col p-4 rounded-lg border">
                              <div className="flex items-center gap-3 mb-3">
                                <div 
                                  className="h-8 w-8 flex items-center justify-center rounded-full"
                                  style={{ 
                                    backgroundColor: prediction.color || '#888888',
                                    color: 'white'
                                  }}
                                >
                                  <span className="text-lg font-bold">
                                    {prediction.bin.charAt(0).toUpperCase()}
                                  </span>
                                </div>
                                <span className="font-medium">{prediction.bin}</span>
                              </div>
                              <div className="space-y-2">
                                <div className="flex justify-between">
                                  <span className="text-sm text-muted-foreground">Current Level</span>
                                  <span className="font-medium">{prediction.currentLevel.toFixed(1)}%</span>
                                </div>
                                <div className="flex justify-between">
                                  <span className="text-sm text-muted-foreground">Time Until Full</span>
                                  <span className="font-medium">{prediction.timeUntilFull.toFixed(1)} hours</span>
                                </div>
                                <div className="flex justify-between">
                                  <span className="text-sm text-muted-foreground">Predicted Date</span>
                                  <span className="font-medium">{prediction.predictedDate}</span>
                                </div>
                                <div className="w-full bg-muted rounded-full h-2 mt-2">
                                  <div 
                                    className="h-2 rounded-full" 
                                    style={{ 
                                      width: `${prediction.currentLevel}%`,
                                      backgroundColor: prediction.color || '#888888'
                                    }}
                                  ></div>
                                </div>
                              </div>
                            </div>
                          ))}
                        </div>
                      </CardContent>
                    </Card>
                  </div>
                </TabsContent>
                
                <TabsContent value="trends" className="space-y-6">
                  <div className="grid grid-cols-1 gap-6">
                    <Card>
                      <CardHeader>
                        <CardTitle>Daily Sorting Trends</CardTitle>
                        <CardDescription>Number of items sorted over time</CardDescription>
                      </CardHeader>
                      <CardContent>
                        <ChartContainer config={timeTrendsConfig} className="min-h-[300px]">
                          <LineChart accessibilityLayer data={getTimeTrendsData()}>
                            <CartesianGrid vertical={false} />
                            <XAxis 
                              dataKey="formattedDate" 
                              axisLine={false} 
                              tickLine={false}
                              tickMargin={8}
                            />
                            <YAxis 
                              axisLine={false} 
                              tickLine={false}
                              tickMargin={8}
                            />
                            <ChartTooltip 
                              content={
                                <ChartTooltipContent 
                                  labelKey="date" 
                                  nameKey="date"
                                />
                              } 
                            />
                            <Line 
                              type="monotone" 
                              dataKey="count" 
                              name="Items Sorted"
                              stroke={settings?.theme === "dark" ? "#3b82f6" : "#2563eb"}
                              strokeWidth={2}
                              dot={false}
                              activeDot={{ r: 6 }}
                            />
                          </LineChart>
                        </ChartContainer>
                      </CardContent>
                    </Card>
                    
                    <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
                      <Card>
                        <CardHeader>
                          <CardTitle>Time of Day Patterns</CardTitle>
                          <CardDescription>When items are most frequently sorted</CardDescription>
                        </CardHeader>
                        <CardContent>
                          <ChartContainer config={timeOfDayConfig} className="min-h-[300px]">
                            <BarChart accessibilityLayer data={getTimeOfDayData()}>
                              <CartesianGrid vertical={false} />
                              <XAxis 
                                dataKey="hour" 
                                axisLine={false} 
                                tickLine={false}
                                tickMargin={8}
                                tick={{ fontSize: 12 }}
                                interval={3}
                              />
                              <YAxis 
                                axisLine={false} 
                                tickLine={false}
                                tickMargin={8}
                              />
                              <ChartTooltip content={<ChartTooltipContent />} />
                              <Bar 
                                dataKey="count" 
                                fill="fill"
                                radius={[4, 4, 0, 0]}
                              />
                            </BarChart>
                          </ChartContainer>
                        </CardContent>
                      </Card>
                      
                      <Card>
                        <CardHeader>
                          <CardTitle>Monthly Usage</CardTitle>
                          <CardDescription>Items sorted by month</CardDescription>
                        </CardHeader>
                        <CardContent>
                          <ChartContainer config={monthlyConfig} className="min-h-[300px]">
                            <BarChart accessibilityLayer data={getMonthlyData()}>
                              <CartesianGrid vertical={false} />
                              <XAxis 
                                dataKey="month" 
                                axisLine={false} 
                                tickLine={false}
                                tickMargin={8}
                              />
                              <YAxis 
                                axisLine={false} 
                                tickLine={false}
                                tickMargin={8}
                              />
                              <ChartTooltip content={<ChartTooltipContent />} />
                              <Bar 
                                dataKey="count" 
                                fill="fill"
                                radius={[4, 4, 0, 0]}
                              />
                            </BarChart>
                          </ChartContainer>
                        </CardContent>
                      </Card>
                    </div>
                  </div>
                </TabsContent>
                
                <TabsContent value="classifications" className="space-y-6">
                  <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
                    <Card>
                      <CardHeader>
                        <CardTitle>Most Common Waste Types</CardTitle>
                        <CardDescription>Distribution of waste classifications</CardDescription>
                      </CardHeader>
                      <CardContent>
                        <ChartContainer config={wasteTypesConfig} className="min-h-[300px]">
                          <BarChart 
                            accessibilityLayer 
                            data={getWasteTypesData()} 
                            layout="vertical"
                            margin={{ left: 80 }}
                          >
                            <CartesianGrid horizontal={false} />
                            <XAxis 
                              type="number" 
                              axisLine={false} 
                              tickLine={false}
                              tickMargin={8}
                            />
                            <YAxis 
                              type="category"
                              dataKey="type" 
                              axisLine={false} 
                              tickLine={false}
                              tickMargin={8}
                              width={80}
                            />
                            <ChartTooltip content={<ChartTooltipContent />} />
                            <Bar 
                              dataKey="count" 
                              nameKey="type"
                              fill={settings?.theme === "dark" ? "#3b82f6" : "#2563eb"}
                              radius={[0, 4, 4, 0]}
                            />
                          </BarChart>
                        </ChartContainer>
                      </CardContent>
                    </Card>
                    
                    <Card>
                      <CardHeader>
                        <CardTitle>Bin Specialization</CardTitle>
                        <CardDescription>Primary waste type for each bin</CardDescription>
                      </CardHeader>
                      <CardContent>
                        <div className="grid grid-cols-1 gap-4">
                          {Object.entries(metrics.bin_specialization).map(([binId, data]) => {
                            const bin = bins.find(b => b.id === binId)
                            return (
                              <div key={binId} className="flex items-center gap-4 p-3 border rounded-lg">
                                <div 
                                  className="h-10 w-10 flex-shrink-0 flex items-center justify-center rounded-full"
                                  style={{ 
                                    backgroundColor: bin ? (settings?.theme === "dark" ? bin.color_dark : bin.color) : '#888888',
                                    color: 'white'
                                  }}
                                >
                                  <span className="text-lg font-bold">
                                    {(bin?.name || binId).charAt(0).toUpperCase()}
                                  </span>
                                </div>
                                <div className="flex-1 min-w-0">
                                  <div className="flex flex-wrap justify-between items-center mb-1">
                                    <p className="font-medium truncate">{bin?.name || binId}</p>
                                    <Badge variant="outline" className="ml-2">
                                      {data.total_items} items
                                    </Badge>
                                  </div>
                                  <div className="text-sm text-muted-foreground">
                                    Most common: {data.most_common_type}
                                  </div>
                                </div>
                              </div>
                            )
                          })}
                        </div>
                      </CardContent>
                    </Card>
                    
                    <Card className="lg:col-span-2">
                      <CardHeader>
                        <CardTitle>Emptying Events</CardTitle>
                        <CardDescription>History of bin emptying by fill level</CardDescription>
                      </CardHeader>
                      <CardContent>
                        <div className="overflow-x-auto">
                          <table className="w-full border-collapse">
                            <thead>
                              <tr>
                                <th className="text-left py-3 px-4 font-medium text-muted-foreground">Bin</th>
                                <th className="text-left py-3 px-4 font-medium text-muted-foreground">Total Empties</th>
                                <th className="text-left py-3 px-4 font-medium text-muted-foreground">Last Emptied</th>
                                <th className="text-left py-3 px-4 font-medium text-muted-foreground">Avg. Fill at Empty</th>
                                <th className="text-left py-3 px-4 font-medium text-muted-foreground">Proactive %</th>
                              </tr>
                            </thead>
                            <tbody className="divide-y">
                              {Object.entries(metrics.fill_level_history || {}).map(([binId, data]) => {
                                const bin = bins.find(b => b.id === binId)
                                const emptyCount = data.emptying_timestamps.length
                                const lastEmptied = data.emptying_timestamps.length > 0 ? 
                                  format(parseISO(data.emptying_timestamps[data.emptying_timestamps.length - 1]), 'MMM dd, yyyy') : 
                                  'Never'
                                
                                // Calculate average fill level at emptying
                                const avgFill = data.fill_levels_at_empty.length > 0 ?
                                  data.fill_levels_at_empty.reduce((sum, level) => sum + level, 0) / data.fill_levels_at_empty.length :
                                  0
                                
                                // Calculate % of proactive empties (below 80% full)
                                const proactiveEmpties = data.fill_levels_at_empty.filter(level => level < 80).length
                                const proactivePercent = emptyCount > 0 ? (proactiveEmpties / emptyCount) * 100 : 0
                                
                                return (
                                  <tr key={binId} className="hover:bg-muted/50">
                                    <td className="py-3 px-4">
                                      <div className="flex items-center gap-2">
                                        <div 
                                          className="h-6 w-6 flex items-center justify-center rounded-full"
                                          style={{ 
                                            backgroundColor: bin ? (settings?.theme === "dark" ? bin.color_dark : bin.color) : '#888888',
                                            color: 'white'
                                          }}
                                        >
                                          <span className="text-xs font-bold">
                                            {(bin?.name || binId).charAt(0).toUpperCase()}
                                          </span>
                                        </div>
                                        <span>{bin?.name || binId}</span>
                                      </div>
                                    </td>
                                    <td className="py-3 px-4">{emptyCount}</td>
                                    <td className="py-3 px-4">{lastEmptied}</td>
                                    <td className="py-3 px-4">{avgFill.toFixed(1)}%</td>
                                    <td className="py-3 px-4">
                                      <div className="flex items-center gap-2">
                                        <span>{proactivePercent.toFixed(0)}%</span>
                                        <div className="w-16 bg-muted rounded-full h-2">
                                          <div 
                                            className="h-2 rounded-full" 
                                            style={{ 
                                              width: `${proactivePercent}%`,
                                              backgroundColor: proactivePercent > 50 ? 'rgb(34, 197, 94)' : 'rgb(234, 179, 8)'
                                            }}
                                          ></div>
                                        </div>
                                      </div>
                                    </td>
                                  </tr>
                                )
                              })}
                            </tbody>
                          </table>
                        </div>
                      </CardContent>
                    </Card>
                  </div>
                </TabsContent>
              </Tabs>
              
              {/* Interactive Features Section */}
              <div className="space-y-4 mt-6">
                <h2 className="text-2xl font-bold tracking-tight">Interactive Features</h2>
                
                <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
                  <Card>
                    <CardHeader>
                      <CardTitle>Fill Level History</CardTitle>
                      <CardDescription>Track fill level changes over time for each bin</CardDescription>
                    </CardHeader>
                    <CardContent>
                      <ChartContainer config={getFillLevelHistoryConfig()} className="min-h-[300px]">
                        <LineChart accessibilityLayer data={
                          // Get the last 20 entries for visibility
                          metrics.basic_metrics.classification_results
                            .filter(result => result.fill_level > 0)
                            .sort((a, b) => parseISO(a.timestamp).getTime() - parseISO(b.timestamp).getTime())
                            .slice(-20)
                            .map(result => {
                              const bin = bins.find(b => b.id === result.bin_id)
                              return {
                                date: format(parseISO(result.timestamp), 'MMM dd'),
                                [result.bin_id]: result.fill_level,
                                fill: bin ? (settings?.theme === "dark" ? bin.color_dark : bin.color) : '#888888',
                                binName: bin?.name || result.bin_id
                              }
                            })
                        }>
                          <CartesianGrid vertical={false} />
                          <XAxis 
                            dataKey="date" 
                            axisLine={false} 
                            tickLine={false}
                            tickMargin={8}
                          />
                          <YAxis 
                            axisLine={false} 
                            tickLine={false}
                            tickMargin={8}
                            domain={[0, 100]}
                            label={{ value: 'Fill Level (%)', angle: -90, position: 'insideLeft', dy: 40 }}
                          />
                          <ChartTooltip 
                            content={
                              <div className="p-2 bg-background border rounded-md shadow-md">
                                {({ active, payload, label }) => {
                                  if (active && payload && payload.length) {
                                    return (
                                      <div className="p-2">
                                        <p className="font-medium">{label}</p>
                                        {payload.map((entry, index) => (
                                          <div key={`item-${index}`} className="flex items-center gap-2 mt-1">
                                            <div 
                                              className="h-3 w-3 rounded-full"
                                              style={{ backgroundColor: entry.payload.fill }}
                                            />
                                            <span className="text-sm">
                                              {entry.payload.binName}: {entry.value.toFixed(1)}%
                                            </span>
                                          </div>
                                        ))}
                                      </div>
                                    )
                                  }
                                  return null
                                }}
                              </div>
                            } 
                          />
                          {/* Add lines dynamically for each bin that has data */}
                          {[...new Set(metrics.basic_metrics.classification_results
                            .filter(result => result.fill_level > 0)
                            .map(r => r.bin_id))]
                            .map(binId => {
                              const bin = bins.find(b => b.id === binId)
                              return (
                                <Line 
                                  key={binId}
                                  type="monotone" 
                                  dataKey={binId} 
                                  name={bin?.name || binId}
                                  stroke={bin ? (settings?.theme === "dark" ? bin.color_dark : bin.color) : '#888888'}
                                  strokeWidth={2}
                                  dot={{ r: 3 }}
                                  activeDot={{ r: 6 }}
                                />
                              )
                            })
                          }
                          <Legend />
                        </LineChart>
                      </ChartContainer>
                    </CardContent>
                  </Card>
                  
                  <Card>
                    <CardHeader>
                      <CardTitle>Environmental Impact</CardTitle>
                      <CardDescription>Breakdown of environmental benefits by material</CardDescription>
                    </CardHeader>
                    <CardContent>
                      <div className="grid grid-cols-1 gap-4">
                        <div className="flex flex-col p-4 border rounded-lg">
                          <div className="mb-3">
                            <span className="text-lg font-medium">Total COâ‚‚ Saved</span>
                            <div className="text-3xl font-bold mt-1">
                              {metrics.environmental_impact.co2_saved.toFixed(1)} kg
                            </div>
                          </div>
                          <div className="grid grid-cols-2 sm:grid-cols-3 gap-4">
                            <div className="flex flex-col">
                              <span className="text-sm text-muted-foreground">Trees Saved</span>
                              <span className="font-medium">{metrics.environmental_impact.trees_saved.toFixed(1)}</span>
                            </div>
                            <div className="flex flex-col">
                              <span className="text-sm text-muted-foreground">Paper Recycled</span>
                              <span className="font-medium">{metrics.environmental_impact.paper_weight_recycled.toFixed(1)} kg</span>
                            </div>
                            <div className="flex flex-col">
                              <span className="text-sm text-muted-foreground">Plastic Recycled</span>
                              <span className="font-medium">{metrics.environmental_impact.plastic_weight_recycled.toFixed(1)} kg</span>
                            </div>
                            <div className="flex flex-col">
                              <span className="text-sm text-muted-foreground">Organic Processed</span>
                              <span className="font-medium">{metrics.environmental_impact.organic_weight_processed.toFixed(1)} kg</span>
                            </div>
                          </div>
                        </div>
                        
                        <ChartContainer config={environmentalImpactConfig} className="min-h-[200px]">
                          <PieChart>
                            <Pie
                              data={[
                                { name: 'Paper', value: metrics.environmental_impact.paper_weight_recycled, fill: '#4f46e5' },
                                { name: 'Plastic', value: metrics.environmental_impact.plastic_weight_recycled, fill: '#06b6d4' },
                                { name: 'Organic', value: metrics.environmental_impact.organic_weight_processed, fill: '#10b981' }
                              ]}
                              cx="50%"
                              cy="50%"
                              outerRadius={80}
                              innerRadius={50}
                              dataKey="value"
                              nameKey="name"
                              paddingAngle={2}
                            >
                              {[
                                { name: 'Paper', value: metrics.environmental_impact.paper_weight_recycled, fill: '#4f46e5' },
                                { name: 'Plastic', value: metrics.environmental_impact.plastic_weight_recycled, fill: '#06b6d4' },
                                { name: 'Organic', value: metrics.environmental_impact.organic_weight_processed, fill: '#10b981' }
                              ].map((entry, index) => (
                                <Cell key={`cell-${index}`} fill={entry.fill} />
                              ))}
                            </Pie>
                            <Tooltip
                              formatter={(value, name) => [`${value.toFixed(1)} kg`, name]}
                              contentStyle={{ 
                                backgroundColor: 'var(--background)', 
                                borderColor: 'var(--border)',
                                borderRadius: '0.5rem'
                              }}
                            />
                            <Legend />
                          </PieChart>
                        </ChartContainer>
                      </div>
                    </CardContent>
                  </Card>
                  
                  {/* Achievements card */}
                  <Card className="lg:col-span-2">
                    <CardHeader>
                      <CardTitle>Achievements Progress</CardTitle>
                      <CardDescription>Track progress towards unlocking achievements</CardDescription>
                    </CardHeader>
                    <CardContent>
                      <div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-3">
                        {Object.entries(metrics.achievements)
                          .sort((a, b) => {
                            // Sort completed first, then by progress percentage
                            if (a[1].status === 'completed' && b[1].status !== 'completed') return -1;
                            if (a[1].status !== 'completed' && b[1].status === 'completed') return 1;
                            return (b[1].progress / b[1].target) - (a[1].progress / a[1].target);
                          })
                          .map(([id, achievement]) => {
                            const progressPercent = Math.min(100, (achievement.progress / achievement.target) * 100);
                            const isCompleted = achievement.status === 'completed';
                            
                            return (
                              <div key={id} className={`p-4 border rounded-lg ${isCompleted ? 'bg-primary/5' : ''}`}>
                                <div className="flex justify-between items-start mb-2">
                                  <h4 className="font-medium">{achievement.name}</h4>
                                  {achievement.tier && (
                                    <Badge variant={
                                      achievement.tier === 'gold' ? 'default' :
                                      achievement.tier === 'silver' ? 'secondary' : 'outline'
                                    }>
                                      {achievement.tier}
                                    </Badge>
                                  )}
                                </div>
                                <p className="text-sm text-muted-foreground mb-3">{achievement.description}</p>
                                <div className="flex items-center justify-between mt-2 text-sm">
                                  <div>
                                    {isCompleted ? (
                                      <Badge className="bg-green-600">Completed</Badge>
                                    ) : (
                                      <span>{achievement.progress} / {achievement.target}</span>
                                    )}
                                  </div>
                                  {achievement.unlock_date && !isCompleted && (
                                    <span className="text-xs text-muted-foreground">
                                      Unlocked: {format(parseISO(achievement.unlock_date), 'MMM dd, yyyy')}
                                    </span>
                                  )}
                                </div>
                                <div className="w-full bg-muted rounded-full h-2 mt-2">
                                  <div 
                                    className={`h-2 rounded-full ${isCompleted ? 'bg-green-600' : 'bg-primary'}`}
                                    style={{ width: `${progressPercent}%` }}
                                  ></div>
                                </div>
                              </div>
                            );
                          })}
                      </div>
                    </CardContent>
                  </Card>
                </div>
              </div>
            </div>
          </main>
        </div>
      </div>
    </SidebarProvider>
  )
}
Editor is loading...
Leave a Comment