Initial commit

This commit is contained in:
Grae Jones
2026-02-03 15:45:39 -08:00
commit 3647b304a3
74 changed files with 27121 additions and 0 deletions

View File

@@ -0,0 +1,135 @@
import React, { useState, useEffect, useCallback } from 'react';
import { useAuth } from '../auth/AuthProvider';
import { API_BASE_URL } from '../auth/authConfig';
import ClientsPanel from './admin/ClientsPanel';
import UsersPanel from './admin/UsersPanel';
import SessionsPanel from './admin/SessionsPanel';
export default function Dashboard() {
const { session } = useAuth();
const [activeTab, setActiveTab] = useState('overview');
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [refreshKey, setRefreshKey] = useState(0);
const fetchData = useCallback(async (endpoint) => {
if (!session?.sessionToken) return null;
setLoading(true);
setError(null);
try {
const response = await fetch(`${API_BASE_URL}${endpoint}`, {
headers: { 'X-Session-Token': session.sessionToken }
});
const result = await response.json();
if (!result.ok) throw new Error(result.error || 'Request failed');
return result;
} catch (err) {
setError(err.message);
return null;
} finally {
setLoading(false);
}
}, [session]);
const refresh = () => setRefreshKey(k => k + 1);
useEffect(() => {
const loadData = async () => {
let result = null;
switch (activeTab) {
case 'overview':
result = await fetchData('/api/monitoring/health');
break;
case 'clients':
result = await fetchData('/api/admin/clients');
break;
case 'users':
result = await fetchData('/api/admin/users');
break;
case 'sessions':
result = await fetchData('/api/admin/sessions');
break;
}
setData(result);
};
loadData();
}, [activeTab, fetchData, refreshKey]);
const tabs = [
{ id: 'overview', label: 'Overview' },
{ id: 'clients', label: 'Clients' },
{ id: 'users', label: 'Users' },
{ id: 'sessions', label: 'Sessions' },
];
return (
<div className="dashboard">
<div className="dashboard-header">
<h1>Management Dashboard</h1>
<div className="dashboard-info">
<span className="info-item"><strong>Client:</strong> {session?.clientName}</span>
<span className="info-item"><strong>Role:</strong> {session?.role}</span>
</div>
</div>
<nav className="dashboard-tabs">
{tabs.map(tab => (
<button
key={tab.id}
className={`tab-btn ${activeTab === tab.id ? 'active' : ''}`}
onClick={() => setActiveTab(tab.id)}
>
{tab.label}
</button>
))}
</nav>
<div className="dashboard-content">
{loading && (
<div className="loading-container">
<div className="spinner"></div>
<p>Loading...</p>
</div>
)}
{error && <div className="error-message">{error}</div>}
{!loading && !error && data && (
<div className="data-panel">
{activeTab === 'overview' && <OverviewPanel data={data} />}
{activeTab === 'clients' && <ClientsPanel data={data} sessionToken={session?.sessionToken} onRefresh={refresh} />}
{activeTab === 'users' && <UsersPanel data={data} sessionToken={session?.sessionToken} onRefresh={refresh} />}
{activeTab === 'sessions' && <SessionsPanel data={data} sessionToken={session?.sessionToken} onRefresh={refresh} />}
</div>
)}
</div>
</div>
);
}
function OverviewPanel({ data }) {
return (
<div className="overview-panel">
<h2>System Overview</h2>
<div className="stats-grid">
<StatCard label="Active Clients" value={data.activeClients} />
<StatCard label="Active Users" value={data.activeUsers} />
<StatCard label="Active Sessions" value={data.activeSessions} />
<StatCard label="API Calls (24h)" value={data.apiCalls24h} />
</div>
<p className="server-time">Server Time: {new Date(data.serverTimeUtc).toLocaleString()}</p>
</div>
);
}
function StatCard({ label, value }) {
return (
<div className="stat-card">
<div className="stat-value">{value ?? '-'}</div>
<div className="stat-label">{label}</div>
</div>
);
}