Untitled
"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