The soft reset to 77073ba inadvertently staged deletions of all phase 2
and 3 artifacts. This commit restores them from their source commits so
subsequent task commits build on the complete prior-phase foundation:
- components/mobile/{BottomNav,HeaderBar,KpiCardMobile,MoreDrawer,NeedsAttentionStrip,WorkerStatusRow}
- app/mobile/layout.tsx, dashboard/page.tsx, analyzer/page.tsx
- app/api/mobile/dashboard/route.ts
- All .planning/** files from phases 01-04
- CLAUDE.md, app/layout.tsx, app/styles/brand.css, public/manifest.json
166 lines
5.7 KiB
TypeScript
166 lines
5.7 KiB
TypeScript
'use client';
|
|
|
|
/* MoreDrawer — phase 02 (DRAWER-01..05).
|
|
*
|
|
* shadcn Sheet (side="right") with three top-to-bottom sections:
|
|
* 1. Mobile sections — Engagement (in-shell route, no ExternalLink hint)
|
|
* 2. Full site — desktop-only routes, each with ExternalLink hint
|
|
* 3. Account — current user (read-only) + Sign out
|
|
*
|
|
* Open state is controlled by the parent so the header avatar AND the
|
|
* bottom-nav More cell can both trigger this single drawer. */
|
|
|
|
import Link from 'next/link';
|
|
import { useRouter } from 'next/navigation';
|
|
import {
|
|
ExternalLink,
|
|
FileText,
|
|
Server,
|
|
HardDrive,
|
|
BarChart3,
|
|
Settings,
|
|
Users,
|
|
LogOut,
|
|
} from 'lucide-react';
|
|
import {
|
|
Sheet,
|
|
SheetContent,
|
|
SheetHeader,
|
|
SheetTitle,
|
|
SheetDescription,
|
|
SheetClose,
|
|
} from '@/components/ui/sheet';
|
|
import { useSession, signOut } from '@/lib/auth-client';
|
|
import { toast } from 'sonner';
|
|
|
|
const MOBILE_SECTIONS = [
|
|
{ href: '/mobile/engagement', label: 'Engagement', icon: Users },
|
|
];
|
|
|
|
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: '/admin/ticket-digest', label: 'Ticket Digest', icon: BarChart3 },
|
|
{ href: '/admin/sync', label: 'Admin / Sync', icon: Settings },
|
|
];
|
|
|
|
interface MoreDrawerProps {
|
|
open: boolean;
|
|
onOpenChange: (open: boolean) => void;
|
|
}
|
|
|
|
export function MoreDrawer({ open, onOpenChange }: MoreDrawerProps) {
|
|
const router = useRouter();
|
|
const { data: session } = useSession();
|
|
const user = session?.user as
|
|
| { name?: string; email?: string; image?: string | null }
|
|
| undefined;
|
|
|
|
const initials = (user?.name ?? user?.email ?? '?')
|
|
.split(/[\s@]/)
|
|
.filter(Boolean)
|
|
.slice(0, 2)
|
|
.map((p) => p[0]?.toUpperCase())
|
|
.join('');
|
|
|
|
async function handleSignOut() {
|
|
try {
|
|
await signOut();
|
|
router.push('/auth/sign-in');
|
|
} catch (e) {
|
|
toast.error('Sign out failed');
|
|
console.error('Sign out failed:', e);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<Sheet open={open} onOpenChange={onOpenChange}>
|
|
<SheetContent side="right" className="w-80 sm:max-w-sm flex flex-col">
|
|
<SheetHeader>
|
|
<SheetTitle>Menu</SheetTitle>
|
|
<SheetDescription className="sr-only">
|
|
Navigation, full-site links, and account actions.
|
|
</SheetDescription>
|
|
</SheetHeader>
|
|
|
|
{/* Section 1: Mobile sections (DRAWER-03) */}
|
|
<div className="px-4">
|
|
<p className="text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-2">
|
|
Mobile sections
|
|
</p>
|
|
<div className="rounded-2xl border divide-y overflow-hidden">
|
|
{MOBILE_SECTIONS.map(({ href, label, icon: Icon }) => (
|
|
<SheetClose asChild key={href}>
|
|
<Link
|
|
href={href}
|
|
className="flex items-center gap-3 px-4 py-3 hover:bg-accent transition-colors"
|
|
>
|
|
<Icon className="w-4 h-4 text-muted-foreground shrink-0" />
|
|
<span className="text-sm flex-1">{label}</span>
|
|
</Link>
|
|
</SheetClose>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Section 2: Full site (DRAWER-04) */}
|
|
<div className="px-4">
|
|
<p className="text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-2">
|
|
Full site
|
|
</p>
|
|
<div className="rounded-2xl border divide-y overflow-hidden">
|
|
{DESKTOP_LINKS.map(({ href, label, icon: Icon }) => (
|
|
<SheetClose asChild key={href}>
|
|
<Link
|
|
href={href}
|
|
className="flex items-center gap-3 px-4 py-3 hover:bg-accent transition-colors"
|
|
>
|
|
<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>
|
|
</SheetClose>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Section 3: Account (DRAWER-05) */}
|
|
<div className="px-4 mt-auto pb-safe">
|
|
<p className="text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-2">
|
|
Account
|
|
</p>
|
|
<div className="rounded-2xl border overflow-hidden">
|
|
{user && (
|
|
<div className="flex items-center gap-3 px-4 py-3 border-b">
|
|
<span className="inline-flex h-8 w-8 items-center justify-center rounded-full bg-primary/15 text-primary text-xs font-semibold shrink-0">
|
|
{initials}
|
|
</span>
|
|
<div className="flex-1 min-w-0">
|
|
{user.name && (
|
|
<p className="text-sm font-medium leading-tight truncate">
|
|
{user.name}
|
|
</p>
|
|
)}
|
|
{user.email && (
|
|
<p className="text-xs text-muted-foreground truncate" title={user.email}>
|
|
{user.email}
|
|
</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)}
|
|
<button
|
|
type="button"
|
|
onClick={handleSignOut}
|
|
className="w-full flex items-center gap-3 px-4 py-3 text-destructive hover:bg-destructive/10 transition-colors"
|
|
>
|
|
<LogOut className="w-4 h-4 shrink-0" />
|
|
<span className="text-sm">Sign out</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</SheetContent>
|
|
</Sheet>
|
|
);
|
|
}
|