import React, { useState, useEffect } from 'react'; import { Server, Database, Users, Activity, RefreshCw, Plus, Edit3, Trash2, Eye, AlertCircle, CheckCircle, XCircle } from 'lucide-react'; import { useEffect } from 'react'; function App() { useEffect(() => { const ws = new WebSocket('ws://localhost:8000/ws/logs'); ws.onmessage = (event) => { const data = JSON.parse(event.data); if (data.type === 'log') { console.log(`[Backend Log]: ${data.message}`); } }; ws.onclose = () => { console.log('WebSocket connection closed'); }; return () => { ws.close(); }; }, []); return
Your React App
; } const Dashboard = () => { const [apiUrl, setApiUrl] = useState('http://127.0.0.1:8000'); const [connectionStatus, setConnectionStatus] = useState('disconnected'); const [isLoading, setIsLoading] = useState(false); const [activeTab, setActiveTab] = useState('dashboard'); const [members, setMembers] = useState([]); const [totalMembers, setTotalMembers] = useState(0); const [activeMembersCount, setActiveMembersCount] = useState(0); const [inactiveMembersCount, setInactiveMembersCount] = useState(0); const [deactivatedMembersCount, setDeactivatedMembersCount] = useState(0); const [loadingData, setLoadingData] = useState(false); const testConnection = async () => { setIsLoading(true); try { console.log('Testing connection to:', apiUrl); // Test with a simple endpoint first - try /member/1 since /member/100 works const response = await fetch(`${apiUrl}/member/100`); if (response.ok) { setConnectionStatus('connected'); console.log('Connection successful!'); // Load members data when connection is successful await loadMembersCount(); } else { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } } catch (error) { console.error('Connection error:', error); setConnectionStatus('error'); } setIsLoading(false); }; const loadMembersCount = async () => { if (connectionStatus !== 'connected' && !isLoading) return; setLoadingData(true); console.log('Attempting to load active members count...'); // const fetchActiveMembersCount = async () => { try { const response = await fetch(`${apiUrl}/activememberscount`); if (response.ok) { const count = await response.json(); // Single number return setTotalMembers(count); console.log('Total active members count:', count); } else { console.log('Failed to fetch active members count:', response.statusText); setTotalMembers(0); } } catch (error) { console.log('Error fetching active members count:', error); setMembers([]); setTotalMembers(0); } //} setLoadingData(false); }; // Fetch member counts const fetchMemberCounts = async () => { if (connectionStatus !== 'connected' && !isLoading) return; try { // Fetch active members count const activeResponse = await fetch(`${apiUrl}/activememberscount`, { headers: { 'Content-Type': 'application/json' } }); if (activeResponse.ok) { const activeCount = await activeResponse.json(); setActiveMembersCount(activeCount); console.log('activememberscount: ', activeCount); } // Fetch inactive members count const inactiveResponse = await fetch(`${apiUrl}/inactivememberscount`, { headers: { 'Content-Type': 'application/json' } }); if (inactiveResponse.ok) { const inactiveCount = await inactiveResponse.json(); setInactiveMembersCount(inactiveCount); } // Fetch deactivated members count const deactivatedResponse = await fetch(`${apiUrl}/deactivatedmemberscount`, { headers: { 'Content-Type': 'application/json' } }); if (deactivatedResponse.ok) { const deactivatedCount = await deactivatedResponse.json(); setDeactivatedMembersCount(deactivatedCount); } } catch (err) { console.error('Error fetching member counts:', err); } }; // Fetch data from API const fetchData = async (endpoint = 'items') => { if (connectionStatus !== 'connected' && !isLoading) return; setLoadingData(true); try { const response = await fetch(`${apiUrl}/${endpoint}`, { headers: { 'Content-Type': 'application/json', }, }); if (response.ok) { const result = await response.json(); setData(Array.isArray(result) ? result : [result]); } else { throw new Error(`Failed to fetch data: ${response.statusText}`); } } catch (err) { setError(err.message); } finally { setLoadingData(false); } }; const loadMembers = async () => { if (connectionStatus !== 'connected' && !isLoading) return; setLoadingData(true); try { // Since /members/ gives 500 error, let's try different approaches console.log('Attempting to load active members count...'); // Option 1: Try individual member requests (if you know IDs) // For now, let's try to get a few members by ID const memberIds = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; // Adjust these IDs as needed const memberPromises = memberIds.map(async (id) => { try { const response = await fetch(`${apiUrl}/member/${id}`); if (response.ok) { return await response.json(); } return null; } catch (error) { console.log(`Member ${id} not found or error:`, error); return null; } }); const results = await Promise.all(memberPromises); const validMembers = results.filter(member => member !== null); console.log('Found members:', validMembers); setMembers(validMembers); setTotalMembers(validMembers.length); if (validMembers.length === 0) { console.warn('No members found with IDs 1-10. You may need to adjust the ID range or fix the /members/ endpoint.'); } } catch (error) { console.error('Error loading members:', error); setMembers([]); setTotalMembers(0); } setLoadingData(false); }; useEffect(() => { if (connectionStatus === 'connected') { // loadMembersCount(); fetchMemberCounts(); if (activeTab === 'data') { fetchData(); } } }, [connectionStatus]); const getStatusColor = () => { switch (connectionStatus) { case 'connected': return '#10b981'; case 'error': return '#ef4444'; default: return '#f59e0b'; } }; const getStatusIcon = () => { switch (connectionStatus) { case 'connected': return ; case 'error': return ; default: return ; } }; const getStatusText = () => { switch (connectionStatus) { case 'connected': return 'Connected'; case 'error': return 'Connection Error'; default: return 'Disconnected'; } }; const getFullName = (member) => { const firstName = member.member_firstname || ''; const lastName = member.member_surname || ''; const memberSuffix = member.member_suffix || ''; const fullName = `${firstName} ${lastName} ${memberSuffix}`.trim(); return fullName || 'N/A'; }; const MEMBER_STATE_COLORS = { 'A': '#10B981', // Green - Active 'I': '#F59E0B', // Amber/Orange - Inactive 'D': '#EF4444' // Red - Deactivated }; const getMemberStateColor = (stateCode) => { return MEMBER_STATE_COLORS[stateCode] || '#6B7280'; // Gray as fallback }; const styles = { container: { minHeight: '100vh', backgroundColor: '#f9fafb', fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif' }, header: { backgroundColor: 'white', boxShadow: '0 1px 3px 0 rgba(0, 0, 0, 0.1)', borderBottom: '1px solid #e5e7eb' }, headerContent: { maxWidth: '1280px', margin: '0 auto', padding: '0 24px', display: 'flex', justifyContent: 'space-between', alignItems: 'center', paddingTop: '24px', paddingBottom: '24px' }, title: { fontSize: '30px', fontWeight: 'bold', color: '#111827', margin: 0 }, subtitle: { color: '#6b7280', marginTop: '4px', fontSize: '16px' }, statusContainer: { display: 'flex', alignItems: 'center', gap: '8px', color: getStatusColor(), fontWeight: '500' }, mainContent: { maxWidth: '1280px', margin: '0 auto', padding: '32px 24px' }, card: { backgroundColor: 'white', borderRadius: '8px', boxShadow: '0 1px 3px 0 rgba(0, 0, 0, 0.1)', border: '1px solid #e5e7eb', padding: '24px', marginBottom: '32px' }, cardTitle: { fontSize: '18px', fontWeight: '600', color: '#111827', marginBottom: '16px' }, connectionForm: { display: 'flex', alignItems: 'center', gap: '16px' }, input: { flex: 1, padding: '8px 12px', border: '1px solid #d1d5db', borderRadius: '6px', fontSize: '14px', outline: 'none' }, button: { display: 'inline-flex', alignItems: 'center', gap: '8px', padding: '8px 16px', backgroundColor: '#2563eb', color: 'white', border: 'none', borderRadius: '6px', fontSize: '14px', fontWeight: '500', cursor: 'pointer', transition: 'background-color 0.2s' }, buttonDisabled: { opacity: 0.5, cursor: 'not-allowed' }, errorAlert: { marginTop: '16px', padding: '16px', backgroundColor: '#fef2f2', border: '1px solid #fecaca', borderRadius: '6px', display: 'flex', alignItems: 'center', gap: '8px' }, errorText: { color: '#991b1b' }, tabs: { borderBottom: '1px solid #e5e7eb', marginBottom: '32px' }, tabsNav: { display: 'flex', gap: '32px' }, tab: { padding: '8px 4px', fontSize: '14px', fontWeight: '500', border: 'none', background: 'none', cursor: 'pointer', borderBottom: '2px solid transparent', transition: 'all 0.2s' }, tabActive: { borderBottom: '2px solid #2563eb', color: '#2563eb' }, tabInactive: { color: '#6b7280' }, statsGrid: { display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(250px, 1fr))', gap: '24px', marginBottom: '32px' }, statCard: { backgroundColor: 'white', borderRadius: '8px', boxShadow: '0 1px 3px 0 rgba(0, 0, 0, 0.1)', border: '1px solid #e5e7eb', padding: '24px', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }, statContent: { flex: 1 }, statLabel: { fontSize: '14px', fontWeight: '500', color: '#6b7280' }, statValue: { fontSize: '24px', fontWeight: 'bold', color: '#111827', marginTop: '8px' }, statIcon: { padding: '12px', borderRadius: '50%' }, iconOrange: { backgroundColor: '#fed7aa', color: '#ea580c' }, iconBlue: { backgroundColor: '#dbeafe', color: '#2563eb' }, iconGreen: { backgroundColor: '#dcfce7', color: '#16a34a' }, iconPurple: { backgroundColor: '#e9d5ff', color: '#9333ea' }, actionButtons: { display: 'flex', flexWrap: 'wrap', gap: '16px' }, actionButton: { display: 'inline-flex', alignItems: 'center', gap: '8px', padding: '8px 16px', border: 'none', borderRadius: '6px', fontSize: '14px', fontWeight: '500', cursor: 'pointer', transition: 'background-color 0.2s' }, actionButtonGray: { backgroundColor: '#f3f4f6', color: '#374151' }, actionButtonGreen: { backgroundColor: '#dcfce7', color: '#166534' }, actionButtonBlue: { backgroundColor: '#dbeafe', color: '#1e40af' }, emptyState: { textAlign: 'center', padding: '32px' }, emptyStateIcon: { margin: '0 auto 16px', color: '#9ca3af' }, emptyStateText: { color: '#6b7280' }, cardHeader: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '20px' }, refreshButton: { display: 'flex', alignItems: 'center', padding: '8px 16px', backgroundColor: '#3B82F6', color: 'white', border: 'none', borderRadius: '6px', cursor: 'pointer', fontSize: '14px', fontWeight: '500' }, loadingState: { display: 'flex', flexDirection: 'column', alignItems: 'center', padding: '40px', color: '#6B7280' }, loadingIcon: { marginBottom: '10px', color: '#3B82F6' }, dataContainer: { marginTop: '20px' }, dataHeader: { marginBottom: '16px', padding: '12px', backgroundColor: '#F3F4F6', borderRadius: '6px' }, dataCount: { margin: 0, fontWeight: '600', color: '#374151' }, tableContainer: { overflowX: 'auto', border: '1px solid #E5E7EB', borderRadius: '8px' }, table: { width: '100%', borderCollapse: 'collapse' }, tableHeaderRow: { backgroundColor: '#F9FAFB' }, tableHeader: { padding: '12px', textAlign: 'left', fontWeight: '600', color: '#374151', borderBottom: '1px solid #E5E7EB' }, tableRow: { borderBottom: '1px solid #F3F4F6' }, tableCell: { padding: '12px', color: '#6B7280' }, statusBadge: { padding: '4px 8px', borderRadius: '12px', fontSize: '12px', fontWeight: '500' }, }; return (
{/* Header */}

FastAPI Dashboard

Manage your backend data and monitor API status

{getStatusIcon()} {getStatusText()}
{/* Main Content */}
{/* Connection Section */}

API Connection

setApiUrl(e.target.value)} style={styles.input} placeholder="Enter API URL" />
{connectionStatus === 'error' && (
NetworkError when attempting to fetch resource.
)}
{/* Navigation Tabs */}
{/* Stats Grid */}
{/* API Status */}

API Status

{connectionStatus === 'connected' ? 'Online' : 'Offline'}

{/* Active Users */}

Active Users

1

{/* Activity */}

Activity

Live

Active Members

{activeMembersCount}

Inactive Members

{inactiveMembersCount}

Deactivated Members

{deactivatedMembersCount}

{/* Quick Actions */}

Quick Actions

{/* Content based on active tab */} {activeTab === 'data' && (

Data Management

{isLoading ? (

Loading members...

) : members.length === 0 ? (

No members found. Click "Load Members" to fetch data.

) : (

Total Members: {members.length}

{members.map((member, index) => ( ))}
ID Name Email Active Actions
{member.id} {getFullName(member)} {member.member_email || 'N/A'} {member.current_state.state_name}
)}
)}
); }; export default Dashboard;