forked from admin/runners-calendar
feat: add public user profiles
This commit is contained in:
66
frontend/src/pages/PublicProfilePage.tsx
Normal file
66
frontend/src/pages/PublicProfilePage.tsx
Normal file
@@ -0,0 +1,66 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { ApiError, getPublicRaces } from "../api";
|
||||
import type { PublicRace } from "../api";
|
||||
import { formatDistance, formatRaceDate, getRaceStatusClassName, getRaceStatusLabel } from "../lib";
|
||||
|
||||
export function PublicProfilePage(): JSX.Element {
|
||||
const { userId } = useParams<{ userId: string }>();
|
||||
const [races, setRaces] = useState<PublicRace[]>([]);
|
||||
const [error, setError] = useState("");
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
const ac = new AbortController();
|
||||
if (!userId) {
|
||||
setError("Страница пользователя не найдена.");
|
||||
setLoading(false);
|
||||
return () => ac.abort();
|
||||
}
|
||||
void getPublicRaces(userId, { signal: ac.signal })
|
||||
.then((items) => {
|
||||
if (!ac.signal.aborted) {
|
||||
setRaces(items);
|
||||
}
|
||||
})
|
||||
.catch((cause) => {
|
||||
if (!ac.signal.aborted) {
|
||||
setError(cause instanceof ApiError ? cause.message : "Не удалось загрузить страницу пользователя.");
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
if (!ac.signal.aborted) {
|
||||
setLoading(false);
|
||||
}
|
||||
});
|
||||
return () => ac.abort();
|
||||
}, [userId]);
|
||||
|
||||
return (
|
||||
<section className="page page--race-day">
|
||||
<section className="race-day-hero">
|
||||
<p className="race-day-hero__eyebrow">Публичная страница</p>
|
||||
<h1 className="page__title">Календарь стартов</h1>
|
||||
<p className="page__subtitle">Опубликованные старты пользователя.</p>
|
||||
</section>
|
||||
{loading ? <p className="page__subtitle" aria-busy="true">Загружаем…</p> : null}
|
||||
{error ? <p className="page__subtitle page__subtitle--error" role="alert">{error}</p> : null}
|
||||
{!loading && !error && races.length === 0 ? <p className="page__subtitle">Пока нет опубликованных стартов.</p> : null}
|
||||
{!loading && !error && races.length > 0 ? (
|
||||
<ul className="race-day__list">
|
||||
{races.map((race, index) => (
|
||||
<li className="race-day__item" key={`${race.date}-${race.title}-${index}`}>
|
||||
<div className="race-day__body">
|
||||
<span className="race-day__title">{race.title}</span>
|
||||
<span className="race-day__meta">
|
||||
{formatRaceDate(race.date)} · {formatDistance(race.distanceKm)} ·{" "}
|
||||
<span className={getRaceStatusClassName(race.status, race.date)}>{getRaceStatusLabel(race.status, race.date)}</span>
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
) : null}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user