feat: mobile nav page + suppress desktop header on /mobile/* routes
- AppNavigation returns null on /mobile/* (no more horizontal scroll) - Mobile header: 'Pulse' title (links home) + Menu icon (links to /mobile/nav) - /mobile/nav: full-screen nav page with touch-friendly cards - Mobile Views: Dashboard, Tickets, Finance (large icon cards) - Full Site: Quotes, Config Items, Backup, Engagement, Ticket Digest, Admin - Sign out button - Bottom tab bar unchanged (Dashboard / Tickets / Finance)
This commit is contained in:
parent
44847ccf21
commit
fea62382de
3 changed files with 99 additions and 3 deletions
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import Link from 'next/link';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import { LayoutDashboard, Ticket, DollarSign } from 'lucide-react';
|
||||
import { LayoutDashboard, Ticket, DollarSign, Menu } from 'lucide-react';
|
||||
|
||||
const NAV = [
|
||||
{ href: '/mobile/dashboard', label: 'Dashboard', icon: LayoutDashboard },
|
||||
|
|
@ -17,8 +17,10 @@ export default function MobileLayout({ children }: { children: React.ReactNode }
|
|||
<div className="flex flex-col min-h-screen bg-background max-w-lg mx-auto">
|
||||
{/* Top bar */}
|
||||
<header className="sticky top-0 z-20 bg-background border-b px-4 py-3 flex items-center justify-between">
|
||||
<span className="font-bold text-lg tracking-tight">Pulse</span>
|
||||
<span className="text-xs text-muted-foreground">Wulf Consulting</span>
|
||||
<Link href="/mobile" className="font-bold text-lg tracking-tight">Pulse</Link>
|
||||
<Link href="/mobile/nav" className="p-1.5 rounded-lg hover:bg-accent transition-colors" aria-label="Navigation menu">
|
||||
<Menu className="w-5 h-5" />
|
||||
</Link>
|
||||
</header>
|
||||
|
||||
{/* Page content */}
|
||||
|
|
|
|||
91
app/mobile/nav/page.tsx
Normal file
91
app/mobile/nav/page.tsx
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import {
|
||||
LayoutDashboard, Ticket, DollarSign, ArrowLeft,
|
||||
ExternalLink, Settings, LogOut, ChevronRight,
|
||||
FileText, Server, HardDrive, Users, BarChart3,
|
||||
} from 'lucide-react';
|
||||
import { signOut } from '@/lib/auth-client';
|
||||
|
||||
const MAIN_SECTIONS = [
|
||||
{ href: '/mobile/dashboard', label: 'Dashboard', icon: LayoutDashboard, description: 'Overview & stats', color: 'bg-blue-500/10 text-blue-600 dark:text-blue-400' },
|
||||
{ href: '/mobile/tickets', label: 'Tickets', icon: Ticket, description: 'Open service tickets', color: 'bg-emerald-500/10 text-emerald-600 dark:text-emerald-400' },
|
||||
{ href: '/mobile/finance', label: 'Finance', icon: DollarSign, description: 'AR, invoices & payments', color: 'bg-violet-500/10 text-violet-600 dark:text-violet-400' },
|
||||
];
|
||||
|
||||
const DESKTOP_LINKS = [
|
||||
{ href: '/quotes', label: 'Quotes', icon: FileText },
|
||||
{ href: '/configuration-items', label: 'Configuration Items', icon: Server },
|
||||
{ href: '/backup-status', label: 'Backup Status', icon: HardDrive },
|
||||
{ href: '/engagement', label: 'Engagement', icon: Users },
|
||||
{ href: '/admin/ticket-digest', label: 'Ticket Digest', icon: BarChart3 },
|
||||
{ href: '/admin/sync', label: 'Admin / Sync', icon: Settings },
|
||||
];
|
||||
|
||||
export default function MobileNav() {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<div className="p-4 space-y-6 pb-24">
|
||||
{/* Header */}
|
||||
<div className="flex items-center gap-3">
|
||||
<button
|
||||
onClick={() => router.back()}
|
||||
className="p-2 rounded-xl hover:bg-accent transition-colors"
|
||||
aria-label="Go back"
|
||||
>
|
||||
<ArrowLeft className="w-5 h-5" />
|
||||
</button>
|
||||
<h1 className="text-lg font-bold">Navigation</h1>
|
||||
</div>
|
||||
|
||||
{/* Main mobile sections */}
|
||||
<div>
|
||||
<p className="text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-3">Mobile Views</p>
|
||||
<div className="grid grid-cols-1 gap-2">
|
||||
{MAIN_SECTIONS.map(({ href, label, icon: Icon, description, color }) => (
|
||||
<Link key={href} href={href}
|
||||
className="flex items-center gap-4 p-4 rounded-2xl border bg-card hover:bg-accent transition-colors active:scale-[0.98]">
|
||||
<div className={`w-11 h-11 rounded-xl flex items-center justify-center shrink-0 ${color.split(' ')[0]}`}>
|
||||
<Icon className={`w-5 h-5 ${color.split(' ').slice(1).join(' ')}`} />
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-sm font-semibold">{label}</p>
|
||||
<p className="text-xs text-muted-foreground">{description}</p>
|
||||
</div>
|
||||
<ChevronRight className="w-4 h-4 text-muted-foreground shrink-0" />
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Desktop links */}
|
||||
<div>
|
||||
<p className="text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-3">Full Site</p>
|
||||
<div className="rounded-2xl border divide-y overflow-hidden">
|
||||
{DESKTOP_LINKS.map(({ href, label, icon: Icon }) => (
|
||||
<Link key={href} href={href}
|
||||
className="flex items-center gap-3 px-4 py-3.5 hover:bg-accent transition-colors active:bg-accent">
|
||||
<Icon className="w-4 h-4 text-muted-foreground shrink-0" />
|
||||
<span className="text-sm flex-1">{label}</span>
|
||||
<ExternalLink className="w-3.5 h-3.5 text-muted-foreground shrink-0" />
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Sign out */}
|
||||
<div className="rounded-2xl border overflow-hidden">
|
||||
<button
|
||||
onClick={() => signOut().then(() => router.push('/auth/sign-in'))}
|
||||
className="w-full flex items-center gap-3 px-4 py-3.5 hover:bg-destructive/10 text-destructive transition-colors"
|
||||
>
|
||||
<LogOut className="w-4 h-4 shrink-0" />
|
||||
<span className="text-sm">Sign out</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -195,6 +195,9 @@ export function AppNavigation() {
|
|||
const userRole = (session?.user as any)?.role || 'user';
|
||||
const isSuperAdmin = userRole === 'super-admin';
|
||||
|
||||
// Mobile routes have their own nav — suppress desktop header
|
||||
if (pathname.startsWith('/mobile')) return null;
|
||||
|
||||
const isActive = (href?: string) => {
|
||||
if (!href) return false;
|
||||
return pathname === href || pathname.startsWith(href + '/');
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue