import React from 'react'; import { useNavigate } from 'react-router-dom'; import { useQuery } from '@tanstack/react-query'; import { Box, Grid, Paper, Typography, Card, CardContent, List, ListItem, ListItemText, ListItemIcon, Chip, IconButton, Tooltip, Skeleton, } from '@mui/material'; import { Dns as ServerIcon, Speed as SpeedIcon, Warning as WarningIcon, Error as ErrorIcon, CheckCircle as CheckIcon, Thermostat as TempIcon, Refresh as RefreshIcon, PowerSettingsNew as PowerIcon, Memory as MemoryIcon, } from '@mui/icons-material'; import { dashboardApi } from '../utils/api'; import { useMutation, useQueryClient } from '@tanstack/react-query'; interface ServerOverview { id: number; name: string; vendor: string; is_active: boolean; manual_control_enabled: boolean; auto_control_enabled: boolean; max_temp: number | null; avg_fan_speed: number | null; power_consumption: number | null; last_updated: string | null; cached: boolean; } export default function Dashboard() { const navigate = useNavigate(); const queryClient = useQueryClient(); // Stats query - poll every 60 seconds (stats don't change often) const { data: stats } = useQuery({ queryKey: ['dashboard-stats'], queryFn: async () => { const response = await dashboardApi.getStats(); return response.data; }, refetchInterval: 60000, // 60 seconds staleTime: 55000, }); // Server overview query - poll every 30 seconds (matches sensor collector) const { data: overviewData, isLoading: overviewLoading } = useQuery({ queryKey: ['servers-overview'], queryFn: async () => { const response = await dashboardApi.getServersOverview(); return response.data.servers as ServerOverview[]; }, refetchInterval: 30000, // 30 seconds - matches sensor collector staleTime: 25000, // Don't refetch on window focus to reduce load refetchOnWindowFocus: false, }); // Background refresh mutation const refreshMutation = useMutation({ mutationFn: async (serverId: number) => { const response = await dashboardApi.refreshServer(serverId); return response.data; }, onSuccess: () => { // Invalidate overview after a short delay to allow background fetch setTimeout(() => { queryClient.invalidateQueries({ queryKey: ['servers-overview'] }); }, 2000); }, }); const getEventIcon = (eventType: string) => { switch (eventType) { case 'panic': return ; case 'error': return ; case 'warning': return ; default: return ; } }; const StatCard = ({ title, value, icon, color }: { title: string; value: number; icon: React.ReactNode; color: string; }) => ( {icon} {value} {title} ); const ServerCard = ({ server }: { server: ServerOverview }) => { const hasData = server.max_temp !== null || server.avg_fan_speed !== null; const isLoading = !hasData && server.is_active; const getTempColor = (temp: number | null) => { if (temp === null) return 'text.secondary'; if (temp > 80) return 'error.main'; if (temp > 70) return 'warning.main'; return 'success.main'; }; const getStatusChip = () => { if (!server.is_active) { return } />; } if (server.manual_control_enabled) { return } />; } if (server.auto_control_enabled) { return } />; } return ; }; return ( navigate(`/servers/${server.id}`)} > {/* Header */} {server.name} {getStatusChip()} {/* Metrics Grid - Always show values or -- placeholder */} {server.max_temp !== null ? `${Math.round(server.max_temp)}°C` : '--'} Max Temp {server.avg_fan_speed !== null ? `${Math.round(server.avg_fan_speed)}%` : '--'} Avg Fan {server.power_consumption !== null ? `${Math.round(server.power_consumption)}W` : '--'} Power {/* Footer */} {server.vendor || 'Unknown Vendor'} {isLoading ? ( ) : server.cached ? ( ) : null} { e.stopPropagation(); refreshMutation.mutate(server.id); }} disabled={refreshMutation.isPending} > ); }; // Show placeholder cards while loading initial data const ServersPlaceholderGrid = () => ( {[1, 2, 3, 4].map((i) => ( ))} ); return ( Dashboard {/* Stats Cards */} } color="primary.main" /> } color="success.main" /> } color="info.main" /> } color="warning.main" /> } color="error.main" /> {/* Servers Grid */} Server Overview {overviewLoading ? ( ) : overviewData && overviewData.length > 0 ? ( {overviewData.map((server) => ( ))} ) : ( No servers configured Add your first server to start monitoring navigate('/servers')} clickable /> )} {/* Recent Logs */} Recent Events navigate('/logs')} clickable /> {stats?.recent_logs?.slice(0, 10).map((log: any) => ( {getEventIcon(log.event_type)} ))} {!stats?.recent_logs?.length && ( )} About IPMI Fan Control This application allows you to control fan speeds on Dell T710 and compatible servers using IPMI commands. Features include: ); }