Aria / frontend /src /components /AppLayout.tsx
teganmosi
Deploy Aria application with fixes for database, TTS, and WebSocket (LFS tracking applied)
f6d0a88
Raw
History Blame Contribute Delete
5.66 kB
import { Outlet, NavLink, useLocation } from 'react-router-dom'
import { AnimatePresence, motion } from 'framer-motion'
import {
House,
MessageSquare,
BookOpen,
BookText,
Calendar,
Settings,
Heart,
Menu,
X
} from 'lucide-react'
import { ThemeToggle } from '../components/ui/SharedComponents'
import { useState, useEffect } from 'react'
export const AppLayout = () => {
const location = useLocation()
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
useEffect(() => {
setMobileMenuOpen(false)
}, [location.pathname])
const navItems = [
{ path: '/app/home', icon: <House size={20} />, label: 'Home' },
{ path: '/app/ai-chat', icon: <MessageSquare size={20} />, label: 'Chat' },
{ path: '/app/bible', icon: <BookText size={20} />, label: 'Bible' },
{ path: '/app/bible-study', icon: <BookOpen size={20} />, label: 'Study' },
{ path: '/app/emotional-support', icon: <Heart size={20} />, label: 'Support' },
]
const moreNavItems = [
{ path: '/app/notes', icon: <BookText size={20} />, label: 'Notes' },
{ path: '/app/devotion', icon: <Calendar size={20} />, label: 'Devotions' },
]
return (
<div className="flex h-screen w-screen bg-[var(--bg-main)] text-[var(--text-main)] overflow-hidden flex-col">
{/* Header */}
<header className="h-16 flex items-center justify-between px-5 border-b border-[var(--border-color)] bg-[var(--glass-bg)] backdrop-blur-[10px] z-[100]">
<div className="flex items-center gap-4">
<button
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
className="bg-transparent border-0 text-[var(--text-main)] cursor-pointer p-2"
>
{mobileMenuOpen ? <X size={24} /> : <Menu size={24} />}
</button>
<h2 className="font-serif" style={{ fontStyle: 'italic', fontSize: '1.5rem', color: 'var(--text-main)', margin: 0 }}>Aria</h2>
</div>
<div className="flex items-center gap-4">
<ThemeToggle />
<NavLink to="/app/profile">
<div className="w-8 h-8 rounded-full bg-[var(--bg-alt)] overflow-hidden">
<Settings size={18} style={{ margin: '7px', color: 'var(--text-secondary)' }} />
</div>
</NavLink>
</div>
</header>
{/* Mobile Slide-out Menu */}
{mobileMenuOpen && (
<>
<div
className="mobile-only fixed inset-0 bg-black/50 z-[998] backdrop-blur-[4px]"
onClick={() => setMobileMenuOpen(false)}
/>
<nav
className="mobile-only fixed top-0 left-0 bottom-0 w-[280px] bg-[var(--bg-card)] z-[999] p-6 flex flex-col gap-2 shadow-[4px_0_20px_rgba(0,0,0,0.1)] overflow-y-auto"
>
<div className="flex justify-between items-center mb-8">
<h3 style={{ fontSize: '0.75rem', letterSpacing: '0.15em', color: 'var(--text-muted)', margin: 0 }}>MENU</h3>
<button onClick={() => setMobileMenuOpen(false)} className="bg-transparent border-0 text-[var(--text-muted)]">
<X size={20} />
</button>
</div>
{navItems.map((item) => (
<NavLink
key={item.path}
to={item.path}
style={({ isActive }) => ({
display: 'flex',
alignItems: 'center',
gap: '1rem',
padding: '1rem',
color: isActive ? 'var(--text-main)' : 'var(--text-secondary)',
textDecoration: 'none',
fontWeight: isActive ? 700 : 500,
fontSize: '0.95rem',
borderRadius: '12px',
background: isActive ? 'var(--bg-alt)' : 'transparent',
transition: 'all 0.2s ease'
})}
>
{item.icon}
{item.label}
</NavLink>
))}
<div style={{ marginTop: '1.5rem', paddingTop: '1.5rem', borderTop: '1px solid var(--border-color)' }}>
<p style={{ fontSize: '0.7rem', fontWeight: 800, color: 'var(--text-muted)', letterSpacing: '0.1em', marginBottom: '1rem' }}>MORE</p>
{moreNavItems.map((item) => (
<NavLink
key={item.path}
to={item.path}
style={({ isActive }) => ({
display: 'flex',
alignItems: 'center',
gap: '1rem',
padding: '1rem',
color: isActive ? 'var(--text-main)' : 'var(--text-secondary)',
textDecoration: 'none',
fontWeight: isActive ? 700 : 500,
fontSize: '0.95rem',
borderRadius: '12px',
background: isActive ? 'var(--bg-alt)' : 'transparent',
transition: 'all 0.2s ease'
})}
>
{item.icon}
{item.label}
</NavLink>
))}
</div>
</nav>
</>
)}
{/* Main Content */}
<main className="flex-1 relative overflow-y-auto bg-[var(--bg-main)]">
<AnimatePresence mode="wait" initial={false}>
<motion.div
key={location.pathname}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.25, ease: 'easeInOut' }}
>
<Outlet />
</motion.div>
</AnimatePresence>
</main>
</div>
)
}