← back to Dear Bubbe Admin

frontend/src/components/UsersPage.js

323 lines

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

const UsersPage = () => {
  const [users, setUsers] = useState([])
  const [loading, setLoading] = useState(true)
  const [selectedUser, setSelectedUser] = useState(null)
  const [filters, setFilters] = useState({
    search: '',
    blocked: '',
    riskLevel: '',
    country: ''
  })

  useEffect(() => {
    fetchUsers()
  }, [filters])

  const fetchUsers = async () => {
    try {
      setLoading(true)
      const params = new URLSearchParams()
      Object.entries(filters).forEach(([key, value]) => {
        if (value) params.append(key, value)
      })

      const response = await axios.get(`/api/users?${params.toString()}`)
      setUsers(response.data.users || [])
    } catch (error) {
      console.error('Failed to fetch users:', error)
    } finally {
      setLoading(false)
    }
  }

  const fetchUserDetails = async (userId) => {
    try {
      const response = await axios.get(`/api/users/${userId}`)
      setSelectedUser(response.data)
    } catch (error) {
      console.error('Failed to fetch user details:', error)
    }
  }

  const blockUser = async (userId, reason) => {
    try {
      await axios.post(`/api/users/${userId}/block`, { reason })
      fetchUsers()
      setSelectedUser(null)
      alert('User blocked successfully')
    } catch (error) {
      console.error('Failed to block user:', error)
      alert('Failed to block user')
    }
  }

  const unblockUser = async (userId) => {
    try {
      await axios.post(`/api/users/${userId}/unblock`)
      fetchUsers()
      setSelectedUser(null)
      alert('User unblocked successfully')
    } catch (error) {
      console.error('Failed to unblock user:', error)
      alert('Failed to unblock user')
    }
  }

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

  return (
    <div>
      <div style={{
        display: 'flex',
        justifyContent: 'space-between',
        alignItems: 'center',
        marginBottom: '24px'
      }}>
        <h2>User Management</h2>
        <button onClick={fetchUsers} className="btn btn-primary">
          Refresh
        </button>
      </div>

      {/* Filters */}
      <div className="card" style={{ marginBottom: '24px' }}>
        <div className="card-content">
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))', gap: '16px' }}>
            <input
              type="text"
              placeholder="Search users..."
              className="form-input"
              value={filters.search}
              onChange={(e) => setFilters({...filters, search: e.target.value})}
            />
            <select
              className="form-select"
              value={filters.blocked}
              onChange={(e) => setFilters({...filters, blocked: e.target.value})}
            >
              <option value="">All Users</option>
              <option value="false">Active Users</option>
              <option value="true">Blocked Users</option>
            </select>
            <select
              className="form-select"
              value={filters.riskLevel}
              onChange={(e) => setFilters({...filters, riskLevel: e.target.value})}
            >
              <option value="">All Risk Levels</option>
              <option value="critical">Critical</option>
              <option value="high">High</option>
              <option value="medium">Medium</option>
              <option value="low">Low</option>
            </select>
          </div>
        </div>
      </div>

      {/* Users Table */}
      <div className="card">
        <div className="card-content">
          <div className="table-container">
            <table className="table">
              <thead>
                <tr>
                  <th>User</th>
                  <th>Email</th>
                  <th>Location</th>
                  <th>Messages</th>
                  <th>Risk Level</th>
                  <th>Status</th>
                  <th>Last Visit</th>
                  <th>Actions</th>
                </tr>
              </thead>
              <tbody>
                {users.map((user) => (
                  <tr key={user.userId}>
                    <td>
                      <div>
                        <strong>{user.profile?.name || user.profile?.preferredName || 'Unknown'}</strong>
                        <div style={{ fontSize: '12px', color: '#64748b' }}>
                          {user.userId}
                        </div>
                      </div>
                    </td>
                    <td>{user.email || 'No email'}</td>
                    <td>
                      {user.location?.city && user.location?.country ? 
                        `${user.location.city}, ${user.location.country}` : 
                        'Unknown'
                      }
                    </td>
                    <td>{user.stats?.totalMessages || 0}</td>
                    <td>
                      <span className={`badge badge-${user.security?.riskLevel || 'low'}`}>
                        {user.security?.riskLevel || 'low'}
                      </span>
                    </td>
                    <td>
                      <span className={`badge ${user.security?.blocked ? 'badge-blocked' : 'badge-active'}`}>
                        {user.security?.blocked ? 'Blocked' : 'Active'}
                      </span>
                    </td>
                    <td style={{ fontSize: '12px', color: '#64748b' }}>
                      {user.stats?.lastVisit ? 
                        new Date(user.stats.lastVisit).toLocaleString() : 
                        'Never'
                      }
                    </td>
                    <td>
                      <button
                        onClick={() => fetchUserDetails(user.userId)}
                        className="btn btn-small btn-primary"
                        style={{ marginRight: '8px' }}
                      >
                        View
                      </button>
                      {user.security?.blocked ? (
                        <button
                          onClick={() => unblockUser(user.userId)}
                          className="btn btn-small btn-secondary"
                        >
                          Unblock
                        </button>
                      ) : (
                        <button
                          onClick={() => {
                            const reason = prompt('Reason for blocking:')
                            if (reason) blockUser(user.userId, reason)
                          }}
                          className="btn btn-small btn-danger"
                        >
                          Block
                        </button>
                      )}
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </div>
      </div>

      {/* User Details Modal */}
      {selectedUser && (
        <div className="live-alert-modal" onClick={() => setSelectedUser(null)}>
          <div 
            className="live-alert-content" 
            style={{ maxWidth: '800px', maxHeight: '80vh', overflow: 'auto' }}
            onClick={e => e.stopPropagation()}
          >
            <div className="alert-header">
              <h3>User Details</h3>
              <button onClick={() => setSelectedUser(null)}>×</button>
            </div>
            
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '20px' }}>
              <div>
                <h4>Profile Information</h4>
                <div style={{ background: '#f8fafc', padding: '16px', borderRadius: '8px' }}>
                  <div><strong>Name:</strong> {selectedUser.user?.profile?.name || 'Unknown'}</div>
                  <div><strong>Email:</strong> {selectedUser.user?.email || 'No email'}</div>
                  <div><strong>Age:</strong> {selectedUser.user?.profile?.age || 'Unknown'}</div>
                  <div><strong>Location:</strong> {
                    selectedUser.user?.location?.city ? 
                    `${selectedUser.user.location.city}, ${selectedUser.user.location.country}` : 
                    'Unknown'
                  }</div>
                  <div><strong>Relationship:</strong> {selectedUser.user?.profile?.relationshipStatus || 'Unknown'}</div>
                  <div><strong>Job:</strong> {selectedUser.user?.profile?.jobTitle || 'Unknown'}</div>
                </div>
              </div>
              
              <div>
                <h4>Security & Activity</h4>
                <div style={{ background: '#f8fafc', padding: '16px', borderRadius: '8px' }}>
                  <div><strong>Risk Level:</strong> 
                    <span className={`badge badge-${selectedUser.user?.security?.riskLevel || 'low'}`} style={{ marginLeft: '8px' }}>
                      {selectedUser.user?.security?.riskLevel || 'low'}
                    </span>
                  </div>
                  <div><strong>Status:</strong> 
                    <span className={`badge ${selectedUser.user?.security?.blocked ? 'badge-blocked' : 'badge-active'}`} style={{ marginLeft: '8px' }}>
                      {selectedUser.user?.security?.blocked ? 'Blocked' : 'Active'}
                    </span>
                  </div>
                  <div><strong>Total Messages:</strong> {selectedUser.user?.stats?.totalMessages || 0}</div>
                  <div><strong>Total Sessions:</strong> {selectedUser.sessions?.length || 0}</div>
                  <div><strong>Alerts:</strong> {selectedUser.alerts?.length || 0}</div>
                </div>
              </div>
            </div>

            {selectedUser.alerts && selectedUser.alerts.length > 0 && (
              <div style={{ marginTop: '20px' }}>
                <h4>Recent Alerts</h4>
                <div style={{ maxHeight: '200px', overflow: 'auto' }}>
                  {selectedUser.alerts.slice(0, 5).map((alert, index) => (
                    <div key={index} style={{
                      padding: '12px',
                      margin: '8px 0',
                      background: '#fef2f2',
                      border: '1px solid #fecaca',
                      borderRadius: '6px'
                    }}>
                      <div style={{ display: 'flex', justifyContent: 'space-between' }}>
                        <span className={`badge badge-${alert.severity}`}>{alert.type}</span>
                        <span style={{ fontSize: '12px', color: '#64748b' }}>
                          {new Date(alert.timestamp).toLocaleString()}
                        </span>
                      </div>
                      {alert.violation?.match && (
                        <div style={{ marginTop: '8px', fontSize: '14px', fontFamily: 'monospace' }}>
                          "{alert.violation.match}"
                        </div>
                      )}
                    </div>
                  ))}
                </div>
              </div>
            )}

            <div style={{ marginTop: '20px', display: 'flex', gap: '12px', justifyContent: 'flex-end' }}>
              <button onClick={() => setSelectedUser(null)} className="btn btn-secondary">
                Close
              </button>
              {selectedUser.user?.security?.blocked ? (
                <button
                  onClick={() => unblockUser(selectedUser.user.userId)}
                  className="btn btn-primary"
                >
                  Unblock User
                </button>
              ) : (
                <button
                  onClick={() => {
                    const reason = prompt('Reason for blocking:')
                    if (reason) blockUser(selectedUser.user.userId, reason)
                  }}
                  className="btn btn-danger"
                >
                  Block User
                </button>
              )}
            </div>
          </div>
        </div>
      )}
    </div>
  )
}

export default UsersPage