- Replace page/offset pagination with opaque base64 cursor (last_activity_date, id) - Export MobileTicket and MobileTicketListResponse interfaces for Plan 02 import - Add requireAuth() gate (T-04-03: legacy route lacked auth) - Server-side limit cap at 25 rows (D-11, T-04-04) - Default status filter [1,8,7] when no status param supplied (matches legacy t.status != 5) - Preserve getMobileCompanyFilter() helper verbatim - Support status/priority arrays, queue, mine, and search filters - Cursor seek predicate: (last_activity_date, id) < (cursor) for stable keyset order
91 lines
4.1 KiB
TypeScript
91 lines
4.1 KiB
TypeScript
'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>
|
|
);
|
|
}
|