← back to Norma

app/pulse/layout.tsx

457 lines

'use client';

import React, { useState, useEffect } from 'react';
import Link from 'next/link';
import { usePathname, useRouter } from 'next/navigation';
import {
  Menu, X, Twitter, Facebook, Instagram, Mail,
  Home, Activity, Bookmark, User, LogOut, ArrowLeft,
  LogIn,
} from 'lucide-react';
import PulseSearch from '@/components/PulseSearch';

/* ────────────────────────── Types ────────────────────────── */

interface SessionInfo {
  authenticated: boolean;
  user?: string;
  role?: 'admin' | 'staff' | 'intern' | 'pulse';
  displayName?: string;
}

/* ────────────────────────── Colors ────────────────────────── */

const THEMES = {
  classic: {
    name: 'Classic',
    darkGreen: '#1B4332',
    darkGreenHover: '#163728',
    gold: '#DAA520',
    goldHover: '#c8941a',
    cream: '#FAFAF5',
    textLight: '#fafafa',
  },
  institutional: {
    name: 'Institutional',
    darkGreen: '#1e3a5f',
    darkGreenHover: '#162d4a',
    gold: '#2563eb',
    goldHover: '#1d4ed8',
    cream: '#f8fafc',
    textLight: '#f1f5f9',
  },
};

type ThemeKey = keyof typeof THEMES;

/* ────────────────────────── Nav Links ────────────────────────── */

const PUBLIC_NAV = [
  { href: '/pulse', label: 'Home', icon: Home },
  { href: '/pulse/petitions', label: 'Petitions' },
  { href: '/pulse/about', label: 'About' },
];

const USER_NAV = [
  { href: '/pulse', label: 'Home', icon: Home },
  { href: '/pulse/petitions', label: 'Petitions' },
  { href: '/pulse/activity', label: 'Activity', icon: Activity },
  { href: '/pulse/tracked', label: 'Tracked', icon: Bookmark },
  { href: '/pulse/profile', label: 'Profile', icon: User },
];

/* ────────────────────────── Layout ────────────────────────── */

