@@ -1,24 +1,42 @@
import { useState , useEffect } from 'react' ;
import { useState , useEffect , useRef } from 'react' ;
import { NavLink , useNavigate , useLocation } from 'react-router-dom' ;
import { useAuth } from '../context/AuthContext' ;
const ICONS = {
dashboard : < svg viewBox = "0 0 16 16" fill = "currentColor" > < rect x = "1" y = "1" width = "6" height = "6" rx = "1.5" / > < rect x = "9" y = "1" width = "6" height = "6" rx = "1.5" / > < rect x = "1" y = "9" width = "6" height = "6" rx = "1.5" / > < rect x = "9" y = "9" width = "6" height = "6" rx = "1.5" / > < / svg > ,
requests : < svg viewBox = "0 0 16 16" fill = "none" stroke = "currentColor" strokeWidth = "1.5" strokeLinecap = "round" > < rect x = "2" y = "2" width = "12" height = "12" rx = "1.5" / > < line x1 = "5" y1 = "5.5" x2 = "11" y2 = "5.5" / > < line x1 = "5" y1 = "8" x2 = "11" y2 = "8" / > < line x1 = "5" y1 = "10.5" x2 = "8" y2 = "10.5" / > < / svg > ,
projects : < svg viewBox = "0 0 16 16" fill = "none" stroke = "currentColor" strokeWidth = "1.5" strokeLinecap = "round" strokeLinejoin = "round" > < path d = "M1.5 5.5C1.5 4.67 2.17 4 3 4h2.5l1.5 2H13c.83 0 1.5.67 1.5 1.5v5.5c0 .83-.67 1.5-1.5 1.5H3c-.83 0-1.5-.67-1.5-1.5V5.5z" / > < / svg > ,
fileSharing : < svg viewBox = "0 0 16 16" fill = "none" stroke = "currentColor" strokeWidth = "1.5" strokeLinecap = "round" strokeLinejoin = "round" > < path d = "M9 2H4a1.5 1.5 0 00-1.5 1.5v9A1.5 1.5 0 004 14h8a1.5 1.5 0 001.5-1.5V6L9 2z" / > < polyline points = "9,2 9,6 13.5,6" / > < / svg > ,
invoices : < svg viewBox = "0 0 16 16" fill = "none" stroke = "currentColor" strokeWidth = "1.5" strokeLinecap = "round" > < rect x = "2" y = "1.5" width = "12" height = "13" rx = "1" / > < line x1 = "8" y1 = "4" x2 = "8" y2 = "12" / > < path d = "M10 5.5H7a1.5 1.5 0 000 3h2a1.5 1.5 0 010 3H5.5" / > < / svg > ,
notes : < svg viewBox = "0 0 16 16" fill = "none" stroke = "currentColor" strokeWidth = "1.5" strokeLinecap = "round" > < rect x = "3" y = "1.5" width = "10" height = "13" rx = "1" / > < line x1 = "5.5" y1 = "5" x2 = "10.5" y2 = "5" / > < line x1 = "5.5" y1 = "7.5" x2 = "10.5" y2 = "7.5" / > < line x1 = "5.5" y1 = "10" x2 = "8.5" y2 = "10" / > < / svg > ,
survey : < svg viewBox = "0 0 16 16" fill = "none" stroke = "currentColor" strokeWidth = "1.5" strokeLinecap = "round" strokeLinejoin = "round" > < rect x = "1.5" y = "9.5" width = "3" height = "5" rx = "0.5" / > < rect x = "6.5" y = "6" width = "3" height = "8.5" rx = "0.5" / > < rect x = "11.5" y = "2.5" width = "3" height = "12" rx = "0.5" / > < / svg > ,
brandBook : < svg viewBox = "0 0 16 16" fill = "none" stroke = "currentColor" strokeWidth = "1.5" strokeLinecap = "round" strokeLinejoin = "round" > < path d = "M2.5 2.5h4a2 2 0 012 2v9a2 2 0 00-2-2h-4V2.5z" / > < path d = "M13.5 2.5h-4a2 2 0 00-2 2v9a2 2 0 012-2h4V2.5z" / > < / svg > ,
converter : < svg viewBox = "0 0 16 16" fill = "none" stroke = "currentColor" strokeWidth = "1.5" strokeLinecap = "round" strokeLinejoin = "round" > < rect x = "1.5" y = "1.5" width = "13" height = "13" rx = "1.5" / > < circle cx = "5.5" cy = "5.5" r = "1.5" / > < path d = "M1.5 11.5l3.5-3.5 3 3L11 7.5l3 3" / > < / svg > ,
passwords : < svg viewBox = "0 0 16 16" fill = "none" stroke = "currentColor" strokeWidth = "1.5" strokeLinecap = "round" strokeLinejoin = "round" > < rect x = "3" y = "7" width = "10" height = "7.5" rx = "1" / > < path d = "M5 7V5.5a3 3 0 016 0V7" / > < circle cx = "8" cy = "10.5" r = "1" fill = "currentColor" stroke = "none" / > < / svg > ,
companies : < svg viewBox = "0 0 16 16" fill = "none" stroke = "currentColor" strokeWidth = "1.5" strokeLinecap = "round" strokeLinejoin = "round" > < rect x = "1.5" y = "5" width = "13" height = "9.5" rx = "1" / > < path d = "M5.5 5V3a1 1 0 011-1h3a1 1 0 011 1v2" / > < line x1 = "8" y1 = "5" x2 = "8" y2 = "14.5" / > < line x1 = "1.5" y1 = "9" x2 = "14.5" y2 = "9" / > < / svg > ,
users : < svg viewBox = "0 0 16 16" fill = "none" stroke = "currentColor" strokeWidth = "1.5" strokeLinecap = "round" > < circle cx = "6" cy = "5" r = "2.5" / > < path d = "M1 14c0-2.76 2.24-5 5-5s5 2.24 5 5" / > < circle cx = "12.5" cy = "5.5" r = "2" / > < path d = "M12.5 10c1.93 0 3.5 1.57 3.5 3.5" / > < / svg > ,
} ;
function NI ( { icon } ) {
return < span className = "nav-icon" > { icon } < / span > ;
}
function TeamNav ( { onNav } ) {
const location = useLocation ( ) ;
const primaryLinks = [
{ to : '/dashboard' , label : 'Dashboard' } ,
{ to : '/projec ts' , label : 'Projects' } ,
{ to : '/reques ts' , label : 'Requests' } ,
{ to : '/file-sharing ' , label : 'File Sharing' } ,
{ to : '/team/ dashboard' , label : 'Dashboard' , icon : ICONS . dashboard } ,
{ to : '/reques ts' , label : 'Requests' , icon : ICONS . requests } ,
{ to : '/projec ts' , label : 'Projects' , icon : ICONS . projects } ,
{ to : '/invoices ' , label : 'Finances' , icon : ICONS . invoices } ,
{ to : '/file-sharing' , label : 'File Sharing' , icon : ICONS . fileSharing } ,
] ;
const utilityLinks = [
{ to : '/meeting-notes ' , label : 'Meeting Notes' } ,
{ to : '/invoices' , label : 'Invoices & Expenses' } ,
{ to : '/survey-mak er' , label : 'Survey Maker' } ,
{ to : '/brand-book' , label : 'Brand Book Maker' } ,
{ to : '/converters' , label : 'Image Converter' } ,
{ to : '/fourge-passwords' , label : 'Fourge Passwords' } ,
{ to : '/survey-maker ' , label : 'Survey Maker' , icon : ICONS . survey } ,
{ to : '/brand-book' , label : 'Brand Book Maker' , icon : ICONS . brandBook } ,
{ to : '/convert ers ' , label : 'Image Converter' , icon : ICONS . converter } ,
{ to : '/fourge-passwords' , label : 'Fourge Passwords' , icon : ICONS . passwords } ,
] ;
const isCompaniesActive = location . pathname === '/company' && ! location . search . includes ( 'tab=users' ) ;
@@ -26,51 +44,44 @@ function TeamNav({ onNav }) {
return (
< div className = "sidebar-section" >
{ primaryLinks . map ( ( { to , label } ) => (
{ primaryLinks . map ( ( { to , label , icon } ) => (
< NavLink key = { to } to = { to } onClick = { onNav } className = { ( { isActive } ) => ` sidebar-link ${ isActive ? ' active' : '' } ` } >
{ label }
< NI icon = { icon } / > < span className = "nav-label" > { label } < / span >
< / NavLink >
) ) }
< div style = { { height : 1 , margin : '10px 12px' , background : 'var(--border)' } } / >
< div style = { { padding : '0 12px 8px' , fontSize : 11 , fontWeight : 700 , letterSpacing : 0.8 , textTransform : 'uppercase' , color : 'var(--text-muted)' } } >
Team Tools
< div className = "sidebar-tools-divider" style = { { height : 1 , margin : '10px 12px' , background : 'var(--border)' } } / >
< div className = "sidebar-tools-label" style = { { padding : '0 12px 8px' , fontSize : 11 , fontWeight : 400 , letterSpacing : 0.8 , textTransform : 'uppercase' , color : 'var(--text-muted)' } } >
Tools
< / div >
{ utilityLinks . map ( ( { to , label } ) => (
{ utilityLinks . map ( ( { to , label , icon } ) => (
< NavLink key = { to } to = { to } onClick = { onNav } className = { ( { isActive } ) => ` sidebar-link ${ isActive ? ' active' : '' } ` } >
{ label }
< NI icon = { icon } / > < span className = "nav-label" > { label } < / span >
< / NavLink >
) ) }
< NavLink to = "/company" onClick = { onNav } className = { ( ) => ` sidebar-link ${ isCompaniesActive ? ' active' : '' } ` } >
Companies
< NI icon = { ICONS . companies } / > < span className = "nav-label" > Companies < / span >
< / NavLink >
< NavLink to = "/company?tab=users" onClick = { onNav } className = { ( ) => ` sidebar-link ${ isUsersActive ? ' active' : '' } ` } >
Users
< NI icon = { ICONS . users } / > < span className = "nav-label" > Users < / span >
< / NavLink >
< / div >
) ;
}
function ClientNav ( { onNav } ) {
const { currentUser } = useAuth ( ) ;
const companies = currentUser ? . companies ? . length
? currentUser . companies
: currentUser ? . company ? [ currentUser . company ] : [ ] ;
const companyTo = companies . length === 1 ? ` /company/ ${ companies [ 0 ] . id } ` : '/company' ;
const links = [
{ to : '/dashboard' , label : 'Dashboard' } ,
{ to : '/projec ts' , label : 'Projects' } ,
{ to : '/reques ts' , label : 'Requests' } ,
{ to : '/file-sharing' , label : 'File Sharing' } ,
{ to : '/my-invoices' , label : 'Invoices' } ,
{ to : companyTo , label : 'Company' } ,
{ to : '/dashboard' , label : 'Dashboard' , icon : ICONS . dashboard } ,
{ to : '/reques ts' , label : 'Requests' , icon : ICONS . requests } ,
{ to : '/projec ts' , label : 'Projects' , icon : ICONS . projects } ,
{ to : '/file-sharing' , label : 'File Sharing' , icon : ICONS . fileSharing } ,
{ to : '/my-invoices' , label : 'Invoices' , icon : ICONS . invoices } ,
] ;
return (
< div className = "sidebar-section" >
{ links . map ( ( { to , label } ) => (
{ links . map ( ( { to , label , icon } ) => (
< NavLink key = { label } to = { to } onClick = { onNav } className = { ( { isActive } ) => ` sidebar-link ${ isActive ? ' active' : '' } ` } >
{ label }
< NI icon = { icon } / > < span className = "nav-label" > { label } < / span >
< / NavLink >
) ) }
< / div >
@@ -79,30 +90,30 @@ function ClientNav({ onNav }) {
function ExternalNav ( { onNav } ) {
const links = [
{ to : '/dashboard' , label : 'Dashboard' } ,
{ to : '/requests' , label : 'Requests' } ,
{ to : '/projects' , label : 'Projects' } ,
{ to : '/my-invoices-sub' , label : 'Invoices' } ,
{ to : '/file-sharing' , label : 'File Sharing' } ,
{ to : '/survey-maker' , label : 'Survey Maker' } ,
{ to : '/brand-book' , label : 'Brand Book Maker' } ,
{ to : '/converters' , label : 'Image Converter' } ,
{ to : '/dashboard' , label : 'Dashboard' , icon : ICONS . dashboard } ,
{ to : '/requests' , label : 'Requests' , icon : ICONS . requests } ,
{ to : '/projects' , label : 'Projects' , icon : ICONS . projects } ,
{ to : '/my-invoices-sub' , label : 'Invoices' , icon : ICONS . invoices } ,
{ to : '/file-sharing' , label : 'File Sharing' , icon : ICONS . fileSharing } ,
{ to : '/survey-maker' , label : 'Survey Maker' , icon : ICONS . survey } ,
{ to : '/brand-book' , label : 'Brand Book Maker' , icon : ICONS . brandBook } ,
{ to : '/converters' , label : 'Image Converter' , icon : ICONS . converter } ,
] ;
return (
< div className = "sidebar-section" >
{ links . map ( ( { to , label } , index ) => (
{ links . map ( ( { to , label , icon } , index ) => (
< div key = { to } >
{ index === 4 && (
< >
< div style = { { height : 1 , margin : '10px 12px' , background : 'var(--border)' } } / >
< div style = { { padding : '0 12px 8px' , fontSize : 11 , fontWeight : 700 , letterSpacing : 0.8 , textTransform : 'uppercase' , color : 'var(--text-muted)' } } >
Team Tools
< div className = "sidebar-tools-divider" style = { { height : 1 , margin : '10px 12px' , background : 'var(--border)' } } / >
< div className = "sidebar-tools-label" style = { { padding : '0 12px 8px' , fontSize : 11 , fontWeight : 400 , letterSpacing : 0.8 , textTransform : 'uppercase' , color : 'var(--text-muted)' } } >
Tools
< / div >
< / >
) }
< NavLink to = { to } onClick = { onNav } className = { ( { isActive } ) => ` sidebar-link ${ isActive ? ' active' : '' } ` } >
{ label }
< NI icon = { icon } / > < span className = "nav-label" > { label } < / span >
< / NavLink >
< / div >
) ) }
@@ -116,7 +127,22 @@ export default function Layout({ children }) {
const location = useLocation ( ) ;
const [ theme , setTheme ] = useState ( ( ) => localStorage . getItem ( 'theme' ) || 'dark' ) ;
const [ menuOpen , setMenuOpen ] = useState ( false ) ;
const [ sidebarCollapsed , setSidebarCollapsed ] = useState ( ( ) => localStorage . getItem ( 'sidebarCollapsed' ) === 'true ') ;
const [ searchQuery , setSearchQuery ] = useState ( ' ') ;
const [ searchResults , setSearchResults ] = useState ( { projects : [ ] , tasks : [ ] } ) ;
const [ searchOpen , setSearchOpen ] = useState ( false ) ;
const [ searchExpanded , setSearchExpanded ] = useState ( false ) ;
const searchInputRef = useRef ( null ) ;
const [ avatarOpen , setAvatarOpen ] = useState ( false ) ;
const avatarRef = useRef ( null ) ;
const hour = new Date ( ) . getHours ( ) ;
const timeOfDay = hour < 12 ? 'morning' : hour < 17 ? 'afternoon' : 'evening' ;
const firstName = currentUser ? . name ? . split ( ' ' ) [ 0 ] || '' ;
const isProfileRoute = location . pathname === '/profile' || location . pathname . startsWith ( '/profile/' ) ;
const headerTitle = isProfileRoute ? 'Profile' : ` Good ${ timeOfDay } ${ firstName ? ` , ${ firstName } ` : '' } ` ;
const headerSubtitle = isProfileRoute
? 'Account details and security settings.'
: "Here's what's happening today." ;
useEffect ( ( ) => {
document . documentElement . setAttribute ( 'data-theme' , theme ) ;
@@ -124,81 +150,146 @@ export default function Layout({ children }) {
} , [ theme ] ) ;
useEffect ( ( ) => {
localStorage . setItem ( 'sidebarCollapsed' , String ( sidebarCollapsed ) ) ;
} , [ sidebarCollapsed ] ) ;
if ( ! searchQuery . trim ( ) ) { setSearchResults ( { projects : [ ] , tasks : [ ] } ) ; return ; }
const t = setTimeout ( async ( ) => {
const { supabase } = await import ( '../lib/supabase' ) ;
const [ { data : projects } , { data : tasks } ] = await Promise . all ( [
supabase . from ( 'projects' ) . select ( 'id, name' ) . ilike ( 'name' , ` % ${ searchQuery } % ` ) . limit ( 6 ) ,
supabase . from ( 'tasks' ) . select ( 'id, title' ) . ilike ( 'title' , ` % ${ searchQuery } % ` ) . limit ( 6 ) ,
] ) ;
setSearchResults ( { projects : projects || [ ] , tasks : tasks || [ ] } ) ;
} , 280 ) ;
return ( ) => clearTimeout ( t ) ;
} , [ searchQuery ] ) ;
useEffect ( ( ) => {
if ( ! avatarOpen ) return ;
function handler ( e ) {
if ( avatarRef . current && ! avatarRef . current . contains ( e . target ) ) setAvatarOpen ( false ) ;
}
document . addEventListener ( 'mousedown' , handler ) ;
return ( ) => document . removeEventListener ( 'mousedown' , handler ) ;
} , [ avatarOpen ] ) ;
// Close menu on route change (derived-state pattern, no effect needed)
const [ lastPathname , setLastPathname ] = useState ( location . pathname ) ;
if ( lastPathname !== location . pathname ) {
setLastPathname ( location . pathname ) ;
setMenuOpen ( false ) ;
setAvatarOpen ( false ) ;
}
const toggleTheme = ( ) => setTheme ( t => t === 'dark' ? 'light' : 'dark' ) ;
const handleLogout = async ( ) => {
await logout ( ) ;
navigate ( '/' ) ;
} ;
const handleLogout = async ( ) => { await logout ( ) ; navigate ( '/' ) ; } ;
const initials = currentUser ? . name
? . split ( ' ' ) . map ( n => n [ 0 ] ) . join ( '' ) . toUpperCase ( ) . slice ( 0 , 2 ) ;
const hasResults = searchResults . projects . length > 0 || searchResults . tasks . length > 0 ;
return (
< div className = { ` app-layout ${ sidebarCollapsed ? ' sidebar-collapsed' : '' } ` } >
{ /* Overlay */ }
< div className = "app-layout" >
{ menuOpen && < div className = "sidebar-overlay" onClick = { ( ) => setMenuOpen ( false ) } / > }
< aside className = { ` sidebar ${ menuOpen ? ' sidebar-open' : '' } ` } >
< div className = "sidebar-logo" >
< img className = "brand-logo brand-logo-sidebar" src = "/fourge-logo .png" alt = "Fourge Branding" / >
< button
className = "sidebar-pin-toggle"
onClick = { ( ) => setSidebarCollapsed ( current => ! current ) }
title = { sidebarCollapsed ? 'Pin sidebar open' : 'Collapse sidebar' }
aria - label = { sidebarCollapsed ? 'Pin sidebar open' : 'Collapse sidebar' }
>
{ sidebarCollapsed ? '› ' : '‹ ' }
< / button >
< img className = "brand-logo brand-logo-sidebar" src = "/logonowordmark .png" alt = "Fourge Branding" / >
< / div >
{ ! sidebarCollapsed && (
currentUser ? . role === 'team'
? < TeamNav onNav = { ( ) => setMenuOpen ( false ) } / >
: currentUser ? . role === 'external'
? < ExternalNav onNav = { ( ) => setMenuOpen ( false ) } / >
: < ClientNav onNav = { ( ) => setMenuOpen ( false ) } / >
) }
{ currentUser ? . role === 'team'
? < TeamNav onNav = { ( ) => setMenuOpen ( false ) } / >
: currentUser ? . role === 'external'
? < ExternalNav onNav = { ( ) => setMenuOpen ( false ) } / >
: < ClientNav onNav = { ( ) => setMenuOpen ( false ) } / >
}
< div className = "sidebar-bottom" >
< NavLink to = "/settings" onClick = { ( ) => setMenuOpen ( false ) } className = { ( { isActive } ) => ` sidebar-link ${ isActive ? ' active' : '' } ` } >
< div className = "sidebar-avatar" style = { { width : 28 , height : 28 , fontSize : 11 , flexShrink : 0 } } > { initials } < / div >
< div className = "sidebar-user-info" >
< div className = "sidebar-user-name" > { currentUser ? . name || 'Set your name' } < / div >
< div className = "sidebar-user-role" > { currentUser ? . role === 'external' ? 'Team' : currentUser ? . role } < / div >
< / div >
< / NavLink >
< div className = "sidebar-bottom-actions" >
{ ! sidebarCollapsed && < button className = "sidebar-link" style = { { flex : 1 } } onClick = { handleLogout } > Sign Out < / button > }
< button
onClick = { toggleTheme }
className = "sidebar-theme-toggle"
title = { theme === 'dark' ? 'Switch to light mode' : 'Switch to dark mode' }
>
{ theme === 'dark' ? '☀' : '☾' }
< / button >
< / div >
< / div >
< / aside >
< div className = "main-wrapper" >
{ /* Mobile top bar inside main wrapper so it sits at the top */ }
< div className = "mobile-topbar" >
< button className = "hamburger" onClick = { ( ) => setMenuOpen ( o => ! o ) } aria - label = "Menu" >
< span / > < span / > < span / >
< / button >
< img className = "brand-logo brand-logo-mobile" src = "/fourge-logo.png" alt = "Fourge Branding" / >
< / div >
< main className = "main-content" >
< div className = "site-header" >
< div >
< div className = "site-header-greeting" > { headerTitle } < / div >
< div className = "site-header-sub" > { headerSubtitle } < / div >
< / div >
< div className = "site-header-right" >
< div className = "site-header-search-wrap" >
{ ! searchExpanded ? (
< button
className = "site-header-search-btn"
onClick = { ( ) => { setSearchExpanded ( true ) ; setTimeout ( ( ) => searchInputRef . current ? . focus ( ) , 50 ) ; } }
aria - label = "Search"
>
< svg viewBox = "0 0 16 16" fill = "none" stroke = "currentColor" strokeWidth = "1.5" strokeLinecap = "round" > < circle cx = "6.5" cy = "6.5" r = "4" / > < line x1 = "10" y1 = "10" x2 = "14" y2 = "14" / > < / svg >
< / button >
) : (
< >
< input
ref = { searchInputRef }
className = "site-header-search"
type = "text"
placeholder = "Search projects & tasks..."
value = { searchQuery }
onChange = { e => setSearchQuery ( e . target . value ) }
onFocus = { ( ) => setSearchOpen ( true ) }
onBlur = { ( ) => setTimeout ( ( ) => { setSearchOpen ( false ) ; setSearchExpanded ( false ) ; setSearchQuery ( '' ) ; } , 150 ) }
onKeyDown = { e => { if ( e . key === 'Escape' ) { setSearchOpen ( false ) ; setSearchExpanded ( false ) ; setSearchQuery ( '' ) ; } } }
/ >
< / >
) }
{ searchOpen && searchQuery . trim ( ) && (
< div className = "site-header-dropdown" >
{ ! hasResults && < div className = "site-header-dropdown-empty" > No results for "{searchQuery}" < / div > }
{ searchResults . projects . length > 0 && (
< >
< div className = "site-header-dropdown-group" > Projects < / div >
{ searchResults . projects . map ( p => (
< div key = { p . id } className = "site-header-dropdown-item" onMouseDown = { ( ) => { navigate ( ` /projects/ ${ p . id } ` ) ; setSearchQuery ( '' ) ; setSearchOpen ( false ) ; setSearchExpanded ( false ) ; } } >
< span className = "nav-icon" style = { { opacity : 0.6 } } > { ICONS . projects } < / span >
{ p . name }
< / div >
) ) }
< / >
) }
{ searchResults . tasks . length > 0 && (
< >
< div className = "site-header-dropdown-group" > Tasks < / div >
{ searchResults . tasks . map ( r => (
< div key = { r . id } className = "site-header-dropdown-item" onMouseDown = { ( ) => { navigate ( ` /requests/ ${ r . id } ` ) ; setSearchQuery ( '' ) ; setSearchOpen ( false ) ; setSearchExpanded ( false ) ; } } >
< span className = "nav-icon" style = { { opacity : 0.6 } } > { ICONS . requests } < / span >
{ r . title }
< / div >
) ) }
< / >
) }
< / div >
) }
< / div >
< button onClick = { toggleTheme } className = "site-header-theme-toggle" title = { theme === 'dark' ? 'Switch to light mode' : 'Switch to dark mode' } >
{ theme === 'dark'
? < svg viewBox = "0 0 16 16" fill = "none" stroke = "currentColor" strokeWidth = "1.5" strokeLinecap = "round" strokeLinejoin = "round" > < circle cx = "8" cy = "8" r = "3" / > < line x1 = "8" y1 = "1" x2 = "8" y2 = "2.5" / > < line x1 = "8" y1 = "13.5" x2 = "8" y2 = "15" / > < line x1 = "1" y1 = "8" x2 = "2.5" y2 = "8" / > < line x1 = "13.5" y1 = "8" x2 = "15" y2 = "8" / > < line x1 = "3" y1 = "3" x2 = "4.1" y2 = "4.1" / > < line x1 = "11.9" y1 = "11.9" x2 = "13" y2 = "13" / > < line x1 = "13" y1 = "3" x2 = "11.9" y2 = "4.1" / > < line x1 = "4.1" y1 = "11.9" x2 = "3" y2 = "13" / > < / svg >
: < svg viewBox = "0 0 16 16" fill = "currentColor" stroke = "none" > < path d = "M14 8.53A6 6 0 1 1 7.47 2 4.67 4.67 0 0 0 14 8.53Z" / > < / svg >
}
< / button >
< div className = "site-header-avatar-wrap" ref = { avatarRef } >
< button className = "site-header-avatar-btn" onClick = { ( ) => setAvatarOpen ( o => ! o ) } aria - label = "Account menu" >
< svg width = "24" height = "24" viewBox = "0 0 24 24" fill = "currentColor" > < circle cx = "12" cy = "8" r = "4" / > < path d = "M4 20c0-4.4 3.6-7 8-7s8 2.6 8 7" fill = "currentColor" / > < / svg >
< / button >
{ avatarOpen && (
< div className = "site-header-avatar-menu" >
< button className = "site-header-avatar-item" onClick = { ( ) => { navigate ( '/profile' ) ; setAvatarOpen ( false ) ; } } > Settings < / button >
< div className = "site-header-avatar-divider" / >
< button className = "site-header-avatar-item" onClick = { handleLogout } > Sign Out < / button >
< / div >
) }
< / div >
< / div >
< / div >
{ children }
< / main >
< / div >