// pages/UserPage/UserPage.tsx import { useEffect, useState } from "react"; import { useParams } from "react-router-dom"; import { DefaultService } from "../../api/services/DefaultService"; import { SearchBar } from "../../components/SearchBar/SearchBar"; import { TitlesSortBox } from "../../components/TitlesSortBox/TitlesSortBox"; import { LayoutSwitch } from "../../components/LayoutSwitch/LayoutSwitch"; import { ListView } from "../../components/ListView/ListView"; import { UserTitleCardSquare } from "../../components/cards/UserTitleCardSquare"; import { UserTitleCardHorizontal } from "../../components/cards/UserTitleCardHorizontal"; import type { User, UserTitle, CursorObj, TitleSort } from "../../api"; const PAGE_SIZE = 10; type UserPageProps = { userId?: string; }; export default function UserPage({ userId }: UserPageProps) { const params = useParams(); const id = userId || params?.id; const [user, setUser] = useState(null); const [loadingUser, setLoadingUser] = useState(true); const [errorUser, setErrorUser] = useState(null); // Для списка тайтлов const [titles, setTitles] = useState([]); const [nextPage, setNextPage] = useState([]); const [cursor, setCursor] = useState(null); const [loadingTitles, setLoadingTitles] = useState(true); const [loadingMore, setLoadingMore] = useState(false); const [search, setSearch] = useState(""); const [sort, setSort] = useState("id"); const [sortForward, setSortForward] = useState(true); const [layout, setLayout] = useState<"square" | "horizontal">("square"); // --- Получение данных пользователя --- useEffect(() => { const fetchUser = async () => { if (!id) return; setLoadingUser(true); try { const result = await DefaultService.getUsersId(id, "all"); setUser(result); setErrorUser(null); } catch (err: any) { console.error(err); setErrorUser(err?.message || "Failed to fetch user data"); } finally { setLoadingUser(false); } }; fetchUser(); }, [id]); // --- Получение списка тайтлов пользователя --- const fetchPage = async (cursorObj: CursorObj | null) => { if (!id) return { items: [], nextCursor: null }; const cursorStr = cursorObj ? btoa(JSON.stringify(cursorObj)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "") : ""; try { const result = await DefaultService.getUsersTitles( id, cursorStr, sort, sortForward, search.trim() || undefined, undefined, // status фильтр, можно добавить undefined, // watchStatus undefined, // rating undefined, // myRate undefined, // releaseYear undefined, // releaseSeason PAGE_SIZE, "all" ); if (!result?.data?.length) return { items: [], nextCursor: null }; return { items: result.data, nextCursor: result.cursor ?? null }; } catch (err: any) { if (err.status === 204) return { items: [], nextCursor: null }; throw err; } }; // Инициализация: загружаем сразу две страницы useEffect(() => { const initLoad = async () => { setLoadingTitles(true); setTitles([]); setNextPage([]); setCursor(null); const firstPage = await fetchPage(null); const secondPage = firstPage.nextCursor ? await fetchPage(firstPage.nextCursor) : { items: [], nextCursor: null }; setTitles(firstPage.items); setNextPage(secondPage.items); setCursor(secondPage.nextCursor); setLoadingTitles(false); }; initLoad(); }, [id, search, sort, sortForward]); const handleLoadMore = async () => { if (nextPage.length === 0) { setLoadingMore(false); return; } setLoadingMore(true); setTitles(prev => [...prev, ...nextPage]); setNextPage([]); if (cursor) { try { const next = await fetchPage(cursor); if (next.items.length > 0) setNextPage(next.items); setCursor(next.nextCursor); } catch (err) { console.error(err); } } setLoadingMore(false); }; // const getAvatarUrl = (avatarId?: number) => (avatarId ? `/api/images/${avatarId}` : "/default-avatar.png"); return (
{/* --- Карточка пользователя --- */} {loadingUser &&
Loading user...
} {errorUser &&
{errorUser}
} {user && (
{user.nickname}

{user.disp_name || user.nickname}

{user.mail &&

{user.mail}

} {user.user_desc &&

{user.user_desc}

} {user.creation_date &&

Registered: {new Date(user.creation_date).toLocaleDateString()}

}
)} {/* --- Панель поиска, сортировки и лейаута --- */}
{/* --- Список тайтлов --- */} {loadingTitles &&
Loading titles...
} {!loadingTitles && titles.length === 0 &&
No titles found.
} {titles.length > 0 && ( <> items={titles} layout={layout} hasMore={!!cursor || nextPage.length > 1} loadingMore={loadingMore} onLoadMore={handleLoadMore} renderItem={(title, layout) => layout === "square" ? : } /> {!cursor && nextPage.length === 0 && (
Результатов больше нет, было найдено {titles.length} тайтлов.
)} )}
); }