NeonClary's picture
Workshop nav, experiment editing, and saved chats
31091fd verified
Raw History Blame Contribute Delete
6.51 kB
import { Outlet, Link, useLocation } from 'react-router-dom'
import { useState } from 'react'
import { useAuth } from '../context/AuthContext'
import { UserMenu } from './UserMenu'
import { SaveWarningBar } from './SaveWarningBar'
import { ProfileModal } from './ProfileModal'
import { AccountModal } from './AccountModal'
import { ClearDataModal } from './ClearDataModal'
import { PersonaPromptBuildHost } from './PersonaPromptBuildHost'
import { AdvisorPanelBuildHost } from './AdvisorPanelBuildHost'
import { WorkshopTutorialHost } from './WorkshopTutorialHost'
import { HeaderNavDropdown } from './HeaderNavDropdown'
import { SiteFooter } from './SiteFooter'
function headerNavLinkClass(active: boolean) {
return `header-nav-link${active ? ' header-nav-link--current' : ''}`
}
/** Soft “you are here” label in the site header (not a second competing headline). */
function headerPageContext(pathname: string): string | null {
/* Experiment pages omit header context — each page has its own H1. */
if (pathname === '/experiment' || pathname === '/experiment-advisor') return null
if (pathname.startsWith('/create-persona')) return 'Create a Persona'
if (pathname.startsWith('/create-advisor')) return 'Create an Advisor Panel'
if (pathname === '/workshop/manage') return 'Manage creations'
if (/^\/persona\/[^/]+\/summary/.test(pathname)) return 'Persona summary'
if (/^\/advisor\/[^/]+\/summary/.test(pathname)) return 'Advisor summary'
return null
}
const PERSONA_NAV_ITEMS = [
{ to: '/persona', label: 'Persona Workshop' },
{ to: '/create-persona/form', label: 'Create a Persona — form' },
{ to: '/create-persona/chat', label: 'Create a Persona — Interactive Q&A' },
{ to: '/experiment', label: 'Experiment with Persona Prompts' },
{ to: '/workshop/manage', label: 'Manage Personas and Advisory Panels' },
] as const
const ADVISOR_NAV_ITEMS = [
{ to: '/advisor', label: 'Advisor Panel Workshop' },
{ to: '/create-advisor/form', label: 'Create an Advisor Panel — form' },
{ to: '/create-advisor/chat', label: 'Create an Advisor Panel — Interactive Q&A' },
{ to: '/experiment-advisor', label: 'Experiment with Advisor Panels' },
{ to: '/workshop/manage', label: 'Manage Personas and Advisory Panels' },
] as const
export function AppLayout() {
const { pathname } = useLocation()
const { user, loading } = useAuth()
const [profile, setProfile] = useState(false)
const [account, setAccount] = useState(false)
const [clear, setClear] = useState(false)
const [pinnedMenu, setPinnedMenu] = useState<'persona' | 'advisor' | null>(null)
const showSaveBanner = !loading && !user
const pageContext = headerPageContext(pathname)
const suppressLayoutFooter =
pathname === '/experiment' || pathname === '/experiment-advisor'
const homeActive = pathname === '/'
const personaActive =
pathname === '/persona' ||
pathname.startsWith('/persona/') ||
pathname.startsWith('/create-persona') ||
pathname === '/experiment'
const advisorActive =
pathname === '/advisor' ||
pathname.startsWith('/advisor/') ||
pathname.startsWith('/create-advisor') ||
pathname === '/experiment-advisor'
return (
<div
className={`layout-main${showSaveBanner ? ' layout-main--save-banner' : ''}`}
>
<header className="header-bar">
<div className="header-bar-brand">
<div className="header-brand-lockup">
<a
href="https://neon.ai"
target="_blank"
rel="noopener noreferrer"
className="header-brand-logo-link"
title="Neon.ai — the company behind this workshop"
>
<img
src="/neon-logo.png"
alt="Neon.ai"
className="header-brand-logo"
/>
</a>
<Link to="/" className="header-brand" title="AI Expert Advisory Builder's Workshop">
<span className="header-brand-kicker">AI Expert Advisory</span>
<span className="header-brand-name">Builder&apos;s Workshop</span>
</Link>
<a
href="https://neon.ai"
target="_blank"
rel="noopener noreferrer"
className="header-maker"
title="Neon.ai — the company behind this workshop"
>
<span className="header-maker-label">by</span>
<span className="header-maker-name">Neon.ai</span>
</a>
</div>
{pageContext && (
<p className="header-page-context">
<span className="header-page-context-label">Now viewing</span>
<span className="header-page-context-value">{pageContext}</span>
</p>
)}
</div>
<nav className="header-bar-nav" aria-label="Main">
<Link
to="/"
className={headerNavLinkClass(homeActive)}
aria-current={homeActive ? 'page' : undefined}
>
Home
</Link>
<HeaderNavDropdown
label="Personas"
active={personaActive}
items={[...PERSONA_NAV_ITEMS]}
pinnedOpen={pinnedMenu === 'persona'}
onPinnedChange={(pinned) => setPinnedMenu(pinned ? 'persona' : null)}
/>
<HeaderNavDropdown
label="Advisor Panels"
active={advisorActive}
items={[...ADVISOR_NAV_ITEMS]}
pinnedOpen={pinnedMenu === 'advisor'}
onPinnedChange={(pinned) => setPinnedMenu(pinned ? 'advisor' : null)}
/>
</nav>
<div className="header-bar-tools">
<UserMenu
onProfile={() => setProfile(true)}
onAccount={() => setAccount(true)}
onClearData={() => setClear(true)}
/>
</div>
</header>
<PersonaPromptBuildHost />
<AdvisorPanelBuildHost />
<div className="layout-body">
<Outlet />
</div>
{!suppressLayoutFooter && <SiteFooter />}
<WorkshopTutorialHost />
<SaveWarningBar />
{profile && <ProfileModal onClose={() => setProfile(false)} />}
{account && <AccountModal onClose={() => setAccount(false)} />}
{clear && <ClearDataModal onClose={() => setClear(false)} />}
</div>
)
}