feat(frontend): redesign race dashboard
Some checks failed
CI / build-and-test (pull_request) Has been cancelled
Some checks failed
CI / build-and-test (pull_request) Has been cancelled
This commit is contained in:
@@ -2,7 +2,14 @@ import { useEffect, useMemo, useState } from "react";
|
||||
import { Link, useParams } from "react-router-dom";
|
||||
import type { Race } from "../api";
|
||||
import { ApiError, getRaces } from "../api";
|
||||
import { formatDistance, formatRaceDate, getRaceStatusClassName, getRaceStatusLabel, sortByDateAsc } from "../lib";
|
||||
import {
|
||||
formatDistance,
|
||||
formatRaceDate,
|
||||
getRaceStatusClassName,
|
||||
getRaceStatusLabel,
|
||||
getRaceVisual,
|
||||
sortByDateAsc,
|
||||
} from "../lib";
|
||||
|
||||
function getErrorMessage(error: unknown): string {
|
||||
if (error instanceof ApiError) {
|
||||
@@ -70,24 +77,33 @@ export function RaceDayPage(): JSX.Element {
|
||||
if (!validYmd) {
|
||||
return (
|
||||
<section className="page page--race-day">
|
||||
<h1 className="page__title">Некорректная дата</h1>
|
||||
<p className="page__subtitle">
|
||||
<div className="race-day-hero">
|
||||
<p className="race-day-hero__eyebrow">Страница дня</p>
|
||||
<h1 className="page__title">Некорректная дата</h1>
|
||||
<Link className="page-link" to="/races">
|
||||
Вернуться к календарю стартов
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="page page--race-day">
|
||||
<p className="page__subtitle">
|
||||
<section className="race-day-hero" aria-label="Старты дня">
|
||||
<Link className="page-link" to="/races">
|
||||
← Календарь стартов
|
||||
</Link>
|
||||
</p>
|
||||
<h1 className="page__title">{heading}</h1>
|
||||
<p className="race-day-hero__eyebrow">Старты дня</p>
|
||||
<h1 className="page__title">{heading}</h1>
|
||||
<p className="page__subtitle">
|
||||
{isLoading
|
||||
? "Загружаем расписание..."
|
||||
: races.length > 0
|
||||
? `Запланировано стартов: ${races.length}`
|
||||
: "Проверьте расписание или добавьте старт на эту дату."}
|
||||
</p>
|
||||
</section>
|
||||
|
||||
{errorMessage ? (
|
||||
<p className="page__subtitle page__subtitle--error" role="alert">
|
||||
@@ -107,19 +123,40 @@ export function RaceDayPage(): JSX.Element {
|
||||
|
||||
{!isLoading && races.length > 0 ? (
|
||||
<ul className="race-day__list">
|
||||
{races.map((race) => (
|
||||
<li key={race.id} className="race-day__item">
|
||||
<Link className="race-day__link" to={`/races/${race.id}`}>
|
||||
{race.title}
|
||||
</Link>
|
||||
<span className="race-day__meta">
|
||||
{formatDistance(race.distanceKm)} ·{" "}
|
||||
<span className={getRaceStatusClassName(race.status, race.date)}>
|
||||
{getRaceStatusLabel(race.status, race.date)}
|
||||
</span>
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
{races.map((race) => {
|
||||
const visual = getRaceVisual(race);
|
||||
|
||||
return (
|
||||
<li key={race.id} className="race-day__item">
|
||||
<Link className="race-day__link" to={`/races/${race.id}`}>
|
||||
<img
|
||||
className={`race-day__image${
|
||||
visual.imageFit === "contain" ? " race-day__image--contain" : ""
|
||||
}`}
|
||||
src={visual.imageSrc}
|
||||
alt=""
|
||||
loading="lazy"
|
||||
referrerPolicy="no-referrer"
|
||||
onError={(event) => {
|
||||
event.currentTarget.onerror = null;
|
||||
event.currentTarget.classList.remove("race-day__image--contain");
|
||||
event.currentTarget.src = visual.fallbackSrc;
|
||||
}}
|
||||
/>
|
||||
<span className="race-day__body">
|
||||
<span className="race-day__kicker">{visual.label}</span>
|
||||
<span className="race-day__title">{race.title}</span>
|
||||
<span className="race-day__meta">
|
||||
{formatDistance(race.distanceKm)} ·{" "}
|
||||
<span className={getRaceStatusClassName(race.status, race.date)}>
|
||||
{getRaceStatusLabel(race.status, race.date)}
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</Link>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
) : null}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user