← back to Dear Bubbe Admin

frontend/src/components/ReportsPage.js

406 lines

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

const ReportsPage = () => {
  const [reports, setReports] = useState([])
  const [loading, setLoading] = useState(false)
  const [selectedReport, setSelectedReport] = useState(null)
  const [reportData, setReportData] = useState(null)

  const generateReport = async (type, timeframe = '24h') => {
    try {
      setLoading(true)
      let endpoint = `/api/reports/${type}`
      if (timeframe) endpoint += `?timeframe=${timeframe}`
      
      const response = await axios.get(endpoint)
      
      if (response.data.success) {
        setReportData(response.data.report)
        setSelectedReport(type)
      }
    } catch (error) {
      console.error(`Failed to generate ${type} report:`, error)
      alert(`Failed to generate ${type} report`)
    } finally {
      setLoading(false)
    }
  }

  const exportReport = async () => {
    try {
      await axios.post('/api/reports/export', {
        reportType: selectedReport,
        data: reportData
      })
      alert('Report exported successfully')
    } catch (error) {
      console.error('Failed to export report:', error)
      alert('Failed to export report')
    }
  }

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

  return (
    <div>
      <div style={{
        display: 'flex',
        justifyContent: 'space-between',
        alignItems: 'center',
        marginBottom: '24px'
      }}>
        <h2>Reports & Analytics</h2>
        {selectedReport && reportData && (
          <button onClick={exportReport} className="btn btn-primary">
            Export Report
          </button>
        )}
      </div>

      {!selectedReport ? (
        <div className="dashboard-grid">
          <div className="card">
            <div className="card-header">
              <h3 className="card-title">Daily Report</h3>
            </div>
            <div className="card-content">
              <p>Comprehensive daily activity summary including user engagement, conversations, and security events.</p>
              <div style={{ marginTop: '16px' }}>
                <button 
                  onClick={() => generateReport('daily')}
                  className="btn btn-primary"
                  style={{ marginRight: '8px' }}
                >
                  Generate Today
                </button>
                <button 
                  onClick={() => {
                    const date = prompt('Enter date (YYYY-MM-DD):')
                    if (date) generateReport('daily', date)
                  }}
                  className="btn btn-secondary"
                >
                  Custom Date
                </button>
              </div>
            </div>
          </div>

          <div className="card">
            <div className="card-header">
              <h3 className="card-title">Security Report</h3>
            </div>
            <div className="card-content">
              <p>Security incidents, threat analysis, and user behavior monitoring.</p>
              <div style={{ marginTop: '16px', display: 'grid', gap: '8px' }}>
                <button 
                  onClick={() => generateReport('security', '24h')}
                  className="btn btn-danger"
                >
                  Last 24 Hours
                </button>
                <button 
                  onClick={() => generateReport('security', '7d')}
                  className="btn btn-danger"
                >
                  Last 7 Days
                </button>
                <button 
                  onClick={() => generateReport('security', '30d')}
                  className="btn btn-danger"
                >
                  Last 30 Days
                </button>
              </div>
            </div>
          </div>

          <div className="card">
            <div className="card-header">
              <h3 className="card-title">Weekly Summary</h3>
            </div>
            <div className="card-content">
              <p>Weekly trends, user growth, and engagement patterns.</p>
              <div style={{ marginTop: '16px' }}>
                <button 
                  onClick={() => generateReport('weekly')}
                  className="btn btn-primary"
                >
                  This Week
                </button>
              </div>
            </div>
          </div>
        </div>
      ) : (
        <div>
          <div style={{ marginBottom: '20px' }}>
            <button 
              onClick={() => { setSelectedReport(null); setReportData(null); }}
              className="btn btn-secondary"
            >
              ← Back to Reports
            </button>
          </div>

          {reportData && (
            <div className="card">
              <div className="card-header">
                <h3 className="card-title">
                  {selectedReport.charAt(0).toUpperCase() + selectedReport.slice(1)} Report
                </h3>
                <div style={{ fontSize: '14px', color: '#64748b' }}>
                  Generated: {new Date(reportData.generatedAt).toLocaleString()}
                </div>
              </div>
              <div className="card-content">
                {selectedReport === 'daily' && (
                  <DailyReportView data={reportData} />
                )}
                {selectedReport === 'security' && (
                  <SecurityReportView data={reportData} />
                )}
                {selectedReport === 'weekly' && (
                  <WeeklyReportView data={reportData} />
                )}
              </div>
            </div>
          )}
        </div>
      )}
    </div>
  )
}

