Spaces:
Running
Running
File size: 4,225 Bytes
5a264f5 | 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 106 107 108 109 110 111 112 113 114 | import { useState } from 'react'
import { Outlet, NavLink, useNavigate, Link } from 'react-router-dom'
import { useAuth } from '../context/AuthContext'
import { useTheme } from '../context/ThemeContext'
const NAV_MAIN = [
{ to: '/app/dashboard', icon: 'β', label: 'Dashboard' },
{ to: '/app/assess', icon: 'β', label: 'New Assessment' },
{ to: '/app/history', icon: 'β·', label: 'History' },
]
const NAV_WELLNESS = [
{ to: '/app/breathe', icon: 'π«', label: 'Breathe Hub' },
{ to: '/app/gratitude', icon: 'βοΈ', label: 'Gratitude' },
{ to: '/app/todo', icon: 'β
', label: 'Daily To-Do' },
]
export default function Layout() {
const { user, logout } = useAuth()
const { theme, toggle } = useTheme()
const navigate = useNavigate()
const [open, setOpen] = useState(false) // mobile sidebar
async function handleLogout() {
await logout()
navigate('/auth')
}
const initials = user?.username?.slice(0, 2).toUpperCase() || 'U'
return (
<div className="app-shell">
{/* Mobile top bar */}
<header className="mobile-bar">
<button className="hamburger" onClick={() => setOpen(o => !o)}>β°</button>
<Link to="/app/breathe" className="mobile-brand-link">
<img src="/logo.svg" alt="BREATHE" className="mobile-brand-logo" />
</Link>
<button className="mobile-theme-toggle" onClick={toggle} aria-label="Toggle theme">
{theme === 'dark' ? 'βοΈ' : 'π'}
</button>
</header>
{/* Backdrop (mobile) */}
{open && <div className="sidebar-backdrop" onClick={() => setOpen(false)} />}
<aside className={`sidebar ${open ? 'sidebar--open' : ''}`}>
<div className="sidebar-top">
<div className="sidebar-brand">
<Link to="/app/breathe" className="sidebar-brand-link" onClick={() => setOpen(false)}>
<img src="/logo.svg" alt="BREATHE" className="brand-logo" />
<div className="brand-sub">Stress Intelligence</div>
</Link>
</div>
<nav className="sidebar-nav">
<div className="nav-section-label">Overview</div>
{NAV_MAIN.map(n => (
<NavLink
key={n.to}
to={n.to}
className={({ isActive }) => `nav-link ${isActive ? 'nav-link--active' : ''}`}
onClick={() => setOpen(false)}
>
<span className="nav-icon">{n.icon}</span>
<span>{n.label}</span>
</NavLink>
))}
<div className="nav-section-label" style={{ marginTop: '16px' }}>Wellness</div>
{NAV_WELLNESS.map(n => (
<NavLink
key={n.to}
to={n.to}
className={({ isActive }) => `nav-link ${isActive ? 'nav-link--active' : ''}`}
onClick={() => setOpen(false)}
>
<span className="nav-icon">{n.icon}</span>
<span>{n.label}</span>
</NavLink>
))}
</nav>
</div>
<div className="sidebar-bottom">
<div className="user-chip">
<Link to="/app/profile" className="user-chip-link" onClick={() => setOpen(false)}>
{user?.avatar
? <img src={user.avatar} alt="avatar" className="user-avatar user-avatar--img" />
: <div className="user-avatar">{initials}</div>
}
<div className="user-meta">
<div className="user-name">{user?.display_name || user?.username}</div>
<div className="user-email">{user?.email}</div>
</div>
</Link>
</div>
<button className="theme-toggle" onClick={toggle}>
<span className="theme-toggle-icon">{theme === 'dark' ? 'βοΈ' : 'π'}</span>
{theme === 'dark' ? 'Light mode' : 'Dark mode'}
</button>
<button className="logout-btn" onClick={handleLogout}>
β© Log out
</button>
</div>
</aside>
<main className="main-area">
<Outlet />
</main>
</div>
)
}
|