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:
);
}