const DailyReportView = ({ data }) => (
  <div>
    <div className="stat-cards" style={{ marginBottom: '24px' }}>
      <div className="stat-card">
        <div className="stat-label">Total Interactions</div>
        <div className="stat-value-large">{data.summary?.totalInteractions || 0}</div>
      </div>
      <div className="stat-card">
        <div className="stat-label">New Users</div>
        <div className="stat-value-large">{data.userActivity?.newRegistrations || 0}</div>
      </div>
      <div className="stat-card critical">
        <div className="stat-label">Security Incidents</div>
        <div className="stat-value-large">{data.summary?.securityIncidents || 0}</div>
      </div>
      <div className="stat-card">
        <div className="stat-label">Engagement Rate</div>
        <div className="stat-value-large">{(data.summary?.engagement || 0).toFixed(1)}</div>
      </div>
    </div>

    <div className="dashboard-grid">
      <div className="card">
        <div className="card-header">
          <h4>User Activity</h4>
        </div>
        <div className="card-content">
          <div style={{ display: 'grid', gap: '8px' }}>
            <div style={{ display: 'flex', justifyContent: 'space-between' }}>
              <span>Active Users:</span>
              <strong>{data.userActivity?.activeUsers || 0}</strong>
            </div>
            <div style={{ display: 'flex', justifyContent: 'space-between' }}>
              <span>Total Sessions:</span>
              <strong>{data.userActivity?.totalSessions || 0}</strong>
            </div>
            <div style={{ display: 'flex', justifyContent: 'space-between' }}>
              <span>Unique Users:</span>
              <strong>{data.userActivity?.uniqueActiveUsers || 0}</strong>
            </div>
          </div>
        </div>
      </div>

      <div className="card">
        <div className="card-header">
          <h4>Conversations</h4>
        </div>
        <div className="card-content">
          <div style={{ display: 'grid', gap: '8px' }}>
            <div style={{ display: 'flex', justifyContent: 'space-between' }}>
              <span>Total:</span>
              <strong>{data.conversations?.totalConversations || 0}</strong>
            </div>
            <div style={{ display: 'flex', justifyContent: 'space-between' }}>
              <span>Voice Used:</span>
              <strong>{data.conversations?.voiceUsage || 0}</strong>
            </div>
            <div style={{ display: 'flex', justifyContent: 'space-between' }}>
              <span>Avg Response Time:</span>
              <strong>{(data.conversations?.avgResponseTime || 0).toFixed(0)}ms</strong>
            </div>
          </div>
        </div>
      </div>
    </div>

    {data.topTopics && data.topTopics.length > 0 && (
      <div className="card" style={{ marginTop: '24px' }}>
        <div className="card-header">
          <h4>Top Discussion Topics</h4>
        </div>
        <div className="card-content">
          <div style={{ display: 'grid', gap: '8px' }}>
            {data.topTopics.map((topic, index) => (
              <div key={index} style={{ display: 'flex', justifyContent: 'space-between' }}>
                <span>{topic._id}</span>
                <span className="badge badge-medium">{topic.count}</span>
              </div>
            ))}
          </div>
        </div>
      </div>
    )}
  </div>
)

