File size: 5,661 Bytes
f6d0a88 | 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 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | 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>
)
}
|