← back to Dear Bubbe Admin

frontend/src/components/MonitoringPage.js

380 lines

import React, { useState, useEffect } from 'react'
import axios from 'axios'

const MonitoringPage = () => {
  const [liveData, setLiveData] = useState({})
  const [bubbeHealth, setBubbeHealth] = useState({})
  const [loading, setLoading] = useState(true)
  const [autoRefresh, setAutoRefresh] = useState(true)

  useEffect(() => {
    fetchLiveData()
    checkBubbeHealth()

    let interval
    if (autoRefresh) {
      interval = setInterval(() => {
        fetchLiveData()
        checkBubbeHealth()
      }, 10000) // Refresh every 10 seconds
    }

    return () => {
      if (interval) clearInterval(interval)
    }
  }, [autoRefresh])

  const fetchLiveData = async () => {
    try {
      const response = await axios.get('/api/monitoring/dashboard')
      setLiveData(response.data)
      setLoading(false)
    } catch (error) {
      console.error('Failed to fetch live data:', error)
    }
  }

  const checkBubbeHealth = async () => {
    try {
      const response = await axios.get('/api/monitoring/bubbe-health')
      setBubbeHealth(response.data)
    } catch (error) {
      console.error('Failed to check Bubbe health:', error)
    }
  }

  if (loading) {
    return (
      <div style={{ display: 'flex', justifyContent: 'center', padding: '40px' }}>
        <div className="loading-spinner"></div>
        <span style={{ marginLeft: '12px' }}>Loading monitoring data...</span>
      </div>
    )
  }

  return (
    <div>
      <div style={{
        display: 'flex',
        justifyContent: 'space-between',
        alignItems: 'center',
        marginBottom: '24px'
      }}>
        <h2>Live Monitoring</h2>
        <div style={{ display: 'flex', gap: '12px', alignItems: 'center' }}>
          <label style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
            <input
              type="checkbox"
              checked={autoRefresh}
              onChange={(e) => setAutoRefresh(e.target.checked)}
            />
            Auto-refresh (10s)
          </label>
          <button onClick={() => { fetchLiveData(); checkBubbeHealth(); }} className="btn btn-primary">
            Refresh Now
          </button>
        </div>
      </div>

      {/* Health Status Banner */}
      <div className={`card ${bubbeHealth.overall === 'healthy' ? 'success' : bubbeHealth.overall === 'degraded' ? 'warning' : 'critical'}`} style={{ marginBottom: '24px' }}>
        <div className="card-content">
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
            <div>
              <h3 style={{ margin: 0, fontSize: '18px' }}>
                {bubbeHealth.overall === 'healthy' ? '✅' : bubbeHealth.overall === 'degraded' ? '⚠️' : '🚨'} 
                Dear Bubbe API Status: {bubbeHealth.overall || 'Unknown'}
              </h3>
              <div style={{ fontSize: '14px', marginTop: '4px', opacity: 0.8 }}>
                {bubbeHealth.api?.responseTime && `Response time: ${bubbeHealth.api.responseTime}ms`}
                {bubbeHealth.pm2?.status && ` • PM2 Status: ${bubbeHealth.pm2.status}`}
              </div>
            </div>
            <div style={{ fontSize: '12px', opacity: 0.7 }}>
              Last checked: {bubbeHealth.timestamp ? new Date(bubbeHealth.timestamp).toLocaleTimeString() : 'Unknown'}
            </div>
          </div>
        </div>
      </div>

      {/* Real-time Stats */}
      <div className="stat-cards" style={{ marginBottom: '24px' }}>
        <div className="stat-card">
          <div className="stat-label">Active Users (5 min)</div>
          <div className="stat-value-large" style={{ color: '#3b82f6' }}>
            {liveData.userActivity?.activeUsers || 0}
          </div>
          <div style={{ fontSize: '12px', color: '#64748b', marginTop: '4px' }}>
            {liveData.userActivity?.activeSessions || 0} sessions
          </div>
        </div>

        <div className="stat-card">
          <div className="stat-label">Conversations/Hour</div>
          <div className="stat-value-large" style={{ color: '#10b981' }}>
            {liveData.conversations?.lastHour || 0}
          </div>
          <div style={{ fontSize: '12px', color: '#64748b', marginTop: '4px' }}>
            {liveData.conversations?.today || 0} today
          </div>
        </div>

        <div className="stat-card critical">
          <div className="stat-label">Active Alerts</div>
          <div className="stat-value-large" style={{ color: '#ef4444' }}>
            {liveData.alerts?.today || 0}
          </div>
          <div style={{ fontSize: '12px', color: '#64748b', marginTop: '4px' }}>
            {liveData.alerts?.critical || 0} critical
          </div>
        </div>

        <div className="stat-card">
          <div className="stat-label">System Load</div>
          <div className="stat-value-large" style={{ 
            color: liveData.system?.cpuUsage > 80 ? '#ef4444' : 
                   liveData.system?.cpuUsage > 60 ? '#f59e0b' : '#10b981'
          }}>
            {liveData.system?.cpuUsage || 0}%
          </div>
          <div style={{ fontSize: '12px', color: '#64748b', marginTop: '4px' }}>
            CPU usage
          </div>
        </div>
      </div>

      {/* Detailed Monitoring */}
      <div className="dashboard-grid">
        {/* System Resources */}
        <div className="card">
          <div className="card-header">
            <h3 className="card-title">System Resources</h3>
          </div>
          <div className="card-content">
            <div style={{ display: 'grid', gap: '16px' }}>
              <div>
                <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '8px' }}>
                  <span>Memory Usage</span>
                  <span style={{ fontWeight: '600' }}>{liveData.system?.memoryUsage || 0}%</span>
                </div>
                <div style={{ 
                  background: '#e2e8f0', 
                  height: '8px', 
                  borderRadius: '4px',
                  overflow: 'hidden'
                }}>
                  <div style={{
                    width: `${liveData.system?.memoryUsage || 0}%`,
                    height: '100%',
                    background: liveData.system?.memoryUsage > 80 ? '#ef4444' : 
                               liveData.system?.memoryUsage > 60 ? '#f59e0b' : '#10b981',
                    transition: 'width 0.3s ease'
                  }}></div>
                </div>
              </div>

              <div>
                <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '8px' }}>
                  <span>CPU Usage</span>
                  <span style={{ fontWeight: '600' }}>{liveData.system?.cpuUsage || 0}%</span>
                </div>
                <div style={{ 
                  background: '#e2e8f0', 
                  height: '8px', 
                  borderRadius: '4px',
                  overflow: 'hidden'
                }}>
                  <div style={{
                    width: `${liveData.system?.cpuUsage || 0}%`,
                    height: '100%',
                    background: liveData.system?.cpuUsage > 80 ? '#ef4444' : 
                               liveData.system?.cpuUsage > 60 ? '#f59e0b' : '#10b981',
                    transition: 'width 0.3s ease'
                  }}></div>
                </div>
              </div>

              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '16px', marginTop: '16px' }}>
                <div style={{ textAlign: 'center' }}>
                  <div style={{ fontSize: '20px', fontWeight: '600', color: '#1e293b' }}>
                    {liveData.system?.processMemoryMB || 0}
                  </div>
                  <div style={{ fontSize: '12px', color: '#64748b' }}>Process Memory (MB)</div>
                </div>
                <div style={{ textAlign: 'center' }}>
                  <div style={{ fontSize: '20px', fontWeight: '600', color: '#1e293b' }}>
                    {liveData.system?.loadAverage?.toFixed(2) || '0.00'}
                  </div>
                  <div style={{ fontSize: '12px', color: '#64748b' }}>Load Average</div>
                </div>
              </div>
            </div>
          </div>
        </div>

        {/* API Health */}
        <div className="card">
          <div className="card-header">
            <h3 className="card-title">API Health Check</h3>
          </div>
          <div className="card-content">
            <div style={{ display: 'grid', gap: '12px' }}>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                <span>API Status:</span>
                <span style={{
                  padding: '4px 8px',
                  borderRadius: '4px',
                  fontSize: '12px',
                  fontWeight: '600',
                  background: bubbeHealth.api?.status === 'healthy' ? '#dcfce7' : '#fee2e2',
                  color: bubbeHealth.api?.status === 'healthy' ? '#16a34a' : '#dc2626'
                }}>
                  {bubbeHealth.api?.status || 'Unknown'}
                </span>
              </div>

              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                <span>Response Time:</span>
                <span style={{ fontWeight: '600' }}>
                  {bubbeHealth.api?.responseTime || 0}ms
                </span>
              </div>

              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                <span>PM2 Status:</span>
                <span style={{
                  padding: '4px 8px',
                  borderRadius: '4px',
                  fontSize: '12px',
                  fontWeight: '600',
                  background: bubbeHealth.pm2?.status === 'online' ? '#dcfce7' : '#fee2e2',
                  color: bubbeHealth.pm2?.status === 'online' ? '#16a34a' : '#dc2626'
                }}>
                  {bubbeHealth.pm2?.status || 'Unknown'}
                </span>
              </div>

              {bubbeHealth.api?.data && (
                <div style={{ 
                  marginTop: '16px',
                  padding: '12px',
                  background: '#f8fafc',
                  borderRadius: '6px',
                  fontSize: '12px'
                }}>
                  <strong>API Info:</strong><br />
                  Uptime: {bubbeHealth.api.data.uptime ? (bubbeHealth.api.data.uptime / 3600).toFixed(1) + 'h' : 'Unknown'}<br />
                  Memory: {bubbeHealth.api.data.memory ? Math.round(bubbeHealth.api.data.memory.rss / 1024 / 1024) + 'MB' : 'Unknown'}
                </div>
              )}
            </div>
          </div>
        </div>

        {/* Live Activity */}
        <div className="card">
          <div className="card-header">
            <h3 className="card-title">Live Activity</h3>
          </div>
          <div className="card-content">
            <div style={{ display: 'grid', gap: '12px' }}>
              <div style={{ display: 'flex', justifyContent: 'space-between' }}>
                <span>New Users Today:</span>
                <strong style={{ color: '#10b981' }}>{liveData.userActivity?.newUsersToday || 0}</strong>
              </div>
              
              <div style={{ display: 'flex', justifyContent: 'space-between' }}>
                <span>Blocked Users:</span>
                <strong style={{ color: '#ef4444' }}>{liveData.userActivity?.blockedUsers || 0}</strong>
              </div>

              <div style={{ display: 'flex', justifyContent: 'space-between' }}>
                <span>Total Users:</span>
                <strong>{liveData.userActivity?.totalUsers || 0}</strong>
              </div>

              <hr style={{ margin: '12px 0', border: 'none', borderTop: '1px solid #e2e8f0' }} />

              <div style={{ display: 'flex', justifyContent: 'space-between' }}>
                <span>Conversations Today:</span>
                <strong style={{ color: '#3b82f6' }}>{liveData.conversations?.today || 0}</strong>
              </div>

              <div style={{ display: 'flex', justifyContent: 'space-between' }}>
                <span>Last Hour:</span>
                <strong>{liveData.conversations?.lastHour || 0}</strong>
              </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' }}>
              <button 
                onClick={() => window.open('/alerts', '_blank')}
                className="btn btn-danger btn-small"
              >
                🚨 View Critical Alerts
              </button>
              
              <button 
                onClick={() => window.open('/users?blocked=true', '_blank')}
                className="btn btn-secondary btn-small"
              >
                🚫 View Blocked Users
              </button>
              
              <button 
                onClick={() => {
                  fetchLiveData()
                  checkBubbeHealth()
                }}
                className="btn btn-primary btn-small"
              >
                🔄 Force Refresh
              </button>
              
              <button 
                onClick={() => window.open('http://45.61.58.125:3011', '_blank')}
                className="btn btn-secondary btn-small"
              >
                🌐 Open Dear Bubbe
              </button>
            </div>
          </div>
        </div>
      </div>

      {/* Status Footer */}
      <div style={{
        marginTop: '24px',
        padding: '16px',
        background: '#f8fafc',
        borderRadius: '8px',
        border: '1px solid #e2e8f0',
        textAlign: 'center',
        fontSize: '14px',
        color: '#64748b'
      }}>
        {autoRefresh && (
          <div>
            🔄 Auto-refreshing every 10 seconds • 
            Last update: {liveData.timestamp ? new Date(liveData.timestamp).toLocaleTimeString() : 'Unknown'}
          </div>
        )}
        
        <div style={{ marginTop: '8px' }}>
          Monitoring {liveData.userActivity?.activeUsers || 0} active users • 
          System uptime: {liveData.system?.uptime ? Math.floor(liveData.system.uptime / 3600) + 'h' : 'Unknown'}
        </div>
      </div>
    </div>
  )
}

export default MonitoringPage