← back to Dear Bubbe Admin
frontend/src/components/Dashboard.js
328 lines
import React, { useState, useEffect } from 'react'
import axios from 'axios'
const Dashboard = ({ stats }) => {
const [dashboardData, setDashboardData] = useState({
userActivity: {},
conversations: {},
alerts: {},
system: {}
})
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
const [refreshInterval, setRefreshInterval] = useState(null)
useEffect(() => {
fetchDashboardData()
// Set up auto-refresh every 30 seconds
const interval = setInterval(fetchDashboardData, 30000)
setRefreshInterval(interval)
return () => {
if (interval) clearInterval(interval)
}
}, [])
const fetchDashboardData = async () => {
try {
setError(null)
const response = await axios.get('/api/monitoring/dashboard')
if (response.data.success) {
setDashboardData(response.data)
}
} catch (error) {
console.error('Failed to fetch dashboard data:', error)
setError('Failed to load dashboard data')
} finally {
setLoading(false)
}
}
const formatUptime = (seconds) => {
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`
if (hours > 0) return `${hours}h ${minutes}m`
return `${minutes}m`
}
const getSystemHealthColor = (health) => {
switch (health) {
case 'healthy': return '#10b981'
case 'warning': return '#f59e0b'
case 'critical': return '#ef4444'
default: return '#6b7280'
}
}
const getSystemHealthText = (memUsage, cpuUsage) => {
if (memUsage > 90 || cpuUsage > 90) return 'Critical'
if (memUsage > 70 || cpuUsage > 70) return 'Warning'
return 'Healthy'
}
if (loading) {
return (
<div style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: '400px'
}}>
<div className="loading-spinner"></div>
<span style={{ marginLeft: '12px' }}>Loading dashboard...</span>
</div>
)
}
if (error) {
return (
<div className="card">
<div className="card-content">
<div style={{
color: '#ef4444',
textAlign: 'center',
padding: '40px'
}}>
<h3>⚠️ {error}</h3>
<button
onClick={fetchDashboardData}
className="btn btn-primary"
style={{ marginTop: '16px' }}
>
Retry
</button>
</div>
</div>
</div>
)
}
const { userActivity, conversations, alerts, system } = dashboardData
const systemHealth = getSystemHealthText(system.memoryUsage, system.cpuUsage)
return (
<div>
<div style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: '24px'
}}>
<h2 style={{ fontSize: '24px', fontWeight: '600', color: '#1e293b' }}>
Dashboard Overview
</h2>
<div style={{ fontSize: '14px', color: '#64748b' }}>
Last updated: {dashboardData.timestamp ? new Date(dashboardData.timestamp).toLocaleTimeString() : 'Unknown'}
</div>
</div>
{/* Key Metrics */}
<div className="stat-cards">
<div className="stat-card">
<div className="stat-label">Active Users</div>
<div className="stat-value-large" style={{ color: '#3b82f6' }}>
{userActivity.activeUsers || 0}
</div>
<div style={{ fontSize: '12px', color: '#64748b', marginTop: '4px' }}>
{userActivity.activeSessions || 0} active sessions
</div>
</div>
<div className="stat-card">
<div className="stat-label">Conversations Today</div>
<div className="stat-value-large" style={{ color: '#10b981' }}>
{conversations.today || 0}
</div>
<div style={{ fontSize: '12px', color: '#64748b', marginTop: '4px' }}>
{conversations.lastHour || 0} in last hour
</div>
</div>
<div className={`stat-card ${alerts.critical > 0 ? 'critical' : alerts.today > 0 ? 'warning' : 'success'}`}>
<div className="stat-label">Security Alerts</div>
<div className="stat-value-large" style={{
color: alerts.critical > 0 ? '#ef4444' : alerts.today > 0 ? '#f59e0b' : '#10b981'
}}>
{alerts.today || 0}
</div>
<div style={{ fontSize: '12px', color: '#64748b', marginTop: '4px' }}>
{alerts.critical || 0} critical alerts
</div>
</div>
<div className={`stat-card ${systemHealth === 'Critical' ? 'critical' : systemHealth === 'Warning' ? 'warning' : 'success'}`}>
<div className="stat-label">System Health</div>
<div className="stat-value-large" style={{
color: getSystemHealthColor(systemHealth.toLowerCase())
}}>
{systemHealth}
</div>
<div style={{ fontSize: '12px', color: '#64748b', marginTop: '4px' }}>
{system.memoryUsage || 0}% memory, {system.cpuUsage || 0}% CPU
</div>
</div>
</div>
{/* Detailed Cards */}
<div className="dashboard-grid">
{/* User Activity */}
<div className="card">
<div className="card-header">
<h3 className="card-title">User Activity</h3>
</div>
<div className="card-content">
<div style={{ display: 'grid', gap: '12px' }}>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<span>Total Users:</span>
<strong>{userActivity.totalUsers || 0}</strong>
</div>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<span>New Users Today:</span>
<strong style={{ color: '#10b981' }}>{userActivity.newUsersToday || 0}</strong>
</div>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<span>Blocked Users:</span>
<strong style={{ color: '#ef4444' }}>{userActivity.blockedUsers || 0}</strong>
</div>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<span>Active Sessions:</span>
<strong style={{ color: '#3b82f6' }}>{userActivity.activeSessions || 0}</strong>
</div>
</div>
</div>
</div>
{/* System Status */}
<div className="card">
<div className="card-header">
<h3 className="card-title">System Status</h3>
</div>
<div className="card-content">
<div style={{ display: 'grid', gap: '12px' }}>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<span>Memory Usage:</span>
<strong style={{
color: system.memoryUsage > 80 ? '#ef4444' : system.memoryUsage > 60 ? '#f59e0b' : '#10b981'
}}>
{system.memoryUsage || 0}%
</strong>
</div>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<span>CPU Usage:</span>
<strong style={{
color: system.cpuUsage > 80 ? '#ef4444' : system.cpuUsage > 60 ? '#f59e0b' : '#10b981'
}}>
{system.cpuUsage || 0}%
</strong>
</div>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<span>System Uptime:</span>
<strong>{system.uptime ? formatUptime(system.uptime) : 'Unknown'}</strong>
</div>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<span>Load Average:</span>
<strong>{system.loadAverage ? system.loadAverage.toFixed(2) : '0.00'}</strong>
</div>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<span>Process Memory:</span>
<strong>{system.processMemoryMB || 0} MB</strong>
</div>
</div>
</div>
</div>
{/* Recent Alerts Preview */}
<div className="card">
<div className="card-header">
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<h3 className="card-title">Security Overview</h3>
<a href="/alerts" className="btn btn-small btn-secondary">View All</a>
</div>
</div>
<div className="card-content">
<div style={{ display: 'grid', gap: '12px' }}>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<span>Critical Alerts:</span>
<span className="badge badge-critical">{alerts.critical || 0}</span>
</div>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<span>Total Today:</span>
<span className="badge badge-medium">{alerts.today || 0}</span>
</div>
<div style={{ fontSize: '12px', color: '#64748b', textAlign: 'center', marginTop: '8px' }}>
{alerts.today === 0 ? '✅ All clear!' : '⚠️ Active monitoring in progress'}
</div>
</div>
</div>
</div>
{/* Quick Actions */}
<div className="card">
<div className="card-header">
<h3 className="card-title">Quick Actions</h3>
</div>
<div className="card-content">
<div style={{ display: 'grid', gap: '12px' }}>
<a href="/alerts" className="btn btn-primary" style={{ textDecoration: 'none' }}>
🚨 View All Alerts
</a>
<a href="/users" className="btn btn-secondary" style={{ textDecoration: 'none' }}>
👥 Manage Users
</a>
<a href="/monitoring" className="btn btn-secondary" style={{ textDecoration: 'none' }}>
📊 Live Monitoring
</a>
<a href="/reports" className="btn btn-secondary" style={{ textDecoration: 'none' }}>
📋 Generate Reports
</a>
</div>
</div>
</div>
</div>
{/* Status Banner */}
<div style={{
marginTop: '24px',
padding: '16px 24px',
borderRadius: '8px',
background: systemHealth === 'Healthy' ? '#f0f9ff' :
systemHealth === 'Warning' ? '#fffbeb' : '#fef2f2',
border: `1px solid ${
systemHealth === 'Healthy' ? '#3b82f6' :
systemHealth === 'Warning' ? '#f59e0b' : '#ef4444'
}`,
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center'
}}>
<div>
<strong style={{
color: systemHealth === 'Healthy' ? '#1d4ed8' :
systemHealth === 'Warning' ? '#d97706' : '#dc2626'
}}>
{systemHealth === 'Healthy' ? '✅' :
systemHealth === 'Warning' ? '⚠️' : '🚨'} System Status: {systemHealth}
</strong>
<div style={{ fontSize: '14px', color: '#64748b', marginTop: '4px' }}>
Dear Bubbe AI is {systemHealth === 'Healthy' ? 'operating normally' :
systemHealth === 'Warning' ? 'experiencing minor issues' :
'experiencing critical issues'}
</div>
</div>
<button
onClick={fetchDashboardData}
className="btn btn-small btn-secondary"
>
Refresh
</button>
</div>
</div>
)
}
export default Dashboard