Untitled

 avatar
unknown
plain_text
4 days ago
8.5 kB
2
Indexable
"use client"

import { useState, useMemo } from "react"
import { format } from "date-fns"
import { CalendarIcon } from "lucide-react"
import { Bar, BarChart, CartesianGrid, XAxis, YAxis } from "recharts"
import { useMetrics } from "@/hooks/useMetrics"
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select"
import {
  Card,
  CardContent,
  CardDescription,
  CardFooter,
  CardHeader,
  CardTitle,
} from "@/components/ui/card"
import {
  ChartConfig,
  ChartContainer,
  ChartTooltip,
  ChartTooltipContent,
} from "@/components/ui/chart"
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"
import { Button } from "@/components/ui/button"
import { Calendar } from "@/components/ui/calendar"

const fillLevelRanges = [
  { label: "0-20%", min: 0, max: 20 },
  { label: "21-40%", min: 21, max: 40 },
  { label: "41-60%", min: 41, max: 60 },
  { label: "61-80%", min: 61, max: 80 },
  { label: "81-100%", min: 81, max: 100 },
]

const periods = [
  { label: "All Time", value: "all" },
  { label: "Last 24h", value: "24h" },
  { label: "Last Week", value: "week" },
  { label: "Last Month", value: "month" },
  { label: "Last Year", value: "year" },
  { label: "Custom", value: "custom" },
]

