feat(02-01): create MoreDrawer component with Sheet, 3 sections, sign out
- shadcn Sheet side=right with controlled open/onOpenChange props - Section 1: Mobile sections (Engagement in-shell route) - Section 2: Full site links with ExternalLink hints (Quotes, Config Items, Backup Status, Ticket Digest, Admin/Sync) - Section 3: Account — user initials display + sign out via Better Auth - Implements DRAWER-01..05 requirements
This commit is contained in:
parent
08467494bf
commit
2ef843766d
1 changed files with 166 additions and 0 deletions
166
components/mobile/MoreDrawer.tsx
Normal file
166
components/mobile/MoreDrawer.tsx
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
'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>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue