Files
IdP/apps/frontend/app/family/page.tsx
2026-07-07 15:32:36 +03:00

117 lines
4.5 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 { useEffect, useState } from 'react';
import { useRouter } from 'next/navigation';
import { ChevronRight, Heart, Plus, UsersRound } from 'lucide-react';
import { useAuth } from '@/components/id/auth-provider';
import { IdShell } from '@/components/id/shell';
import { useToast } from '@/components/id/toast-provider';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { useRequireAuth } from '@/hooks/use-require-auth';
import { apiFetch, FamilyGroup, fetchFamilyGroups, getAccessToken, getApiErrorMessage } from '@/lib/api';
import { defaultFamilyGroupName } from '@/lib/family-defaults';
export default function FamilyPage() {
const router = useRouter();
const { user, token, isLoading, isPinLocked } = useAuth();
const { isReady } = useRequireAuth();
const { showToast } = useToast();
const [groups, setGroups] = useState<FamilyGroup[]>([]);
const [name, setName] = useState('');
const [creating, setCreating] = useState(false);
useEffect(() => {
if (user?.displayName) {
setName(defaultFamilyGroupName(user.displayName));
}
}, [user?.displayName]);
useEffect(() => {
const accessToken = getAccessToken() ?? token?.trim() ?? null;
if (!user || !accessToken || isPinLocked || isLoading) return;
fetchFamilyGroups(user.id, accessToken)
.then((response) => setGroups(response.groups ?? []))
.catch((error) => {
const message = getApiErrorMessage(error, 'Не удалось загрузить семью');
if (message) showToast(message);
});
}, [isLoading, isPinLocked, showToast, token, user]);
async function createGroup() {
const accessToken = getAccessToken() ?? token?.trim() ?? null;
if (!user || !accessToken) return;
setCreating(true);
try {
const group = await apiFetch<FamilyGroup>('/family/groups', {
method: 'POST',
body: JSON.stringify({ ownerId: user.id, name })
}, accessToken);
router.push(`/family/${group.id}`);
} catch (error) {
const message = getApiErrorMessage(error, 'Не удалось создать семью');
if (message) showToast(message);
} finally {
setCreating(false);
}
}
if (!isReady || isLoading) {
return (
<IdShell active="/family">
<div className="py-20 text-center text-[#667085]">Загрузка...</div>
</IdShell>
);
}
return (
<IdShell active="/family" wide>
<p className="text-sm text-[#667085]">Семья</p>
<h1 className="text-2xl font-medium tracking-tight sm:text-4xl">Семейный доступ</h1>
<p className="mt-2 text-sm text-[#667085] sm:text-base">Приглашайте близких, общайтесь в чатах и управляйте семейной группой.</p>
<div className="mt-6 flex flex-col gap-3 sm:mt-8 sm:flex-row sm:items-stretch">
<Input
value={name}
onChange={(event) => setName(event.target.value)}
placeholder="Название семьи"
className="h-14 min-h-14 min-w-0 flex-1 px-4 text-base"
/>
<Button
size="lg"
className="h-14 w-full shrink-0 px-6 sm:w-auto"
onClick={() => void createGroup()}
disabled={creating || !name.trim()}
>
{creating ? 'Создаём...' : (<><Plus className="h-4 w-4" />Создать</>)}
</Button>
</div>
<div className="mt-8 space-y-3">
{groups.length ? groups.map((group) => (
<button
key={group.id}
type="button"
onClick={() => router.push(`/family/${group.id}`)}
className="flex w-full items-center gap-4 rounded-[24px] bg-[#f4f5f8] p-5 text-left transition hover:bg-[#eceef4]"
>
<div className="flex h-12 w-12 items-center justify-center rounded-2xl bg-white">
<Heart className="h-5 w-5" />
</div>
<div className="flex-1">
<h2 className="font-semibold">{group.name}</h2>
<p className="text-sm text-[#667085]">{(group.members ?? []).length} участников</p>
</div>
<ChevronRight className="h-5 w-5 text-[#a8adbc]" />
</button>
)) : (
<div className="rounded-[24px] bg-[#f4f5f8] p-5 text-[#667085]">
<UsersRound className="mb-3 h-6 w-6" />
Семейных групп пока нет
</div>
)}
</div>
</IdShell>
);
}