Files
IdP/apps/frontend/components/id/user-menu.tsx
2026-06-25 16:01:41 +03:00

131 lines
5.3 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client';
import Link from 'next/link';
import { useRouter } from 'next/navigation';
import {
ChevronRight,
FileText,
Heart,
LockKeyhole,
LogOut,
ShieldCheck,
Smartphone,
UserRound
} from 'lucide-react';
import { AdminBadge } from '@/components/id/admin-badge';
import { UserAvatar } from '@/components/id/user-avatar';
import { useAuth } from '@/components/id/auth-provider';
import { getAdminLandingPath } from '@/lib/admin-access';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import { cn } from '@/lib/utils';
function maskPhone(phone?: string | null) {
if (!phone) return null;
const digits = phone.replace(/\D/g, '');
if (digits.length < 10) return phone;
return `+${digits.slice(0, 1)} ${digits.slice(1, 4)} ***-**-${digits.slice(-2)}`;
}
const menuItems = [
{ href: '/data', label: 'Личные данные', subtitle: 'ФИО, день рождения, пол', icon: UserRound },
{ href: '/security', label: 'Телефон и безопасность', subtitle: 'PIN, пароль, устройства', icon: Smartphone },
{ href: '/documents', label: 'Документы', subtitle: 'Паспорт, права, загран', icon: FileText },
{ href: '/family', label: 'Семья', subtitle: 'Участники и доступ', icon: Heart },
{ href: '/security', label: 'Защита аккаунта', subtitle: 'Способы входа и сессии', icon: LockKeyhole }
];
export function UserMenu({ className }: { className?: string }) {
const router = useRouter();
const { user, token, logout } = useAuth();
if (!user) return null;
const contactLine = [maskPhone(user.phone), user.username ?? user.email].filter(Boolean).join(' · ');
return (
<Popover>
<PopoverTrigger asChild>
<button
type="button"
className={cn('flex items-center gap-2 rounded-full border border-[#eceef4] bg-white py-1 pl-1 pr-3 shadow-sm transition hover:bg-[#fafbfd]', className)}
aria-label="Меню пользователя"
>
<UserAvatar
userId={user.id}
displayName={user.displayName}
hasAvatar={user.hasAvatar}
token={token}
isVerified={user.isVerified}
verificationIcon={user.verificationIcon}
className="h-9 w-9"
badgeSize="xs"
/>
<span className="hidden max-w-[120px] truncate text-sm font-medium md:inline">{user.displayName}</span>
</button>
</PopoverTrigger>
<PopoverContent align="end" className="w-[min(92vw,360px)] rounded-[28px] border-[#eceef4] p-0 shadow-2xl">
<div className="border-b border-[#eceef4] px-5 pb-5 pt-5 text-center">
<UserAvatar
userId={user.id}
displayName={user.displayName}
hasAvatar={user.hasAvatar}
token={token}
isVerified={user.isVerified}
verificationIcon={user.verificationIcon}
className="mx-auto h-20 w-20"
badgeSize="md"
/>
<div className="mt-3 flex items-center justify-center gap-2">
<p className="text-lg font-semibold">{user.displayName}</p>
<AdminBadge user={user} />
</div>
<p className="mt-1 text-sm text-[#667085]">{contactLine || 'Контакты не указаны'}</p>
</div>
<div className="p-2">
{menuItems.map((item) => (
<Link
key={`${item.href}-${item.label}`}
href={item.href}
className="flex items-center gap-3 rounded-[18px] px-3 py-3 transition hover:bg-[#f4f5f8]"
>
<item.icon className="h-5 w-5 shrink-0 text-[#667085]" />
<div className="min-w-0 flex-1 text-left">
<div className="font-medium">{item.label}</div>
<div className="truncate text-xs text-[#667085]">{item.subtitle}</div>
</div>
<ChevronRight className="h-4 w-4 text-[#a8adbc]" />
</Link>
))}
{user.canAccessAdmin ? (
<Link href={getAdminLandingPath(user)} className="mt-1 flex items-center gap-3 rounded-[18px] px-3 py-3 transition hover:bg-[#f4f5f8]">
<ShieldCheck className="h-5 w-5 shrink-0 text-[#667085]" />
<div className="min-w-0 flex-1 text-left">
<div className="font-medium">Админ-панель</div>
<div className="text-xs text-[#667085]">OAuth, пользователи и настройки</div>
</div>
<ChevronRight className="h-4 w-4 text-[#a8adbc]" />
</Link>
) : null}
<button
type="button"
onClick={() => {
logout();
router.push('/auth/login');
}}
className="mt-1 flex w-full items-center gap-3 rounded-[18px] px-3 py-3 text-left transition hover:bg-[#f4f5f8]"
>
<LogOut className="h-5 w-5 shrink-0 text-[#667085]" />
<div>
<div className="font-medium">Выйти</div>
<div className="text-xs text-[#667085]">Завершить текущую сессию</div>
</div>
</button>
</div>
</PopoverContent>
</Popover>
);
}