feat(02-01): create HeaderBar component with brand, Bell placeholder, avatar trigger

- Sticky header with bg-background/95 backdrop-blur and border-b
- WulfMark + Pulse wordmark linked to /mobile/dashboard
- Bell icon placeholder (aria-label=Notifications, empty onClick per SHELL-03)
- Avatar circle (h-7 w-7) triggers drawer via onAvatarClick prop
- pt-safe applied for notch/dynamic-island clearance (PWA-04)
- Implements SHELL-02..04 requirements
This commit is contained in:
lorentz 2026-05-03 16:08:00 -04:00
parent 2ef843766d
commit 05611cd80e

View file

@ -0,0 +1,72 @@
'use client';
/* HeaderBar phase 02 (SHELL-02..04).
*
* Sticky top bar inside the /mobile shell. Three slots:
* left: WulfMark + "Pulse" wordmark, linked to /mobile/dashboard
* right: Bell icon button (placeholder, aria-label="Notifications")
* right: compact avatar circle opens the More drawer (parent owns state)
*
* No page title in the header pages render their own H1. */
import Link from 'next/link';
import { Bell } from 'lucide-react';
import { WulfMark } from '@/components/branding/wulf-mark';
import { useSession } from '@/lib/auth-client';
interface HeaderBarProps {
onAvatarClick: () => void;
}
export function HeaderBar({ onAvatarClick }: HeaderBarProps) {
const { data: session } = useSession();
const user = session?.user as
| { name?: string; email?: string }
| undefined;
const initials = (user?.name ?? user?.email ?? '?')
.split(/[\s@]/)
.filter(Boolean)
.slice(0, 2)
.map((p) => p[0]?.toUpperCase())
.join('');
return (
<header className="sticky top-0 z-30 bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/80 border-b pt-safe">
<div className="flex items-center justify-between px-4 h-14">
{/* Left: brand mark + wordmark, linked to /mobile/dashboard */}
<Link
href="/mobile/dashboard"
className="flex items-center gap-2 -ml-1 px-1 rounded-md hover:bg-accent/50 transition-colors"
aria-label="Pulse — go to Dashboard"
>
<WulfMark variant="mark" className="h-6 w-auto" />
<span className="font-bold text-base tracking-tight">Pulse</span>
</Link>
{/* Right: Bell placeholder, then avatar trigger */}
<div className="flex items-center gap-1">
<button
type="button"
onClick={() => { /* SHELL-03: placeholder — no menu, no badge */ }}
aria-label="Notifications"
className="inline-flex items-center justify-center h-9 w-9 rounded-md hover:bg-accent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
>
<Bell className="h-5 w-5" />
</button>
<button
type="button"
onClick={onAvatarClick}
aria-label="Open menu"
className="inline-flex items-center justify-center h-9 w-9 rounded-md hover:bg-accent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
>
<span className="inline-flex h-7 w-7 items-center justify-center rounded-full bg-primary/15 text-primary text-[11px] font-semibold">
{initials}
</span>
</button>
</div>
</div>
</header>
);
}