'use client'; import { useState, useEffect } from 'react'; import { Box, Card, CardContent, Typography, Button, Alert, Chip, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Paper, CircularProgress, Dialog, DialogTitle, DialogContent, DialogActions, FormControl, InputLabel, Select, MenuItem, LinearProgress } from '@mui/material'; import { Storage, Memory, Computer, Security, Backup, Refresh, Download, CheckCircle, Warning, Error } from '@mui/icons-material'; interface SystemHealth { timestamp: string; status: string; responseTime: number; metrics: { database: { status: string; responseTime: number; connections: { active: string; max: string; }; }; application: { status: string; uptime: number; memory: { used: number; total: number; rss: number; }; nodeVersion: string; platform: string; arch: string; }; }; database: { tables: { users: number; conversations: number; messages: number; prayerRequests: number; prayers: number; bookmarks: number; notes: number; }; recentActivity: { last24h: { newUsers: number; newConversations: number; newPrayers: number; }; }; }; security: { adminUsers: number; suspendedUsers: number; inactivePrayerRequests: number; inactiveConversations: number; }; } interface Backup { filename: string; size: string; date: string; type: string; } export function SystemDashboard() { const [health, setHealth] = useState(null); const [backups, setBackups] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(''); const [backupDialogOpen, setBackupDialogOpen] = useState(false); const [backupType, setBackupType] = useState('database'); const [backupLoading, setBackupLoading] = useState(false); const [refreshing, setRefreshing] = useState(false); const fetchSystemHealth = async () => { try { const response = await fetch('/api/admin/system/health', { credentials: 'include' }); if (response.ok) { const data = await response.json(); setHealth(data); } else { setError('Failed to load system health data'); } } catch (error) { setError('Network error loading system health'); } }; const fetchBackups = async () => { try { const response = await fetch('/api/admin/system/backup', { credentials: 'include' }); if (response.ok) { const data = await response.json(); setBackups(data.backups); } } catch (error) { console.error('Error loading backups:', error); } }; const refreshData = async () => { setRefreshing(true); await Promise.all([fetchSystemHealth(), fetchBackups()]); setRefreshing(false); }; useEffect(() => { const loadData = async () => { setLoading(true); await Promise.all([fetchSystemHealth(), fetchBackups()]); setLoading(false); }; loadData(); // Auto-refresh every 30 seconds const interval = setInterval(fetchSystemHealth, 30000); return () => clearInterval(interval); }, []); const handleCreateBackup = async () => { setBackupLoading(true); try { const response = await fetch('/api/admin/system/backup', { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ type: backupType }) }); if (response.ok) { await fetchBackups(); setBackupDialogOpen(false); } else { const data = await response.json(); setError(data.error || 'Backup failed'); } } catch (error) { setError('Network error creating backup'); } finally { setBackupLoading(false); } }; const getStatusIcon = (status: string) => { switch (status) { case 'healthy': return ; case 'degraded': return ; case 'unhealthy': return ; default: return ; } }; const getStatusColor = (status: string) => { switch (status) { case 'healthy': return 'success'; case 'degraded': return 'warning'; case 'unhealthy': return 'error'; default: return 'default'; } }; const formatUptime = (seconds: number) => { const days = Math.floor(seconds / 86400); const hours = Math.floor((seconds % 86400) / 3600); const minutes = Math.floor((seconds % 3600) / 60); if (days > 0) { return `${days}d ${hours}h ${minutes}m`; } else if (hours > 0) { return `${hours}h ${minutes}m`; } else { return `${minutes}m`; } }; if (loading) { return ( ); } if (error) { return ( {error} ); } if (!health) return null; const memoryUsagePercent = (health.metrics.application.memory.used / health.metrics.application.memory.total) * 100; return ( {/* Header with Refresh */} System Status {/* System Health Overview */} System Status {getStatusIcon(health.status)} Database {getStatusIcon(health.metrics.database.status)} {health.metrics.database.responseTime}ms Memory Usage {health.metrics.application.memory.used}MB / {health.metrics.application.memory.total}MB 80 ? 'error' : memoryUsagePercent > 60 ? 'warning' : 'primary'} /> Uptime {formatUptime(health.metrics.application.uptime)} {/* Database Statistics */} Database Statistics Table Records {Object.entries(health.database.tables).map(([table, count]) => ( {table} {count.toLocaleString()} ))}
{/* Security Status */} Security Status Admin Users {health.security.adminUsers} Suspended Users {health.security.suspendedUsers} Inactive Prayers {health.security.inactivePrayerRequests} Inactive Chats {health.security.inactiveConversations}
{/* Recent Activity & Backups */} {/* Recent Activity */} Recent Activity (24h) New Users {health.database.recentActivity.last24h.newUsers} New Conversations {health.database.recentActivity.last24h.newConversations} New Prayers {health.database.recentActivity.last24h.newPrayers} {/* System Backups */} System Backups {backups.length > 0 ? ( Type Size Date {backups.slice(0, 5).map((backup, index) => ( {backup.size} {backup.date} ))}
) : ( No backups available )}
{/* System Information */} System Information Node.js Version {health.metrics.application.nodeVersion} Platform {health.metrics.application.platform} Architecture {health.metrics.application.arch} Last Check {new Date(health.timestamp).toLocaleString()} {/* Backup Creation Dialog */} setBackupDialogOpen(false)} maxWidth="sm" fullWidth> Create System Backup Backup Type {backupType === 'database' ? 'Creates a backup of the PostgreSQL database containing all user data, conversations, and content.' : 'Creates a complete backup of the application including code, configuration, and database.'}
); }