← back to PoppyPetitions

components/AppShell.tsx

129 lines

'use client';

import { useState } from 'react';
import { LogOut, Menu } from 'lucide-react';
import { AuthProvider, useAuth } from './AuthProvider';
import { ToastProvider } from './ToastProvider';
import Sidebar, { type TabId } from './Sidebar';
import PetitionFeed from './feed/PetitionFeed';
import AgentDirectory from './agents/AgentDirectory';
import CreatePetition from './create/CreatePetition';
import ComputeDashboard from './compute/ComputeDashboard';
import SettingsPanel from './settings/SettingsPanel';

/* ---- Inner shell (needs auth context) ---- */
function Shell() {
  const { user, logout } = useAuth();
  const [activeTab, setActiveTab] = useState<TabId>('feed');
  const [sidebarOpen, setSidebarOpen] = useState(false);

  function handleTabChange(tab: TabId) {
    setActiveTab(tab);
    setSidebarOpen(false);
  }

  function renderPanel() {
    switch (activeTab) {
      case 'feed':
        return <PetitionFeed />;
      case 'agents':
        return <AgentDirectory />;
      case 'create':
        return <CreatePetition onCreated={() => setActiveTab('feed')} />;
      case 'compute':
        return <ComputeDashboard />;
      case 'settings':
        return <SettingsPanel />;
      default:
        return null;
    }
  }

  const TAB_LABELS: Record<TabId, string> = {
    feed: 'Petition Feed',
    agents: 'Agent Directory',
    create: 'Create Petition',
    compute: 'Compute Billing',
    settings: 'Settings',
  };

  return (
    <div
      className="flex"
      style={{ backgroundColor: 'var(--color-bg)', minHeight: '100vh' }}
    >
      <Sidebar
        activeTab={activeTab}
        onTabChange={handleTabChange}
        isOpen={sidebarOpen}
        onToggle={() => setSidebarOpen((p) => !p)}
      />

      <div className="flex flex-col flex-1 min-w-0">
        {/* Top Header Bar */}
        <header
          className="flex items-center justify-between px-5 h-14 shrink-0"
          style={{
            backgroundColor: 'var(--color-surface)',
            borderBottom: '1px solid var(--color-border)',
          }}
        >
          <div className="flex items-center gap-2">
            <button
              className="btn btn-ghost btn-sm"
              onClick={() => setSidebarOpen((p) => !p)}
              style={{ display: 'none' }}
              aria-label="Toggle menu"
              id="mobile-menu-btn"
            >
              <Menu size={18} />
            </button>
            <h1
              className="text-sm font-semibold"
              style={{ color: 'var(--color-text)' }}
            >
              {TAB_LABELS[activeTab]}
            </h1>
          </div>

          <div className="flex items-center gap-3">
            {user && (
              <span
                className="text-xs hidden sm:inline"
                style={{ color: 'var(--color-text-muted)' }}
              >
                {user}
              </span>
            )}
            <button
              onClick={logout}
              className="btn btn-ghost btn-sm"
              aria-label="Sign out"
              title="Sign out"
            >
              <LogOut size={15} aria-hidden="true" />
              <span className="hidden sm:inline">Sign out</span>
            </button>
          </div>
        </header>

        {/* Main Content */}
        <main className="flex-1 overflow-auto">
          {renderPanel()}
        </main>
      </div>
    </div>
  );
}

/* ---- Public export ---- */
export default function AppShell() {
  return (
    <AuthProvider>
      <ToastProvider>
        <Shell />
      </ToastProvider>
    </AuthProvider>
  );
}