import React, { useState, useEffect, useCallback } from 'react';
import {
ActionIcon,
Box,
Button,
Card,
Group,
NumberInput,
Pagination,
Select,
Stack,
Text,
Title,
} from '@mantine/core';
import { useElementSize } from '@mantine/hooks';
import {
ChevronDown,
CirclePlay,
Download,
Gauge,
HardDriveDownload,
LogIn,
LogOut,
RefreshCw,
ShieldAlert,
SquareX,
Timer,
Users,
Video,
XCircle,
} from 'lucide-react';
import API from '../api';
import useLocalStorage from '../hooks/useLocalStorage';
import { format } from '../utils/dateTimeUtils.js';
const getEventIcon = (eventType) => {
switch (eventType) {
case 'channel_start':
return ;
case 'channel_stop':
return ;
case 'channel_reconnect':
return ;
case 'channel_buffering':
return ;
case 'channel_failover':
return ;
case 'client_connect':
return ;
case 'client_disconnect':
return ;
case 'recording_start':
return ;
case 'recording_end':
return ;
case 'stream_switch':
return ;
case 'm3u_refresh':
return ;
case 'm3u_download':
return ;
case 'epg_refresh':
return ;
case 'epg_download':
return ;
case 'login_success':
return ;
case 'login_failed':
return ;
case 'logout':
return ;
case 'm3u_blocked':
return ;
case 'epg_blocked':
return ;
default:
return ;
}
};
const getEventColor = (eventType) => {
switch (eventType) {
case 'channel_start':
case 'client_connect':
case 'recording_start':
case 'login_success':
return 'green';
case 'channel_reconnect':
return 'yellow';
case 'channel_stop':
case 'client_disconnect':
case 'recording_end':
case 'logout':
return 'gray';
case 'channel_buffering':
return 'yellow';
case 'channel_failover':
case 'channel_error':
return 'orange';
case 'stream_switch':
return 'blue';
case 'm3u_refresh':
case 'epg_refresh':
return 'cyan';
case 'm3u_download':
case 'epg_download':
return 'teal';
case 'login_failed':
case 'm3u_blocked':
case 'epg_blocked':
return 'red';
default:
return 'gray';
}
};
const getSystemEvents = (eventsLimit, offset) => {
return API.getSystemEvents(eventsLimit, offset);
}
const Event = ({ event }) => {
const [dateFormatSetting] = useLocalStorage('date-format', 'mdy');
const dateFormat = dateFormatSetting === 'mdy' ? 'MM/DD' : 'DD/MM';
return (
{getEventIcon(event.event_type)}
{event.event_type_display || event.event_type}
{event.channel_name && (
{event.channel_name}
)}
{event.details &&
Object.keys(event.details).length > 0 && (
{Object.entries(event.details)
.filter(([key]) =>
!['stream_url', 'new_url'].includes(key))
.map(([key, value]) => `${key}: ${value}`)
.join(', ')}
)}
{format(event.timestamp, `${dateFormat} HH:mm:ss`)}
);
};
const SystemEvents = () => {
const [events, setEvents] = useState([]);
const [totalEvents, setTotalEvents] = useState(0);
const [isExpanded, setIsExpanded] = useState(false);
const { ref: cardRef, width: cardWidth } = useElementSize();
const isNarrow = cardWidth < 650;
const [isLoading, setIsLoading] = useState(false);
const [eventsRefreshInterval, setEventsRefreshInterval] = useLocalStorage(
'events-refresh-interval',
0
);
const [eventsLimit, setEventsLimit] = useLocalStorage('events-limit', 100);
const [currentPage, setCurrentPage] = useState(1);
// Calculate offset based on current page and limit
const offset = (currentPage - 1) * eventsLimit;
const totalPages = Math.ceil(totalEvents / eventsLimit);
const fetchEvents = useCallback(async () => {
try {
setIsLoading(true);
const response = await getSystemEvents(eventsLimit, offset);
if (response && response.events) {
setEvents(response.events);
setTotalEvents(response.total || 0);
}
} catch (error) {
console.error('Error fetching system events:', error);
} finally {
setIsLoading(false);
}
}, [eventsLimit, offset]);
// Fetch events on mount and when eventsRefreshInterval changes
useEffect(() => {
fetchEvents();
// Set up polling if interval is set and events section is expanded
if (eventsRefreshInterval > 0 && isExpanded) {
const interval = setInterval(fetchEvents, eventsRefreshInterval * 1000);
return () => clearInterval(interval);
}
}, [fetchEvents, eventsRefreshInterval, isExpanded]);
// Reset to first page when limit changes
useEffect(() => {
setCurrentPage(1);
}, [eventsLimit]);
return (
System Events
{(isExpanded || !isNarrow) && (
<>
setEventsLimit(value || 10)}
min={10}
max={1000}
step={10}
style={{ width: 130 }}
/>
{isExpanded && (
<>
{totalEvents > eventsLimit && (
Showing {offset + 1}-
{Math.min(offset + eventsLimit, totalEvents)} of {totalEvents}
)}
{events.length === 0 ? (
No events recorded yet
) : (
events.map((event) => (
))
)}
>
)}
);
};
export default SystemEvents;