ipmi-fan-control/frontend/src/pages/Logs.tsx

198 lines
5.6 KiB
TypeScript

import { useState } from 'react';
import { useQuery } from '@tanstack/react-query';
import {
Box,
Typography,
Paper,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Chip,
FormControl,
InputLabel,
Select,
MenuItem,
Pagination,
} from '@mui/material';
import {
Error as ErrorIcon,
Warning as WarningIcon,
CheckCircle as CheckIcon,
Info as InfoIcon,
Speed as SpeedIcon,
} from '@mui/icons-material';
import { logsApi, serversApi } from '../utils/api';
const LOGS_PER_PAGE = 25;
export default function Logs() {
const [page, setPage] = useState(1);
const [eventType, setEventType] = useState<string>('');
const [serverFilter, setServerFilter] = useState<number | ''>('');
const { data: servers } = useQuery({
queryKey: ['servers'],
queryFn: async () => {
const response = await serversApi.getAll();
return response.data;
},
});
const { data: logs } = useQuery({
queryKey: ['logs', eventType, serverFilter],
queryFn: async () => {
const response = await logsApi.getAll({
event_type: eventType || undefined,
server_id: serverFilter || undefined,
limit: 100,
});
return response.data;
},
});
const getEventIcon = (eventType: string) => {
switch (eventType) {
case 'panic':
return <ErrorIcon color="error" />;
case 'error':
return <ErrorIcon color="error" />;
case 'warning':
return <WarningIcon color="warning" />;
case 'fan_change':
return <SpeedIcon color="info" />;
case 'info':
return <InfoIcon color="info" />;
default:
return <CheckIcon color="success" />;
}
};
const getEventColor = (eventType: string) => {
switch (eventType) {
case 'panic':
return 'error';
case 'error':
return 'error';
case 'warning':
return 'warning';
case 'fan_change':
return 'info';
case 'info':
return 'default';
default:
return 'default';
}
};
const totalPages = Math.ceil((logs?.length || 0) / LOGS_PER_PAGE);
const paginatedLogs = logs?.slice((page - 1) * LOGS_PER_PAGE, page * LOGS_PER_PAGE);
return (
<Box>
<Typography variant="h4" gutterBottom>
System Logs
</Typography>
<Paper sx={{ p: 2, mb: 2 }}>
<Box sx={{ display: 'flex', gap: 2, flexWrap: 'wrap' }}>
<FormControl sx={{ minWidth: 150 }}>
<InputLabel>Event Type</InputLabel>
<Select
value={eventType}
label="Event Type"
onChange={(e) => setEventType(e.target.value)}
>
<MenuItem value="">All</MenuItem>
<MenuItem value="panic">Panic</MenuItem>
<MenuItem value="error">Error</MenuItem>
<MenuItem value="warning">Warning</MenuItem>
<MenuItem value="fan_change">Fan Change</MenuItem>
<MenuItem value="info">Info</MenuItem>
</Select>
</FormControl>
<FormControl sx={{ minWidth: 200 }}>
<InputLabel>Server</InputLabel>
<Select
value={serverFilter}
label="Server"
onChange={(e) => setServerFilter(e.target.value as number | '')}
>
<MenuItem value="">All Servers</MenuItem>
{servers?.map((server) => (
<MenuItem key={server.id} value={server.id}>
{server.name}
</MenuItem>
))}
</Select>
</FormControl>
</Box>
</Paper>
<TableContainer component={Paper}>
<Table>
<TableHead>
<TableRow>
<TableCell width={50}></TableCell>
<TableCell>Time</TableCell>
<TableCell>Server</TableCell>
<TableCell>Type</TableCell>
<TableCell>Message</TableCell>
<TableCell>Details</TableCell>
</TableRow>
</TableHead>
<TableBody>
{paginatedLogs?.map((log) => (
<TableRow key={log.id} hover>
<TableCell>{getEventIcon(log.event_type)}</TableCell>
<TableCell>
{new Date(log.timestamp).toLocaleString()}
</TableCell>
<TableCell>
{log.server_id
? servers?.find((s) => s.id === log.server_id)?.name ||
`Server ${log.server_id}`
: 'System'}
</TableCell>
<TableCell>
<Chip
size="small"
label={log.event_type}
color={getEventColor(log.event_type) as any}
/>
</TableCell>
<TableCell>{log.message}</TableCell>
<TableCell>{log.details}</TableCell>
</TableRow>
))}
{!paginatedLogs?.length && (
<TableRow>
<TableCell colSpan={6} align="center">
<Typography color="text.secondary" sx={{ py: 4 }}>
No logs found
</Typography>
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</TableContainer>
{totalPages > 1 && (
<Box sx={{ display: 'flex', justifyContent: 'center', mt: 3 }}>
<Pagination
count={totalPages}
page={page}
onChange={(_, value) => setPage(value)}
color="primary"
/>
</Box>
)}
</Box>
);
}