export default function PulseLayout({ children }: { children: React.ReactNode }) {
  const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
  const [session, setSession] = useState<SessionInfo>({ authenticated: false });
  const [sessionLoaded, setSessionLoaded] = useState(false);
  const [themeKey, setThemeKey] = useState<ThemeKey>(() => {
    if (typeof window !== 'undefined') {
      return (localStorage.getItem('pulse-theme') as ThemeKey) || 'classic';
    }
    return 'classic';
  });
  const C = THEMES[themeKey];
  const pathname = usePathname();
  const router = useRouter();

  function toggleTheme() {
    const next: ThemeKey = themeKey === 'classic' ? 'institutional' : 'classic';
    setThemeKey(next);
    if (typeof window !== 'undefined') localStorage.setItem('pulse-theme', next);
  }

  useEffect(() => {
    fetch('/api/auth/session', { credentials: 'include' })
      .then(r => r.json())
      .then(data => {
        if (data.authenticated) {
          setSession({
            authenticated: true,
            user: data.user,
            role: data.role,
            displayName: data.displayName,
          });
        }
      })
      .catch(() => {})
      .finally(() => setSessionLoaded(true));
  }, []);

  const navLinks = session.authenticated ? USER_NAV : PUBLIC_NAV;
  const isAdminOrStaff = session.role === 'admin' || session.role === 'staff';

  async function handleSignOut() {
    await fetch('/api/auth/logout', { method: 'POST', credentials: 'include' });
    setSession({ authenticated: false });
    router.push('/pulse');
    router.refresh();
  }

  function isActive(href: string): boolean {
    if (href === '/pulse') return pathname === '/pulse';
    return pathname.startsWith(href);
  }

  return (
    <div style={{ minHeight: '100vh', backgroundColor: C.cream, color: '#1a1a1a', fontFamily: 'Georgia, "Times New Roman", serif' }}>
      {/* Navigation */}
      <nav style={{ backgroundColor: C.darkGreen, position: 'sticky', top: 0, zIndex: 50 }}>
        <div style={{ maxWidth: 1200, margin: '0 auto', padding: '0 24px' }}>
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', height: 72 }}>
            {/* Logo */}
            <Link href="/pulse" style={{ textDecoration: 'none', display: 'flex', alignItems: 'center', gap: 12 }}>
              <div style={{
                width: 40, height: 40, borderRadius: '50%', border: '2px solid ' + C.gold,
                display: 'flex', alignItems: 'center', justifyContent: 'center',
                fontFamily: 'Georgia, serif', fontWeight: 'bold', fontSize: 18,
                color: C.gold,
              }}>
                P
              </div>
              <span style={{ color: C.gold, fontSize: 22, fontWeight: 'bold', letterSpacing: '0.5px', fontFamily: 'Georgia, serif' }}>
                Pulse of America
              </span>
            </Link>

            {/* Desktop nav */}
            <div style={{ display: 'flex', alignItems: 'center', gap: 8 }} className="poa-desktop-nav">
              {navLinks.map((link) => (
                <Link
                  key={link.href}
                  href={link.href}
                  style={{
                    color: isActive(link.href) ? C.gold : C.textLight,
                    textDecoration: 'none', fontSize: 15,
                    fontFamily: 'system-ui, -apple-system, sans-serif', fontWeight: isActive(link.href) ? 600 : 500,
                    padding: '8px 14px', borderRadius: 8,
                    backgroundColor: isActive(link.href) ? 'rgba(218,165,32,0.12)' : 'transparent',
                    transition: 'all 150ms',
                    display: 'inline-flex', alignItems: 'center', gap: 6,
                  }}
                >
                  {link.icon && <link.icon size={16} />}
                  {link.label}
                </Link>
              ))}

              {/* Theme toggle */}
              <button
                onClick={toggleTheme}
                title={`Switch to ${themeKey === 'classic' ? 'Institutional' : 'Classic'} theme`}
                style={{
                  background: 'rgba(255,255,255,0.1)', border: 'none', color: C.textLight,
                  padding: '6px 10px', borderRadius: 6, cursor: 'pointer', fontSize: 12,
                  fontFamily: 'system-ui, sans-serif', fontWeight: 600, transition: 'all 150ms',
                }}
              >
                {themeKey === 'classic' ? '🏛' : '🌿'} {THEMES[themeKey === 'classic' ? 'institutional' : 'classic'].name}
              </button>

              {/* Global search (⌘K) — public-tier search over petitions/politicians/orgs/topics/articles */}
              <PulseSearch theme={C} />

              {/* Separator */}
              <div style={{ width: 1, height: 28, backgroundColor: 'rgba(255,255,255,0.15)', margin: '0 8px' }} />

              {/* Auth section */}
              {sessionLoaded && session.authenticated ? (
                <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                  {/* Admin/staff: back to dashboard */}
                  {isAdminOrStaff && (
                    <Link
                      href="/"
                      style={{
                        color: C.textLight, textDecoration: 'none', fontSize: 13,
                        fontFamily: 'system-ui, sans-serif', fontWeight: 500,
                        padding: '6px 12px', borderRadius: 6,
                        backgroundColor: 'rgba(255,255,255,0.08)',
                        display: 'inline-flex', alignItems: 'center', gap: 5,
                        transition: 'background-color 150ms',
                      }}
                    >
                      <ArrowLeft size={14} />
                      Dashboard
                    </Link>
                  )}

                  {/* Username badge */}
                  <span style={{
                    color: C.gold, fontSize: 13, fontFamily: 'system-ui, sans-serif',
                    fontWeight: 600, padding: '6px 12px', borderRadius: 6,
                    backgroundColor: 'rgba(218,165,32,0.1)',
                  }}>
                    {session.displayName || session.user}
                  </span>

                  {/* Sign out */}
                  <button
                    onClick={handleSignOut}
                    style={{
                      color: C.textLight, background: 'none', border: 'none', cursor: 'pointer',
                      fontSize: 13, fontFamily: 'system-ui, sans-serif', fontWeight: 500,
                      padding: '6px 10px', borderRadius: 6, display: 'inline-flex',
                      alignItems: 'center', gap: 5, transition: 'background-color 150ms',
                    }}
                    onMouseEnter={e => (e.currentTarget.style.backgroundColor = 'rgba(255,255,255,0.08)')}
                    onMouseLeave={e => (e.currentTarget.style.backgroundColor = 'transparent')}
                  >
                    <LogOut size={14} />
                    Sign Out
                  </button>
                </div>
              ) : sessionLoaded ? (
                <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                  <Link
                    href={`/login?returnTo=${encodeURIComponent(pathname)}`}
                    style={{
                      color: C.textLight, textDecoration: 'none', fontSize: 14,
                      fontFamily: 'system-ui, sans-serif', fontWeight: 500,
                      padding: '8px 16px', borderRadius: 8,
                      display: 'inline-flex', alignItems: 'center', gap: 6,
                      transition: 'background-color 150ms',
                    }}
                    onMouseEnter={e => (e.currentTarget.style.backgroundColor = 'rgba(255,255,255,0.08)')}
                    onMouseLeave={e => (e.currentTarget.style.backgroundColor = 'transparent')}
                  >
                    <LogIn size={15} />
                    Sign In
                  </Link>
                  <Link
                    href="/pulse/petitions/create"
                    style={{
                      color: C.darkGreen, backgroundColor: C.gold, textDecoration: 'none',
                      fontSize: 14, fontFamily: 'system-ui, sans-serif', fontWeight: 600,
                      padding: '10px 24px', borderRadius: 9999, transition: 'background-color 150ms',
                    }}
                    onMouseEnter={e => (e.currentTarget.style.backgroundColor = C.goldHover)}
                    onMouseLeave={e => (e.currentTarget.style.backgroundColor = C.gold)}
                  >
                    Create A Petition
                  </Link>
                </div>
              ) : null}
            </div>

            {/* Mobile menu button */}
            <button
              onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
              className="poa-mobile-menu-btn"
              style={{
                display: 'none', background: 'none', border: 'none', cursor: 'pointer',
                color: C.textLight, padding: 8,
              }}
              aria-label="Toggle menu"
            >
              {mobileMenuOpen ? <X size={24} /> : <Menu size={24} />}
            </button>
          </div>

          {/* Mobile menu */}
          {mobileMenuOpen && (
            <div className="poa-mobile-menu" style={{ paddingBottom: 16 }}>
              {navLinks.map((link) => (
                <Link
                  key={link.href}
                  href={link.href}
                  onClick={() => setMobileMenuOpen(false)}
                  style={{
                    display: 'flex', alignItems: 'center', gap: 8,
                    color: isActive(link.href) ? C.gold : C.textLight,
                    textDecoration: 'none', fontSize: 16,
                    fontFamily: 'system-ui, -apple-system, sans-serif',
                    fontWeight: isActive(link.href) ? 600 : 400,
                    padding: '12px 0',
                    borderBottom: '1px solid rgba(255,255,255,0.1)',
                  }}
                >
                  {link.icon && <link.icon size={16} />}
                  {link.label}
                </Link>
              ))}

              {session.authenticated ? (
                <div style={{ display: 'flex', flexDirection: 'column', gap: 8, marginTop: 12 }}>
                  {isAdminOrStaff && (
                    <Link
                      href="/"
                      onClick={() => setMobileMenuOpen(false)}
                      style={{
                        display: 'inline-flex', alignItems: 'center', gap: 6,
                        color: C.textLight, textDecoration: 'none', fontSize: 14,
                        fontFamily: 'system-ui, sans-serif',
                      }}
                    >
                      <ArrowLeft size={14} />
                      Back to Dashboard
                    </Link>
                  )}
                  <button
                    onClick={() => { setMobileMenuOpen(false); handleSignOut(); }}
                    style={{
                      display: 'inline-flex', alignItems: 'center', gap: 6,
                      color: C.textLight, background: 'none', border: 'none', cursor: 'pointer',
                      fontSize: 14, fontFamily: 'system-ui, sans-serif', padding: 0,
                    }}
                  >
                    <LogOut size={14} />
                    Sign Out ({session.displayName || session.user})
                  </button>
                </div>
              ) : (
                <div style={{ display: 'flex', flexDirection: 'column', gap: 8, marginTop: 12 }}>
                  <Link
                    href={`/login?returnTo=${encodeURIComponent(pathname)}`}
                    onClick={() => setMobileMenuOpen(false)}
                    style={{
                      display: 'inline-flex', alignItems: 'center', gap: 6,
                      color: C.textLight, textDecoration: 'none', fontSize: 14,
                      fontFamily: 'system-ui, sans-serif',
                    }}
                  >
                    <LogIn size={14} />
                    Sign In
                  </Link>
                  <Link
                    href="/pulse/petitions/create"
                    onClick={() => setMobileMenuOpen(false)}
                    style={{
                      display: 'inline-block', color: C.darkGreen, backgroundColor: C.gold,
                      textDecoration: 'none', fontSize: 14, fontWeight: 600,
                      padding: '10px 24px', borderRadius: 9999,
                    }}
                  >
                    Create A Petition
                  </Link>
                </div>
              )}
            </div>
          )}
        </div>
      </nav>

      {/* Main content */}
      <main>{children}</main>

      {/* Footer */}
      <footer style={{ backgroundColor: C.darkGreen, color: C.textLight, marginTop: 0 }}>
        <div style={{ maxWidth: 1200, margin: '0 auto', padding: '48px 24px 32px' }}>
          <div style={{ display: 'flex', flexWrap: 'wrap', gap: 48, justifyContent: 'space-between', marginBottom: 40 }}>
            {/* Brand */}
            <div style={{ maxWidth: 320 }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 16 }}>
                <div style={{
                  width: 36, height: 36, borderRadius: '50%', border: '2px solid ' + C.gold,
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                  fontWeight: 'bold', fontSize: 16, color: C.gold,
                }}>P</div>
                <span style={{ color: C.gold, fontSize: 20, fontWeight: 'bold', fontFamily: 'Georgia, serif' }}>
                  Pulse of America
                </span>
              </div>
              <p style={{ fontSize: 14, lineHeight: '22px', opacity: 0.8, fontFamily: 'system-ui, sans-serif' }}>
                Your Voice Matters. We empower citizens to create, sign, and share petitions
                that drive real change in communities across America.
              </p>
            </div>

            {/* Quick links */}
            <div>
              <h4 style={{ fontSize: 14, fontWeight: 600, color: C.gold, marginBottom: 16, fontFamily: 'system-ui, sans-serif', textTransform: 'uppercase', letterSpacing: 1 }}>
                Quick Links
              </h4>
              <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
                {PUBLIC_NAV.map((link) => (
                  <Link
                    key={link.href}
                    href={link.href}
                    style={{ color: C.textLight, textDecoration: 'none', fontSize: 14, opacity: 0.8, fontFamily: 'system-ui, sans-serif' }}
                  >
                    {link.label}
                  </Link>
                ))}
              </div>
            </div>

            {/* Social */}
            <div>
              <h4 style={{ fontSize: 14, fontWeight: 600, color: C.gold, marginBottom: 16, fontFamily: 'system-ui, sans-serif', textTransform: 'uppercase', letterSpacing: 1 }}>
                Connect
              </h4>
              <div style={{ display: 'flex', gap: 16 }}>
                {[
                  { icon: <Twitter size={20} />, label: 'Twitter' },
                  { icon: <Facebook size={20} />, label: 'Facebook' },
                  { icon: <Instagram size={20} />, label: 'Instagram' },
                  { icon: <Mail size={20} />, label: 'Email' },
                ].map((social) => (
                  <button
                    key={social.label}
                    aria-label={social.label}
                    style={{
                      width: 40, height: 40, borderRadius: '50%', border: '1px solid rgba(218,165,32,0.4)',
                      backgroundColor: 'transparent', color: C.gold, cursor: 'pointer',
                      display: 'flex', alignItems: 'center', justifyContent: 'center',
                      transition: 'all 150ms',
                    }}
                    onMouseEnter={e => { e.currentTarget.style.backgroundColor = C.gold; e.currentTarget.style.color = C.darkGreen; }}
                    onMouseLeave={e => { e.currentTarget.style.backgroundColor = 'transparent'; e.currentTarget.style.color = C.gold; }}
                  >
                    {social.icon}
                  </button>
                ))}
              </div>
            </div>
          </div>

          {/* Bottom bar */}
          <div style={{
            borderTop: '1px solid rgba(255,255,255,0.15)', paddingTop: 24,
            display: 'flex', flexWrap: 'wrap', justifyContent: 'space-between', alignItems: 'center', gap: 12,
          }}>
            <p style={{ fontSize: 13, opacity: 0.7, fontFamily: 'system-ui, sans-serif', margin: 0 }}>
              Pulse of America &mdash; Your Voice Matters
            </p>
            <p style={{ fontSize: 13, opacity: 0.5, fontFamily: 'system-ui, sans-serif', margin: 0 }}>
              Making democracy work, one petition at a time.
            </p>
          </div>
        </div>
      </footer>

      {/* Scoped responsive styles */}
      <style>{`
        .poa-desktop-nav { display: flex !important; }
        .poa-mobile-menu-btn { display: none !important; }
        .poa-mobile-menu { display: block; }

        @media (max-width: 768px) {
          .poa-desktop-nav { display: none !important; }
          .poa-mobile-menu-btn { display: block !important; }
        }
      `}</style>
    </div>
  );
}