export function ProactiveEmptyingChart() {
  const { metrics, helpers } = useMetrics()
  const [selectedPeriod, setSelectedPeriod] = useState("all")
  const [selectedBin, setSelectedBin] = useState("all")
  const [selectedType, setSelectedType] = useState("all")
  const [customDateRange, setCustomDateRange] = useState<{ from: Date | undefined; to: Date | undefined }>({
    from: undefined,
    to: undefined,
  })

  const chartData = useMemo(() => {
    if (!metrics || !helpers) return []

    const now = new Date()
    let timeFilter

    if (selectedPeriod === "all") {
      timeFilter = undefined
    } else if (selectedPeriod === "custom") {
      timeFilter = customDateRange.from && customDateRange.to 
        ? { start: customDateRange.from, end: customDateRange.to }
        : undefined
    } else {
      let startDate = new Date(now)
      switch (selectedPeriod) {
        case "24h":
          startDate = new Date(now.getTime() - 24 * 60 * 60 * 1000)
          break
        case "week":
          startDate = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000)
          break
        case "month":
          startDate = new Date(now.getFullYear(), now.getMonth() - 1, now.getDate())
          break
        case "year":
          startDate = new Date(now.getFullYear() - 1, now.getMonth(), now.getDate())
          break
      }
      timeFilter = { start: startDate, end: now }
    }

    const events = helpers.getEmptyingEvents(
      selectedBin === "all" ? undefined : selectedBin,
      timeFilter
    )

    const rangeCounts = fillLevelRanges.map(range => ({
      range: range.label,
      proactive: 0,
      normal: 0,
    }))

    events.forEach(event => {
      const range = fillLevelRanges.find(r => 
        event.fill_level > r.min && event.fill_level <= r.max
      )
      if (!range) return
      
      const idx = fillLevelRanges.indexOf(range)
      const isProactive = event.fill_level < 90
      
      if (selectedType === "proactive" && isProactive) {
        rangeCounts[idx].proactive++
      } else if (selectedType === "normal" && !isProactive) {
        rangeCounts[idx].normal++
      } else {
        isProactive ? rangeCounts[idx].proactive++ : rangeCounts[idx].normal++
      }
    })

    return rangeCounts.map(item => ({
      ...item,
      total: item.proactive + item.normal,
    }))
  }, [metrics, selectedPeriod, selectedBin, selectedType, helpers, customDateRange])

  const activeBins = helpers?.getActiveBinIds() || []
  const chartConfig = {
    ...(selectedType !== "normal" && { proactive: { 
      label: "Proactive", 
      color: "hsl(var(--chart-1))" 
    }}),
    ...(selectedType !== "proactive" && { normal: { 
      label: "Normal", 
      color: "hsl(var(--chart-2))" 
    }}),
  } satisfies ChartConfig

  return (
    <Card>
      <CardHeader className="flex flex-row items-center justify-between gap-4">
        <div>
          <CardTitle>Emptying Statistics</CardTitle>
          <CardDescription>Distribution of emptying events by fill level</CardDescription>
        </div>
        <div className="flex gap-2">
          <Select value={selectedPeriod} onValueChange={setSelectedPeriod}>
            <SelectTrigger className="w-[120px]">
              <SelectValue placeholder="Period" />
            </SelectTrigger>
            <SelectContent>
              {periods.map((period) => (
                <SelectItem key={period.value} value={period.value}>
                  {period.label}
                </SelectItem>
              ))}
            </SelectContent>
          </Select>

          <Select value={selectedBin} onValueChange={setSelectedBin}>
            <SelectTrigger className="w-[100px]">
              <SelectValue placeholder="Bin" />
            </SelectTrigger>
            <SelectContent>
              <SelectItem value="all">All</SelectItem>
              {activeBins.map(bin => (
                <SelectItem key={bin} value={bin}>{bin}</SelectItem>
              ))}
            </SelectContent>
          </Select>

          <Select value={selectedType} onValueChange={setSelectedType}>
            <SelectTrigger className="w-[120px]">
              <SelectValue placeholder="Type" />
            </SelectTrigger>
            <SelectContent>
              <SelectItem value="all">All</SelectItem>
              <SelectItem value="proactive">Proactive</SelectItem>
              <SelectItem value="normal">Normal</SelectItem>
            </SelectContent>
          </Select>

          {selectedPeriod === "custom" && (
            <Popover>
              <PopoverTrigger asChild>
                <Button variant="outline" className="w-[280px] justify-start text-left font-normal">
                  <CalendarIcon className="mr-2 h-4 w-4" />
                  {customDateRange.from ? (
                    customDateRange.to ? (
                      <>
                        {format(customDateRange.from, "LLL dd, y")} - {format(customDateRange.to, "LLL dd, y")}
                      </>
                    ) : (
                      format(customDateRange.from, "LLL dd, y")
                    )
                  ) : (
                    <span>Pick a date range</span>
                  )}
                </Button>
              </PopoverTrigger>
              <PopoverContent className="w-auto p-0" align="start">
                <Calendar
                  initialFocus
                  mode="range"
                  defaultMonth={customDateRange.from}
                  selected={customDateRange}
                  onSelect={setCustomDateRange}
                  numberOfMonths={2}
                />
              </PopoverContent>
            </Popover>
          )}
        </div>
      </CardHeader>

      <CardContent>
        <ChartContainer config={chartConfig} className="aspect-auto h-[400px] w-full">
          <BarChart data={chartData}>
            <CartesianGrid vertical={false} />
            <XAxis
              dataKey="range"
              tickLine={false}
              axisLine={false}
              tickMargin={10}
            />
            <YAxis
              tickLine={false}
              axisLine={false}
              tickFormatter={(value) => `${value}`}
            />
            <ChartTooltip content={<ChartTooltipContent />} />

            {selectedType !== "normal" && (
              <Bar
                dataKey="proactive"
                fill="var(--color-proactive)"
                radius={4}
                stackId="a"
              />
            )}
            {selectedType !== "proactive" && (
              <Bar
                dataKey="normal"
                fill="var(--color-normal)"
                radius={4}
                stackId="a"
              />
            )}
          </BarChart>
        </ChartContainer>
      </CardContent>

      <CardFooter className="text-sm text-muted-foreground">
        Proactive emptying = below 90% fill level | Normal emptying = 90%+
      </CardFooter>
    </Card>
  )
}
Leave a Comment