← back to Dw Boardroom V2
frontend/src/App.tsx
109 lines
import { useState, useEffect } from 'react';
import { api } from './api';
import { useWebSocket } from './hooks/useWebSocket';
import MeetingRoom from './components/MeetingRoom';
import AgendaManager from './components/AgendaManager';
import AgentRoster from './components/AgentRoster';
const TABS = [
{ id: 'meeting', label: 'Meeting Room', icon: '\uD83C\uDFDB' },
{ id: 'agenda', label: 'Agenda', icon: '\uD83D\uDCCB' },
{ id: 'agents', label: 'Agents', icon: '\uD83E\uDD16' },
];
export default function App() {
const [tab, setTab] = useState('meeting');
const [status, setStatus] = useState<any>(null);
const { events, connected } = useWebSocket();
useEffect(() => {
api.getStatus().then(setStatus).catch(() => {});
const interval = setInterval(() => {
api.getStatus().then(setStatus).catch(() => {});
}, 15000);
return () => clearInterval(interval);
}, []);
return (
<div style={{ minHeight: '100vh', background: '#0a0a12' }}>
{/* Header */}
<header style={styles.header}>
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
<span style={{ fontSize: 20 }}>{'\uD83C\uDFDB'}</span>
<h1 style={{ fontSize: 16, fontWeight: 800, margin: 0, letterSpacing: '-0.3px' }}>DW Boardroom V2</h1>
</div>
<div style={{ display: 'flex', gap: 12, alignItems: 'center' }}>
<span style={{ ...styles.badge, background: connected ? '#22c55e22' : '#ef444422', color: connected ? '#22c55e' : '#ef4444' }}>
{connected ? 'WS Live' : 'WS Off'}
</span>
{status?.activeMeeting && (
<span style={{ ...styles.badge, background: '#8b5cf622', color: '#8b5cf6' }}>
{status.activeMeeting.type} — {status.activeMeeting.phase}
</span>
)}
<span style={{ ...styles.badge, background: '#3b82f622', color: '#3b82f6' }}>
{status?.agents || 0} agents
</span>
<span style={{
...styles.badge,
background: status?.governance?.available ? '#22c55e22' : '#ef444422',
color: status?.governance?.available ? '#22c55e' : '#ef4444'
}}>
Gov {status?.governance?.available ? 'OK' : 'OFF'}
</span>
</div>
</header>
{/* Tabs */}
<nav style={styles.nav}>
{TABS.map(t => (
<button
key={t.id}
onClick={() => setTab(t.id)}
style={{
...styles.tab,
...(tab === t.id ? styles.tabActive : {}),
}}
>
<span style={{ fontSize: 14 }}>{t.icon}</span>
{t.label}
</button>
))}
</nav>
{/* Content */}
<main style={styles.main}>
{tab === 'meeting' && <MeetingRoom events={events} />}
{tab === 'agenda' && <AgendaManager />}
{tab === 'agents' && <AgentRoster />}
</main>
</div>
);
}
const styles: Record<string, React.CSSProperties> = {
header: {
display: 'flex', justifyContent: 'space-between', alignItems: 'center',
padding: '12px 24px', borderBottom: '1px solid #252540',
background: 'linear-gradient(180deg, #0f0f1e 0%, #0a0a12 100%)',
},
badge: {
padding: '4px 10px', borderRadius: 6, fontSize: 11, fontWeight: 700,
},
nav: {
display: 'flex', gap: 4, padding: '8px 24px', borderBottom: '1px solid #252540',
},
tab: {
display: 'flex', alignItems: 'center', gap: 6,
padding: '8px 16px', borderRadius: 8, border: 'none',
background: 'transparent', color: '#7a7f96', fontWeight: 600, fontSize: 12,
cursor: 'pointer', fontFamily: 'inherit', transition: 'all 0.2s',
},
tabActive: {
background: '#8b5cf622', color: '#8b5cf6',
},
main: {
padding: 24, maxWidth: 1440, margin: '0 auto',
},
};