← 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