← 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',
  },
};