126 lines
5.3 KiB
TypeScript
126 lines
5.3 KiB
TypeScript
'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 { useAuth } from '@/components/id/auth-provider';
|
||
import { useAvatarUrl } from '@/hooks/use-avatar-url';
|
||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
||
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();
|
||
const { avatarUrl } = useAvatarUrl(user?.id, user?.hasAvatar, token);
|
||
|
||
if (!user) return null;
|
||
|
||
const contactLine = [maskPhone(user.phone), user.username ?? user.email].filter(Boolean).join(' · ');
|
||
const initials = user.displayName
|
||
.split(' ')
|
||
.map((part) => part[0])
|
||
.join('')
|
||
.slice(0, 2)
|
||
.toUpperCase();
|
||
|
||
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="Меню пользователя"
|
||
>
|
||
<Avatar className="h-9 w-9">
|
||
{avatarUrl ? <AvatarImage src={avatarUrl} alt={user.displayName} /> : null}
|
||
<AvatarFallback className="text-xs font-semibold">{initials}</AvatarFallback>
|
||
</Avatar>
|
||
<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">
|
||
<Avatar className="mx-auto h-20 w-20">
|
||
{avatarUrl ? <AvatarImage src={avatarUrl} alt={user.displayName} /> : null}
|
||
<AvatarFallback className="text-lg font-semibold">{initials}</AvatarFallback>
|
||
</Avatar>
|
||
<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="/admin/users" 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>
|
||
);
|
||
}
|