← back to Dw Boardroom Governance
frontend/src/App.tsx
95 lines
import { useState, useEffect } from 'react';
import { api } from './api';
import { useWebSocket } from './hooks/useWebSocket';
import TabNav from './components/TabNav';
import Boardroom from './components/Boardroom';
import DecisionPanel from './components/DecisionPanel';
import Ledger from './components/Ledger';
import DelegationForm from './components/DelegationForm';
import Initiatives from './components/Initiatives';
import EscalationQueue from './components/EscalationQueue';
import AutonomyControl from './components/AutonomyControl';
import type { GovernanceStatus } from './types';
const TABS = ['Meeting Room', 'Decisions', 'Delegation', 'Initiatives', 'Escalations'];
export default function App() {
const [activeTab, setActiveTab] = useState(0);
const [status, setStatus] = useState<GovernanceStatus | null>(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={styles.app}>
{/* Header */}
<header style={styles.header}>
<div style={styles.headerLeft}>
<div style={styles.logo}>G</div>
<div>
<h1 style={styles.title}>DW Boardroom Governance</h1>
<div style={styles.subtitle}>
Decision Engine • Delegation • Escalation
<span style={{ ...styles.dot, background: connected ? '#22c55e' : '#ef4444' }} />
{connected ? 'Live' : 'Disconnected'}
{status && ` | ${status.uptime ? Math.round(status.uptime / 60) + 'm uptime' : ''}`}
</div>
</div>
</div>
<div style={styles.headerRight}>
{status && (
<>
<span style={styles.badge}>{status.decisions?.pending || 0} pending</span>
<span style={{ ...styles.badge, background: '#7c3aed33', borderColor: '#7c3aed' }}>
{status.meeting?.active ? 'Meeting Active' : 'No Meeting'}
</span>
</>
)}
</div>
</header>
{/* Tabs */}
<TabNav tabs={TABS} active={activeTab} onChange={setActiveTab} />
{/* Content */}
<main style={styles.main}>
{activeTab === 0 && <Boardroom events={events} />}
{activeTab === 1 && (
<div style={styles.split}>
<DecisionPanel />
<Ledger />
</div>
)}
{activeTab === 2 && <DelegationForm />}
{activeTab === 3 && <Initiatives />}
{activeTab === 4 && (
<div style={styles.split}>
<EscalationQueue />
<AutonomyControl />
</div>
)}
</main>
</div>
);
}
const styles: Record<string, React.CSSProperties> = {
app: { minHeight: '100vh', background: '#0a0a12', color: '#e8eaf0', fontFamily: "'Inter', -apple-system, sans-serif" },
header: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '16px 24px', borderBottom: '1px solid #252540', background: '#12121f' },
headerLeft: { display: 'flex', alignItems: 'center', gap: 12 },
logo: { width: 40, height: 40, borderRadius: 10, background: 'linear-gradient(135deg, #8b5cf6, #6366f1)', display: 'flex', alignItems: 'center', justifyContent: 'center', fontWeight: 800, fontSize: 20 },
title: { margin: 0, fontSize: 20, fontWeight: 700, letterSpacing: '-0.02em' },
subtitle: { fontSize: 12, color: '#7a7f96', display: 'flex', alignItems: 'center', gap: 6, marginTop: 2 },
dot: { width: 6, height: 6, borderRadius: '50%', display: 'inline-block' },
headerRight: { display: 'flex', gap: 8 },
badge: { padding: '4px 10px', borderRadius: 6, fontSize: 11, fontWeight: 600, background: '#22c55e22', border: '1px solid #22c55e44', color: '#e8eaf0' },
main: { padding: 24, maxWidth: 1400, margin: '0 auto' },
split: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 24 },
};