Spaces:
Running
Running
File size: 3,906 Bytes
e6ba1a2 03d90a3 e6ba1a2 03d90a3 e6ba1a2 e881b4c 2dc7022 e881b4c 2dc7022 e881b4c e6ba1a2 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | import React from 'react';
import { Home, Menu, Users } from 'lucide-react';
import ThemeToggle from './ThemeToggle';
import { useAppConfig } from '../contexts/AppConfigContext';
/**
* Shared floating header used on every page so the app feels like one surface.
*
* Props:
* currentPage: 'home' | 'chat' | 'canvas'
* onNavigateToHome, onNavigateToChat, onNavigateToCanvas: navigation callbacks
* (onNavigateToCanvas may receive 'insights' | 'workspace' to deep-link a view)
* onMobileMenu?: () => void — when present, shows the mobile menu button
* children?: ReactNode — extra controls slotted between the tabs and the theme toggle
*/
const AppHeader = ({
currentPage = 'home',
onNavigateToHome,
onNavigateToChat,
onNavigateToCanvas,
onMobileMenu,
children,
}) => {
const { config, resolveIcon } = useAppConfig();
const BrandIcon = resolveIcon ? resolveIcon('Users') : Users;
const goToCanvas = (view) => {
if (onNavigateToCanvas) onNavigateToCanvas(view);
};
// Accept either 'canvas' (all canvas tabs highlight equally) or a more specific
// 'canvas-<subview>' from CanvasPage so only the active one highlights.
const isOnHome = currentPage === 'home';
const isOnChat = currentPage === 'chat';
const isOnCanvas = currentPage === 'canvas' || currentPage.startsWith('canvas-');
const canvasSub = currentPage.startsWith('canvas-') ? currentPage.slice(7) : null;
const tabActive = (sub) => isOnCanvas && (canvasSub === null ? false : canvasSub === sub);
return (
<header className="floating-header app-header">
<div className="header-left">
{onMobileMenu && (
<button className="mobile-menu-button" onClick={onMobileMenu}>
<Menu size={20} />
</button>
)}
<button
className="modern-home-btn"
onClick={onNavigateToHome}
title="Home"
disabled={isOnHome}
aria-disabled={isOnHome}
>
<Home size={20} />
</button>
<div className="header-brand">
<div className="brand-icon">
<BrandIcon size={24} />
</div>
<div className="brand-text">
<h1>{config?.app?.title || 'Advisory'}</h1>
<p>{config?.app?.subtitle || 'AI-Powered Guidance'}</p>
</div>
</div>
</div>
{/* Hide the view pill bar on the home page — home is a landing page,
not part of the chat ↔ canvas surface. */}
{!isOnHome && (
<div className="canvas-tabs chat-view-tabs">
<button className={`tab ${isOnChat ? 'active' : ''}`} onClick={onNavigateToChat}>Chat</button>
<button className={`tab ${tabActive('insights') ? 'active' : ''}`} onClick={() => goToCanvas('insights')}>Insights</button>
<button className={`tab ${tabActive('workspace') ? 'active' : ''}`} onClick={() => goToCanvas('workspace')}>Workspace</button>
<button className={`tab ${tabActive('deliverables') ? 'active' : ''}`} onClick={() => goToCanvas('deliverables')}>Documents</button>
</div>
)}
{/* Compact mobile dropdown — appears in place of the pill bar at narrow widths */}
{!isOnHome && (
<select
className="canvas-tabs-mobile"
value={isOnChat ? 'chat' : (canvasSub || 'workspace')}
onChange={(e) => {
const v = e.target.value;
if (v === 'chat') onNavigateToChat();
else goToCanvas(v);
}}
>
<option value="chat">Chat</option>
<option value="insights">Insights</option>
<option value="workspace">Workspace</option>
<option value="deliverables">Documents</option>
</select>
)}
<div className="header-right">
{children}
<ThemeToggle />
</div>
</header>
);
};
export default AppHeader;
|