Untitled
// /hooks/useMetrics.ts 'use client'; import { useState, useEffect, useMemo } from 'react'; import type { MetricsData } from '@/app/api/metrics/route'; interface ProcessedFillLevelData { timestamp: string; fill_level: number; bin_id: string; } export function useMetrics() { const [metrics, setMetrics] = useState<MetricsData | null>(null); const [loading, setLoading] = useState(true); const [error, setError] = useState<string | null>(null); useEffect(() => { async function fetchMetrics() { try { const response = await fetch('/api/metrics'); if (!response.ok) throw new Error('Failed to fetch metrics'); const data = await response.json(); setMetrics(data); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to load metrics'); } finally { setLoading(false); } } fetchMetrics(); const intervalId = setInterval(fetchMetrics, 5000); return () => clearInterval(intervalId); }, []); // Memoized helper functions for accessing fill level data const helpers = useMemo(() => { if (!metrics) return null; return { // Get all unique bin IDs that have any kind of data getActiveBinIds: () => { const binIds = new Set<string>(); // Check fill predictions Object.keys(metrics.fill_predictions || {}).forEach(id => binIds.add(id)); // Check fill level history Object.keys(metrics.fill_level_history || {}).forEach(id => binIds.add(id)); // Check classification results metrics.basic_metrics.classification_results.forEach(result => binIds.add(result.bin_id)); return Array.from(binIds); }, // Get processed fill level history for a specific bin or all bins getFillLevelHistory: (binId?: string, timeRange?: {start: Date, end: Date}) => { const results = metrics.basic_metrics.classification_results .map(result => ({ timestamp: result.timestamp, fill_level: result.fill_level, bin_id: result.bin_id })); let filtered = results; // Filter by bin if specified if (binId) { filtered = filtered.filter(result => result.bin_id === binId); } // Filter by time range if specified if (timeRange) { filtered = filtered.filter(result => { const timestamp = new Date(result.timestamp); return timestamp >= timeRange.start && timestamp <= timeRange.end; }); } return filtered.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime() ); }, // Get emptying events for a specific bin or all bins getEmptyingEvents: (binId?: string, timeRange?: {start: Date, end: Date}) => { const history = metrics.fill_level_history; const events: Array<{ timestamp: string; fill_level: number; bin_id: string; }> = []; Object.entries(history).forEach(([id, data]) => { if (!binId || id === binId) { data.emptying_timestamps.forEach((timestamp, index) => { events.push({ timestamp, fill_level: data.fill_levels_at_empty[index], bin_id: id }); }); } }); let filtered = events; // Filter by time range if specified if (timeRange) { filtered = filtered.filter(event => { const timestamp = new Date(event.timestamp); return timestamp >= timeRange.start && timestamp <= timeRange.end; }); } return filtered.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime() ); }, // Get the latest fill level prediction for a specific bin or all bins getLatestPredictions: (binId?: string) => { const predictions = metrics.fill_predictions; if (binId) { return predictions[binId] ? { [binId]: predictions[binId] } : {}; } return predictions; }, // Get proactive emptying statistics getProactiveEmptyingStats: () => { const emptyingEvents = Object.values(metrics.fill_level_history).flatMap( history => history.fill_levels_at_empty ); // Group emptying events by fill level ranges (0-20%, 21-40%, etc.) const ranges = Array.from({ length: 5 }, (_, i) => ({ range: `${i * 20 + 1}-${(i + 1) * 20}%`, count: emptyingEvents.filter( level => level > i * 20 && level <= (i + 1) * 20 ).length })); ranges.push({ range: '81-100%', count: emptyingEvents.filter(level => level > 80).length }); return ranges; } }; }, [metrics]); return { metrics, loading, error, helpers }; }
Leave a Comment