const SecurityReportView = ({ data }) => (
  <div>
    <div className="stat-cards" style={{ marginBottom: '24px' }}>
      <div className="stat-card critical">
        <div className="stat-label">Total Alerts</div>
        <div className="stat-value-large">{data.summary?.totalAlerts || 0}</div>
      </div>
      <div className="stat-card critical">
        <div className="stat-label">Critical Incidents</div>
        <div className="stat-value-large">{data.summary?.criticalIncidents || 0}</div>
      </div>
      <div className="stat-card warning">
        <div className="stat-label">Repeat Offenders</div>
        <div className="stat-value-large">{data.summary?.repeatOffenderCount || 0}</div>
      </div>
      <div className="stat-card">
        <div className="stat-label">Users Blocked</div>
        <div className="stat-value-large">{data.summary?.blockedUserCount || 0}</div>
      </div>
    </div>

    {data.repeatOffenders && data.repeatOffenders.length > 0 && (
      <div className="card" style={{ marginBottom: '24px' }}>
        <div className="card-header">
          <h4>Repeat Offenders</h4>
        </div>
        <div className="card-content">
          <div className="table-container">
            <table className="table">
              <thead>
                <tr>
                  <th>IP Address</th>
                  <th>Alert Count</th>
                  <th>Severities</th>
                  <th>Types</th>
                </tr>
              </thead>
              <tbody>
                {data.repeatOffenders.slice(0, 10).map((offender, index) => (
                  <tr key={index}>
                    <td style={{ fontFamily: 'monospace' }}>{offender._id}</td>
                    <td><span className="badge badge-critical">{offender.alertCount}</span></td>
                    <td style={{ fontSize: '12px' }}>
                      {offender.severities.join(', ')}
                    </td>
                    <td style={{ fontSize: '12px' }}>
                      {offender.types.join(', ')}
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </div>
      </div>
    )}

    {data.geoThreats && data.geoThreats.length > 0 && (
      <div className="card">
        <div className="card-header">
          <h4>Geographic Threat Distribution</h4>
        </div>
        <div className="card-content">
          <div style={{ display: 'grid', gap: '8px' }}>
            {data.geoThreats.slice(0, 10).map((geo, index) => (
              <div key={index} style={{ display: 'flex', justifyContent: 'space-between' }}>
                <span>{geo._id || 'Unknown'}</span>
                <span className="badge badge-high">{geo.alerts} alerts</span>
              </div>
            ))}
          </div>
        </div>
      </div>
    )}
  </div>
)

const WeeklyReportView = ({ data }) => (
  <div>
    <div className="stat-cards" style={{ marginBottom: '24px' }}>
      <div className="stat-card">
        <div className="stat-label">Total Conversations</div>
        <div className="stat-value-large">{data.weekTotals?.conversations || 0}</div>
      </div>
      <div className="stat-card">
        <div className="stat-label">New Users</div>
        <div className="stat-value-large">{data.weekTotals?.newUsers || 0}</div>
      </div>
      <div className="stat-card warning">
        <div className="stat-label">Total Alerts</div>
        <div className="stat-value-large">{data.weekTotals?.alerts || 0}</div>
      </div>
      <div className="stat-card">
        <div className="stat-label">Sessions</div>
        <div className="stat-value-large">{data.weekTotals?.sessions || 0}</div>
      </div>
    </div>

    <div className="card">
      <div className="card-header">
        <h4>Daily Breakdown</h4>
      </div>
      <div className="card-content">
        <div className="table-container">
          <table className="table">
            <thead>
              <tr>
                <th>Date</th>
                <th>Conversations</th>
                <th>Sessions</th>
                <th>Alerts</th>
              </tr>
            </thead>
            <tbody>
              {data.dailyBreakdown?.map((day, index) => (
                <tr key={index}>
                  <td>{new Date(day.date).toLocaleDateString()}</td>
                  <td>{day.conversations}</td>
                  <td>{day.sessions}</td>
                  <td>
                    {day.alerts > 0 ? (
                      <span className="badge badge-warning">{day.alerts}</span>
                    ) : (
                      day.alerts
                    )}
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </div>
    </div>
  </div>
)

export default ReportsPage