import React, { useState, useEffect } from 'react'; import axios from 'axios'; import { ComposedChart, Area, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer, } from 'recharts'; import './App.css'; function History() { const [historyData, setHistoryData] = useState([]); const [days, setDays] = useState(7); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [showAveraged, setShowAveraged] = useState(false); const [currentPage, setCurrentPage] = useState(1); const [pageSize, setPageSize] = useState(10); const [totalItems, setTotalItems] = useState(0); useEffect(() => { const loadHistory = async () => { setLoading(true); setError(null); try { const response = await axios.get('/api/history/overview', { params: { days, page: currentPage, limit: pageSize } }); setHistoryData(response.data.resources || []); setTotalItems(response.data.total || response.data.resources?.length || 0); setLoading(false); } catch (error) { console.error('Error loading history:', error); setError(error.message); setLoading(false); } }; loadHistory(); }, [days, currentPage, pageSize]); if (loading) return
Loading history...
; if (error) return
Error loading history: {error}
; return (

Monitoring History

{[7, 14, 30].map((d) => ( ))}
{historyData.length === 0 ? (

No check history

Add resources and wait for checks to complete

) : (
{historyData.map((resource) => { const chartData = showAveraged ? prepareAveragedChartData(resource.checks || [], days) : prepareChartData(resource.checks || []); return (

{resource.name}

Type: {resource.type.toUpperCase()} Uptime: = 95 ? '#4caf50' : resource.uptime >= 80 ? '#ff9800' : '#f44336' }}>{resource.uptime}% Avg Response: {resource.avgResponseTime}ms

{chartData.length > 0 ? (
{ if (name === 'Response Time') return `${value}ms`; if (name === 'Status') return value === 1 ? '✓ UP' : '✗ DOWN'; if (name === 'Uptime %') return `${value}%`; return value; }} />
) : (
No check data available
)}

{resource.checks?.length || 0}

Total Checks

= 95 ? '#4caf50' : resource.uptime >= 80 ? '#ff9800' : '#f44336', fontSize: '2rem', fontWeight: 'bold' }}>{resource.uptime}%

Uptime

{resource.avgResponseTime}ms

Avg Response

{(resource.checks || []).filter((c) => c.status === 'up').length}/{resource.checks?.length || 0}

Successful Checks

); })} {totalItems > pageSize && (
Page { const page = Math.max(1, Math.min(Math.ceil(totalItems / pageSize), parseInt(e.target.value) || 1)); setCurrentPage(page); }} style={{ width: '50px', padding: '0.5rem', border: '1px solid #ddd', borderRadius: '4px', textAlign: 'center' }} /> of {Math.ceil(totalItems / pageSize)}
)}
)}
); } function prepareChartData(checks) { return checks.map((check) => { const date = new Date(check.checked_at); const label = `${date.toLocaleDateString()} ${date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}`; return { label, responseTime: check.response_time || 0, status: check.status, statusNumeric: check.status === 'up' ? 1 : 0, }; }); } function prepareAveragedChartData(checks, days) { if (!checks || checks.length === 0) return []; // Determine interval based on days let intervalHours; if (days <= 7) { intervalHours = 1; // Hourly for 7 days } else if (days <= 14) { intervalHours = 3; // Every 3 hours for 14 days } else { intervalHours = 6; // Every 6 hours for 30+ days } const intervalMs = intervalHours * 60 * 60 * 1000; const now = Date.now(); const startTime = now - (days * 24 * 60 * 60 * 1000); // Group checks into time buckets const buckets = new Map(); checks.forEach(check => { const checkTime = new Date(check.checked_at).getTime(); if (checkTime < startTime) return; const bucketKey = Math.floor(checkTime / intervalMs) * intervalMs; if (!buckets.has(bucketKey)) { buckets.set(bucketKey, { checks: [], upCount: 0, totalResponse: 0, }); } const bucket = buckets.get(bucketKey); bucket.checks.push(check); if (check.status === 'up') { bucket.upCount++; bucket.totalResponse += check.response_time || 0; } }); // Convert buckets to chart data const chartData = Array.from(buckets.entries()) .sort((a, b) => a[0] - b[0]) .map(([timestamp, data]) => { const date = new Date(timestamp); const avgResponse = data.upCount > 0 ? Math.round(data.totalResponse / data.upCount) : 0; const uptime = Math.round((data.upCount / data.checks.length) * 100); let label; if (intervalHours === 1) { label = `${date.toLocaleDateString()} ${date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}`; } else { label = `${date.toLocaleDateString()} ${date.toLocaleTimeString([], { hour: '2-digit' })}`; } return { label, responseTime: avgResponse, statusNumeric: uptime / 100, status: uptime > 50 ? 'up' : 'down', uptime: uptime, }; }); return chartData; } export default History;