Spaces:
Sleeping
Sleeping
| import { Link, useLocation, useNavigate } from "react-router-dom"; | |
| import { Activity, Users, AlertTriangle, LogOut, Brain } from "lucide-react"; | |
| import { useAuth } from "@/contexts/AuthContext"; | |
| import { useProfile } from "@/hooks/useProfile"; | |
| import { XP_PER_LEVEL, xpInLevel, isNightMode } from "@/lib/engine"; | |
| import { Button } from "@/components/ui/button"; | |
| const tabs = [ | |
| { to: "/", label: "Dashboard", icon: Activity }, | |
| { to: "/social", label: "Social", icon: Users }, | |
| { to: "/emergency", label: "Emergency", icon: AlertTriangle }, | |
| ]; | |
| export default function AppHeader() { | |
| const location = useLocation(); | |
| const navigate = useNavigate(); | |
| const { user, signOut } = useAuth(); | |
| const { profile } = useProfile(); | |
| const xp = profile?.total_xp ?? 0; | |
| const level = profile?.level ?? 1; | |
| const inLevel = xpInLevel(xp); | |
| const pct = Math.min(100, (inLevel / XP_PER_LEVEL) * 100); | |
| return ( | |
| <> | |
| {isNightMode() && ( | |
| <div className="bg-gradient-danger/10 border-b border-warning/30 text-warning text-xs text-center py-2 font-medium"> | |
| 🌙 Blue Light Alert — past 10pm. Wind down to protect tomorrow's focus. | |
| </div> | |
| )} | |
| <header className="sticky top-0 z-40 glass-strong border-b border-border/50"> | |
| <div className="container flex items-center justify-between gap-4 py-3"> | |
| <Link to="/" className="flex items-center gap-2 group"> | |
| <div className="relative"> | |
| <Brain className="h-7 w-7 text-primary" /> | |
| <div className="absolute inset-0 blur-lg bg-primary/40 group-hover:bg-primary/60 transition-all" /> | |
| </div> | |
| <span className="font-display text-xl font-bold tracking-tight"> | |
| Distract<span className="text-gradient-emerald">IQ</span> | |
| </span> | |
| </Link> | |
| <nav className="hidden md:flex items-center gap-1 glass rounded-full p-1"> | |
| {tabs.map((t) => { | |
| const active = location.pathname === t.to; | |
| const Icon = t.icon; | |
| return ( | |
| <Link | |
| key={t.to} | |
| to={t.to} | |
| className={`flex items-center gap-2 px-4 py-1.5 rounded-full text-sm font-medium transition-all ${ | |
| active | |
| ? "bg-primary text-primary-foreground glow-emerald" | |
| : "text-muted-foreground hover:text-foreground" | |
| }`} | |
| > | |
| <Icon className="h-4 w-4" /> | |
| {t.label} | |
| </Link> | |
| ); | |
| })} | |
| </nav> | |
| <div className="flex items-center gap-3"> | |
| {user && profile && ( | |
| <div className="hidden sm:flex flex-col items-end min-w-[140px]"> | |
| <div className="flex items-center gap-2 text-xs"> | |
| <span className="font-mono text-primary">L{level}</span> | |
| <span className="text-muted-foreground">{profile.username}</span> | |
| </div> | |
| <div className="w-32 h-1.5 bg-muted rounded-full overflow-hidden mt-1"> | |
| <div | |
| className="h-full bg-gradient-emerald transition-all duration-500" | |
| style={{ width: `${pct}%` }} | |
| /> | |
| </div> | |
| <span className="text-[10px] text-muted-foreground mt-0.5 font-mono"> | |
| {inLevel}/{XP_PER_LEVEL} XP | |
| </span> | |
| </div> | |
| )} | |
| {user ? ( | |
| <Button variant="ghost" size="icon" onClick={signOut} aria-label="Sign out"> | |
| <LogOut className="h-4 w-4" /> | |
| </Button> | |
| ) : ( | |
| <Button onClick={() => navigate("/auth")} variant="default" size="sm"> | |
| Sign in | |
| </Button> | |
| )} | |
| </div> | |
| </div> | |
| {/* Mobile nav */} | |
| <nav className="md:hidden flex items-center justify-around border-t border-border/40 py-2"> | |
| {tabs.map((t) => { | |
| const active = location.pathname === t.to; | |
| const Icon = t.icon; | |
| return ( | |
| <Link | |
| key={t.to} | |
| to={t.to} | |
| className={`flex flex-col items-center gap-1 text-xs ${active ? "text-primary" : "text-muted-foreground"}`} | |
| > | |
| <Icon className="h-4 w-4" /> | |
| {t.label} | |
| </Link> | |
| ); | |
| })} | |
| </nav> | |
| </header> | |
| </> | |
| ); | |
| } | |