← back to Dear Bubbe Admin

frontend/src/App.js

261 lines

import React, { useState, useEffect } from 'react'
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom'
import axios from 'axios'
import io from 'socket.io-client'

// Components
import Login from './components/Login'
import Dashboard from './components/Dashboard'
import AlertsPage from './components/AlertsPage'
import UsersPage from './components/UsersPage'
import ReportsPage from './components/ReportsPage'
import MonitoringPage from './components/MonitoringPage'
import Sidebar from './components/Sidebar'
import Header from './components/Header'
import LiveAlertModal from './components/LiveAlertModal'

// Styles
import './App.css'

// API configuration
const API_BASE_URL = process.env.REACT_APP_API_URL || 'http://45.61.58.125:3015'
const SOCKET_URL = process.env.REACT_APP_SOCKET_URL || 'http://45.61.58.125:3015'

class App extends React.Component {
  constructor(props) {
    super(props)
    
    this.state = {
      isAuthenticated: false,
      user: null,
      loading: true,
      socket: null,
      liveAlert: null,
      stats: {},
      error: null
    }
    
    // Set up axios defaults
    axios.defaults.baseURL = API_BASE_URL
    
    // Add token to requests if available
    const token = localStorage.getItem('adminToken')
    if (token) {
      axios.defaults.headers.common['Authorization'] = `Bearer ${token}`
    }
  }
  
  async componentDidMount() {
    await this.checkAuthStatus()
  }
  
  componentWillUnmount() {
    if (this.state.socket) {
      this.state.socket.disconnect()
    }
  }
  
  checkAuthStatus = async () => {
    try {
      const token = localStorage.getItem('adminToken')
      
      if (!token) {
        this.setState({ loading: false, isAuthenticated: false })
        return
      }
      
      const response = await axios.get('/api/auth/me')
      
      if (response.data.success) {
        this.setState({
          isAuthenticated: true,
          user: response.data.user,
          loading: false
        })
        
        this.connectWebSocket(token)
      } else {
        localStorage.removeItem('adminToken')
        this.setState({ loading: false, isAuthenticated: false })
      }
    } catch (error) {
      console.error('Auth check failed:', error)
      localStorage.removeItem('adminToken')
      this.setState({ 
        loading: false, 
        isAuthenticated: false,
        error: 'Authentication failed'
      })
    }
  }
  
  connectWebSocket = (token) => {
    try {
      const socket = io(SOCKET_URL, {
        auth: { token },
        transports: ['websocket', 'polling']
      })
      
      socket.on('connect', () => {
        console.log('Connected to admin socket')
        socket.emit('authenticate', token)
      })
      
      socket.on('auth_error', (error) => {
        console.error('Socket auth error:', error)
        this.handleLogout()
      })
      
      socket.on('stats', (stats) => {
        this.setState({ stats })
      })
      
      socket.on('new_alert', (alert) => {
        console.log('🚨 New alert received:', alert)
        this.setState({ liveAlert: alert })
        
        // Play alert sound if critical
        if (alert.severity === 'critical' || alert.severity === 'high') {
          this.playAlertSound()
        }
      })
      
      socket.on('system_alert', (alert) => {
        console.log('⚠️ System alert:', alert)
      })
      
      socket.on('threat_response', (data) => {
        console.log('🚨 Threat response sent:', data)
      })
      
      socket.on('disconnect', () => {
        console.log('Disconnected from admin socket')
      })
      
      this.setState({ socket })
      
    } catch (error) {
      console.error('Failed to connect WebSocket:', error)
    }
  }
  
  playAlertSound = () => {
    // Create a simple alert tone
    try {
      const audioContext = new (window.AudioContext || window.webkitAudioContext)()
      const oscillator = audioContext.createOscillator()
      const gainNode = audioContext.createGain()
      
      oscillator.connect(gainNode)
      gainNode.connect(audioContext.destination)
      
      oscillator.frequency.value = 800
      oscillator.type = 'square'
      
      gainNode.gain.setValueAtTime(0.3, audioContext.currentTime)
      gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.5)
      
      oscillator.start(audioContext.currentTime)
      oscillator.stop(audioContext.currentTime + 0.5)
    } catch (error) {
      console.log('Could not play alert sound:', error)
    }
  }
  
  handleLogin = async (credentials) => {
    try {
      const response = await axios.post('/api/auth/login', credentials)
      
      if (response.data.success) {
        const { token, user } = response.data
        
        localStorage.setItem('adminToken', token)
        axios.defaults.headers.common['Authorization'] = `Bearer ${token}`
        
        this.setState({
          isAuthenticated: true,
          user,
          error: null
        })
        
        this.connectWebSocket(token)
        
        return { success: true }
      } else {
        return { success: false, error: response.data.error }
      }
    } catch (error) {
      const errorMessage = error.response?.data?.error || 'Login failed'
      return { success: false, error: errorMessage }
    }
  }
  
  handleLogout = () => {
    localStorage.removeItem('adminToken')
    delete axios.defaults.headers.common['Authorization']
    
    if (this.state.socket) {
      this.state.socket.disconnect()
    }
    
    this.setState({
      isAuthenticated: false,
      user: null,
      socket: null,
      stats: {},
      error: null
    })
  }
  
  closeLiveAlert = () => {
    this.setState({ liveAlert: null })
  }
  
  render() {
    const { isAuthenticated, user, loading, liveAlert, stats, error } = this.state
    
    if (loading) {
      return (
        <div className="loading-container">
          <div className="loading-spinner"></div>
          <p>Loading Dear Bubbe Admin Dashboard...</p>
        </div>
      )
    }
    
    if (!isAuthenticated) {
      return <Login onLogin={this.handleLogin} error={error} />
    }
    
    return (
      <Router>
        <div className="app">
          <Sidebar />
          <div className="main-content">
            <Header user={user} stats={stats} onLogout={this.handleLogout} />
            <div className="content">
              <Routes>
                <Route path="/" element={<Dashboard stats={stats} />} />
                <Route path="/dashboard" element={<Dashboard stats={stats} />} />
                <Route path="/alerts" element={<AlertsPage />} />
                <Route path="/users" element={<UsersPage />} />
                <Route path="/monitoring" element={<MonitoringPage />} />
                <Route path="/reports" element={<ReportsPage />} />
                <Route path="*" element={<Navigate to="/dashboard" replace />} />
              </Routes>
            </div>
          </div>
          
          {liveAlert && (
            <LiveAlertModal 
              alert={liveAlert} 
              onClose={this.closeLiveAlert} 
            />
          )}
        </div>
      </Router>
    )
  }
}

export default App