feat(02-01): create BottomNav component with 4 tabs + More button
- Fixed bottom bar with border-t bg-background pb-safe - 4 tabs: Dashboard, Tickets, Finance, Analyzer linked to /mobile/* routes - Active state via pathname.startsWith(href), text-primary when active - More cell triggers onMoreClick prop (parent controls drawer state) - max-w-lg mx-auto gutter alignment, h-16 (64px) touch targets - Icons: LayoutDashboard, Ticket, DollarSign, Sparkles, Menu - Implements SHELL-06, NAV-01..03, DRAWER-01 requirements
This commit is contained in:
parent
05611cd80e
commit
01943a6dc9
1 changed files with 75 additions and 0 deletions
75
components/mobile/BottomNav.tsx
Normal file
75
components/mobile/BottomNav.tsx
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
'use client';
|
||||
|
||||
/* BottomNav — phase 02 (SHELL-06, NAV-01..03, DRAWER-01).
|
||||
*
|
||||
* Fixed bottom bar with five cells:
|
||||
* - Dashboard (LayoutDashboard) -> /mobile/dashboard
|
||||
* - Tickets (Ticket) -> /mobile/tickets
|
||||
* - Finance (DollarSign) -> /mobile/finance
|
||||
* - Analyzer (Sparkles) -> /mobile/analyzer
|
||||
* - More (Menu) -> opens the MoreDrawer (parent state)
|
||||
*
|
||||
* Active tab detected via pathname.startsWith(href). Active = text-primary,
|
||||
* inactive = text-muted-foreground. */
|
||||
|
||||
import Link from 'next/link';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import {
|
||||
LayoutDashboard,
|
||||
Ticket,
|
||||
DollarSign,
|
||||
Sparkles,
|
||||
Menu,
|
||||
} from 'lucide-react';
|
||||
|
||||
const TABS = [
|
||||
{ href: '/mobile/dashboard', label: 'Dashboard', icon: LayoutDashboard },
|
||||
{ href: '/mobile/tickets', label: 'Tickets', icon: Ticket },
|
||||
{ href: '/mobile/finance', label: 'Finance', icon: DollarSign },
|
||||
{ href: '/mobile/analyzer', label: 'Analyzer', icon: Sparkles },
|
||||
] as const;
|
||||
|
||||
interface BottomNavProps {
|
||||
onMoreClick: () => void;
|
||||
}
|
||||
|
||||
export function BottomNav({ onMoreClick }: BottomNavProps) {
|
||||
const pathname = usePathname();
|
||||
|
||||
return (
|
||||
<nav
|
||||
aria-label="Primary"
|
||||
className="fixed bottom-0 left-0 right-0 z-30 border-t bg-background pb-safe"
|
||||
>
|
||||
<div className="max-w-lg mx-auto flex h-16">
|
||||
{TABS.map(({ href, label, icon: Icon }) => {
|
||||
const active = pathname?.startsWith(href) ?? false;
|
||||
return (
|
||||
<Link
|
||||
key={href}
|
||||
href={href}
|
||||
aria-current={active ? 'page' : undefined}
|
||||
className={`flex-1 flex flex-col items-center justify-center gap-0.5 text-[11px] transition-colors ${
|
||||
active
|
||||
? 'text-primary'
|
||||
: 'text-muted-foreground hover:text-foreground'
|
||||
}`}
|
||||
>
|
||||
<Icon className="w-5 h-5" aria-hidden="true" />
|
||||
<span>{label}</span>
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
<button
|
||||
type="button"
|
||||
onClick={onMoreClick}
|
||||
aria-label="Open menu"
|
||||
className="flex-1 flex flex-col items-center justify-center gap-0.5 text-[11px] text-muted-foreground hover:text-foreground transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
||||
>
|
||||
<Menu className="w-5 h-5" aria-hidden="true" />
|
||||
<span>More</span>
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue