wulf-pulse/app/mobile/nav/page.tsx
lorentz 6268d1fe37 feat(04-01): rewrite /api/mobile/tickets with cursor pagination and exported interfaces
- 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
2026-05-03 17:59:54 -04:00

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>
);
}