import React, { useEffect, useState, useRef } from 'react'; import { fetchGroupsWithClients, restartClient } from './apiClients'; import type { Group, Client } from './apiClients'; import { GridComponent, ColumnsDirective, ColumnDirective, Page, DetailRow, Inject, Sort, } from '@syncfusion/ej2-react-grids'; const REFRESH_INTERVAL = 15000; // 15 Sekunden // Typ für Collapse-Event // type DetailRowCollapseArgs = { // data?: { id?: string | number }; // }; // Typ für DataBound-Event type DetailRowDataBoundArgs = { data?: { id?: string | number }; }; const Dashboard: React.FC = () => { const [groups, setGroups] = useState([]); const [expandedGroupIds, setExpandedGroupIds] = useState([]); const gridRef = useRef(null); // Funktion für das Schließen einer Gruppe (Collapse) // const onDetailCollapse = (args: DetailRowCollapseArgs) => { // if (args && args.data && args.data.id) { // const groupId = String(args.data.id); // setExpandedGroupIds(prev => prev.filter(id => String(id) !== groupId)); // } // }; // // Registriere das Event nach dem Mount am Grid // useEffect(() => { // if (gridRef.current) { // gridRef.current.detailCollapse = onDetailCollapse; // } // }, []); // Optimiertes Update: Nur bei echten Datenänderungen wird das Grid aktualisiert useEffect(() => { let lastGroups: Group[] = []; const fetchAndUpdate = async () => { const newGroups = await fetchGroupsWithClients(); // Vergleiche nur die relevanten Felder (id, clients, is_alive) const changed = lastGroups.length !== newGroups.length || lastGroups.some((g, i) => { const ng = newGroups[i]; if (!ng || g.id !== ng.id || g.clients.length !== ng.clients.length) return true; // Optional: Vergleiche tiefer, z.B. Alive-Status for (let j = 0; j < g.clients.length; j++) { if ( g.clients[j].uuid !== ng.clients[j].uuid || g.clients[j].is_alive !== ng.clients[j].is_alive ) { return true; } } return false; }); if (changed) { setGroups(newGroups); lastGroups = newGroups; setTimeout(() => { expandedGroupIds.forEach(id => { const rowIndex = newGroups.findIndex(g => String(g.id) === String(id)); if (rowIndex !== -1 && gridRef.current) { gridRef.current.detailRowModule.expand(rowIndex); } }); }, 100); } }; fetchAndUpdate(); const interval = setInterval(fetchAndUpdate, REFRESH_INTERVAL); return () => clearInterval(interval); }, [expandedGroupIds]); // Health-Badge const getHealthBadge = (group: Group) => { const total = group.clients.length; const alive = group.clients.filter((c: Client) => c.is_alive).length; const ratio = total === 0 ? 0 : alive / total; let color = 'danger'; let text = `${alive} / ${total} offline`; if (ratio === 1) { color = 'success'; text = `${alive} / ${total} alive`; } else if (ratio >= 0.5) { color = 'warning'; text = `${alive} / ${total} teilw. alive`; } return {text}; }; // Einfache Tabelle für Clients einer Gruppe const getClientTable = (group: Group) => (
{/* { if (!props.last_alive) return '-'; const dateStr = props.last_alive.endsWith('Z') ? props.last_alive : props.last_alive + 'Z'; const date = new Date(dateStr); return isNaN(date.getTime()) ? props.last_alive : date.toLocaleString(); }} /> */} ( {props.is_alive ? 'alive' : 'offline'} )} sortComparer={(a, b) => (a === b ? 0 : a ? -1 : 1)} /> ( )} />
); // Neustart-Logik const handleRestartClient = async (uuid: string) => { try { const result = await restartClient(uuid); alert(`Neustart erfolgreich: ${result.message}`); } catch (error: unknown) { if (error && typeof error === 'object' && 'message' in error) { alert(`Fehler beim Neustart: ${(error as { message: string }).message}`); } else { alert('Unbekannter Fehler beim Neustart'); } } }; // SyncFusion Grid liefert im Event die Zeile/Gruppe const onDetailDataBound = (args: DetailRowDataBoundArgs) => { if (args && args.data && args.data.id) { const groupId = String(args.data.id); setExpandedGroupIds(prev => (prev.includes(groupId) ? prev : [...prev, groupId])); } }; return (

Dashboard

Raumgruppen Übersicht

getClientTable(props)} detailDataBound={onDetailDataBound} ref={gridRef} > getHealthBadge(props)} /> {groups.length === 0 && (
Keine Gruppen gefunden.
)}
); }; export default Dashboard;