Files
madbase/control-plane-ui/src/pages/Logs.tsx
Vlad Durnea a66d908eff
Some checks failed
CI / podman-build (push) Has been cancelled
CI / rust (push) Has been cancelled
chore: full stack stability and migration fixes, plus react UI progress
2026-03-18 09:01:38 +02:00

116 lines
4.1 KiB
TypeScript

import React, { useState } from 'react'
import {
Box,
Typography,
Paper,
TextField,
InputAdornment,
CircularProgress,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Chip,
} from '@mui/material'
import {
Search as SearchIcon,
FilterList as FilterIcon,
} from '@mui/icons-material'
import { useLogs } from '../hooks/useLogs'
export default function Logs() {
const [query, setQuery] = useState('')
const { logs, isLoading, isRefetching } = useLogs(query)
return (
<Box sx={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
<Box sx={{ mb: 3, display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end' }}>
<Box>
<Typography variant="h4" gutterBottom>
Logs Viewer
</Typography>
<Typography variant="body2" color="text.secondary">
Query and analyze system logs from Loki
</Typography>
</Box>
{isRefetching && <CircularProgress size={20} />}
</Box>
<Paper sx={{ p: 2, mb: 3 }}>
<TextField
fullWidth
size="small"
placeholder='Search logs (e.g. {pillar="worker"} |= "error")...'
value={query}
onChange={(e) => setQuery(e.target.value)}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<SearchIcon fontSize="small" />
</InputAdornment>
),
endAdornment: (
<InputAdornment position="end">
<FilterIcon fontSize="small" color="action" cursor="pointer" />
</InputAdornment>
)
}}
/>
</Paper>
<TableContainer component={Paper} sx={{ flexGrow: 1, bgcolor: '#0d1117' }}>
<Table stickyHeader size="small">
<TableHead>
<TableRow>
<TableCell sx={{ bgcolor: '#161b22', color: '#8b949e', borderBottom: '1px solid #30363d', width: 200 }}>Timestamp</TableCell>
<TableCell sx={{ bgcolor: '#161b22', color: '#8b949e', borderBottom: '1px solid #30363d', width: 120 }}>Level</TableCell>
<TableCell sx={{ bgcolor: '#161b22', color: '#8b949e', borderBottom: '1px solid #30363d' }}>Message</TableCell>
</TableRow>
</TableHead>
<TableBody>
{logs.map((log: any, index: number) => (
<TableRow key={index} sx={{ '&:hover': { bgcolor: '#161b22' } }}>
<TableCell sx={{ color: '#8b949e', borderBottom: '1px solid #21262d', fontFamily: 'monospace' }}>
{log.timestamp}
</TableCell>
<TableCell sx={{ borderBottom: '1px solid #21262d' }}>
<Chip
label={log.level || 'INFO'}
size="small"
sx={{
height: 20,
fontSize: '0.7rem',
bgcolor: log.level === 'ERROR' ? '#f8514933' : log.level === 'WARN' ? '#d2992233' : '#23863633',
color: log.level === 'ERROR' ? '#f85149' : log.level === 'WARN' ? '#d29922' : '#3fb950',
border: '1px solid currentColor'
}}
/>
</TableCell>
<TableCell sx={{ color: '#e6edf3', borderBottom: '1px solid #21262d', fontFamily: 'monospace' }}>
{log.message}
</TableCell>
</TableRow>
))}
{logs.length === 0 && !isLoading && (
<TableRow>
<TableCell colSpan={3} sx={{ textAlign: 'center', py: 5, color: '#8b949e', borderBottom: 'none' }}>
No logs found for the current query
</TableCell>
</TableRow>
)}
{isLoading && (
<TableRow>
<TableCell colSpan={3} sx={{ textAlign: 'center', py: 5, borderBottom: 'none' }}>
<CircularProgress size={30} />
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</TableContainer>
</Box>
)
}