142 lines
6.6 KiB
TypeScript
142 lines
6.6 KiB
TypeScript
'use client';
|
||
|
||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||
import { useRouter } from 'next/navigation';
|
||
import { BadgeCheck, Bell, Car, FileText, Fingerprint, Heart, KeyRound, PawPrint, ShieldCheck, Smartphone, UserRound } from 'lucide-react';
|
||
import { AddressQuickSection } from '@/components/addresses/address-quick-section';
|
||
import { DocumentFormDialog } from '@/components/documents/document-form-dialog';
|
||
import { ActionTile, SectionTitle } from '@/components/id/action-tile';
|
||
import { AdminBadge } from '@/components/id/admin-badge';
|
||
import { AvatarDisplay } from '@/components/id/avatar-upload';
|
||
import { useAuth } from '@/components/id/auth-provider';
|
||
import { IdShell } from '@/components/id/shell';
|
||
import { Button } from '@/components/ui/button';
|
||
import { useRequireAuth } from '@/hooks/use-require-auth';
|
||
import { type DocumentTypeCode, indexDocumentsByType } from '@/lib/document-catalog';
|
||
import { apiFetch, UserDocument } from '@/lib/api';
|
||
|
||
export default function HomePage() {
|
||
const router = useRouter();
|
||
const { user, token, isPinLocked } = useAuth();
|
||
const { isReady } = useRequireAuth();
|
||
const userId = user?.id;
|
||
const contactLine = [user?.phone, user?.username ?? user?.email].filter(Boolean).join(' · ');
|
||
const [documents, setDocuments] = useState<UserDocument[]>([]);
|
||
const [activeDocumentType, setActiveDocumentType] = useState<DocumentTypeCode | null>(null);
|
||
|
||
const loadDocuments = useCallback(async () => {
|
||
if (!userId || !token || isPinLocked) return;
|
||
try {
|
||
const response = await apiFetch<{ documents?: UserDocument[] }>(`/documents/users/${userId}`, {}, token);
|
||
setDocuments(response.documents ?? []);
|
||
} catch {
|
||
// Фоновая ошибка не должна очищать уже показанные документы.
|
||
}
|
||
}, [isPinLocked, token, userId]);
|
||
|
||
useEffect(() => {
|
||
if (isReady && userId && !isPinLocked) void loadDocuments();
|
||
}, [isPinLocked, isReady, loadDocuments, userId]);
|
||
|
||
const documentsByType = useMemo(() => indexDocumentsByType(documents), [documents]);
|
||
|
||
function openDocument(type: DocumentTypeCode) {
|
||
setActiveDocumentType(type);
|
||
}
|
||
|
||
if (!isReady) {
|
||
return (
|
||
<IdShell active="/" wide>
|
||
<div className="py-20 text-center text-[#667085]">Загрузка профиля...</div>
|
||
</IdShell>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<IdShell active="/" wide>
|
||
<div className="flex flex-col items-center">
|
||
{user ? (
|
||
<AvatarDisplay
|
||
userId={user.id}
|
||
displayName={user.displayName}
|
||
hasAvatar={user.hasAvatar}
|
||
token={token}
|
||
isVerified={user.isVerified}
|
||
verificationIcon={user.verificationIcon}
|
||
className="h-24 w-24 border-4 border-white shadow-xl"
|
||
badgeSize="md"
|
||
/>
|
||
) : (
|
||
<div className="h-24 w-24 rounded-full bg-[#f4f5f8]" />
|
||
)}
|
||
<h1 className="mt-4 text-2xl font-semibold">{user?.displayName ?? 'Загрузка профиля...'}</h1>
|
||
{user ? <AdminBadge user={user} className="mt-2" /> : null}
|
||
<p className="text-sm text-[#667085]">{contactLine || 'Данные профиля загружаются'}</p>
|
||
<div className="mt-4 flex gap-3">
|
||
<Button variant="secondary" size="sm" onClick={() => router.push('/family')}>
|
||
<Heart className="h-4 w-4 text-red-500" />
|
||
Семья
|
||
</Button>
|
||
<Button variant="secondary" size="sm" onClick={() => router.push('/data')}>
|
||
<UserRound className="h-4 w-4" />
|
||
Профиль
|
||
</Button>
|
||
</div>
|
||
|
||
<div className="mt-8 flex w-full max-w-[450px] items-center justify-between rounded-[24px] bg-[#f0f2f7] p-5">
|
||
<div>
|
||
<h2 className="font-semibold">Защитите ваш аккаунт</h2>
|
||
<p className="mt-1 text-sm text-[#667085]">Настройте PIN-код и способы восстановления</p>
|
||
<Button className="mt-4 rounded-xl" size="sm" onClick={() => router.push('/security')}>
|
||
Перейти к защите
|
||
</Button>
|
||
</div>
|
||
<BadgeCheck className="h-20 w-20 text-[#68b8ff]" />
|
||
</div>
|
||
|
||
<div className="w-full max-w-[720px]">
|
||
<SectionTitle>Защита аккаунта</SectionTitle>
|
||
<div className="grid grid-cols-3 gap-4 md:grid-cols-5">
|
||
<ActionTile icon={ShieldCheck} label="Способы входа" onClick={() => router.push('/security')} />
|
||
<ActionTile icon={Smartphone} label="Проверить устройства" onClick={() => router.push('/security')} />
|
||
<ActionTile icon={Bell} label="Активность" onClick={() => router.push('/security')} />
|
||
<ActionTile icon={KeyRound} label="Добавить запасную почту" onClick={() => router.push('/security')} />
|
||
<ActionTile icon={Fingerprint} label="Привязать лицо или отпечаток" onClick={() => router.push('/security')} />
|
||
</div>
|
||
|
||
<SectionTitle>Документы</SectionTitle>
|
||
<div className="grid grid-cols-3 gap-4 md:grid-cols-6">
|
||
<ActionTile icon={FileText} label="Все" onClick={() => router.push('/documents')} />
|
||
<ActionTile icon={FileText} label="Паспорт" onClick={() => openDocument('PASSPORT_RF')} />
|
||
<ActionTile icon={FileText} label="Загран" onClick={() => openDocument('FOREIGN_PASSPORT')} />
|
||
<ActionTile icon={Car} label="ВУ" onClick={() => openDocument('DRIVER_LICENSE')} />
|
||
</div>
|
||
|
||
<AddressQuickSection layout="home" isPinLocked={isPinLocked} isReady={isReady} />
|
||
|
||
<SectionTitle>Семья</SectionTitle>
|
||
<div className="grid grid-cols-3 gap-4 md:grid-cols-6">
|
||
<ActionTile icon={UserRound} label="Вы" onClick={() => router.push('/family')} />
|
||
<ActionTile icon={Heart} label="Добавить" onClick={() => router.push('/family')} />
|
||
<ActionTile icon={PawPrint} label="Питомцы" onClick={() => router.push('/family')} />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{activeDocumentType && user ? (
|
||
<DocumentFormDialog
|
||
open={Boolean(activeDocumentType)}
|
||
onOpenChange={(open) => {
|
||
if (!open) setActiveDocumentType(null);
|
||
}}
|
||
documentType={activeDocumentType}
|
||
userId={user.id}
|
||
token={token}
|
||
existing={documentsByType.get(activeDocumentType) ?? null}
|
||
onSaved={loadDocuments}
|
||
/>
|
||
) : null}
|
||
</IdShell>
|
||
);
|
||
}
|