feat: русский UI, версии в футере, даты и устойчивость загрузки API
Some checks failed
CI / build-and-test (pull_request) Has been cancelled

- API: дата старта всегда YYYY-MM-DD; фронт: parseRaceDate без двойного T00:00:00
- GET /health с version из package.json; Vite define __FRONTEND_VERSION__
- Футер с версиями клиента/сервера (BEM), сетка app-shell на три ряда
- AbortController для карточки старта; ретраи GET при 502–504 и понятные ошибки шлюза
- Русские подписи навигации/страниц, lang=ru, без английских фраз в интерфейсе
This commit is contained in:
Vaka.pro
2026-04-08 00:40:03 +03:00
parent fc995ed07d
commit 42ee36d0a2
22 changed files with 251 additions and 77 deletions

View File

@@ -1,11 +1,12 @@
import { NavLink, Outlet } from "react-router-dom";
import { AppShellFooter } from "./AppShellFooter";
export function AppLayout(): JSX.Element {
return (
<div className="app-shell">
<header className="app-shell__header">
<div className="app-shell__brand">Calendar Run</div>
<nav className="app-shell__nav" aria-label="Primary navigation">
<div className="app-shell__brand">Календарь стартов</div>
<nav className="app-shell__nav" aria-label="Основная навигация">
<NavLink
to="/"
end
@@ -13,7 +14,7 @@ export function AppLayout(): JSX.Element {
isActive ? "app-shell__link app-shell__link--active" : "app-shell__link"
}
>
Dashboard
Обзор
</NavLink>
<NavLink
to="/races"
@@ -21,7 +22,7 @@ export function AppLayout(): JSX.Element {
isActive ? "app-shell__link app-shell__link--active" : "app-shell__link"
}
>
Races
Старты
</NavLink>
<NavLink
to="/races/new"
@@ -36,6 +37,7 @@ export function AppLayout(): JSX.Element {
<main className="app-shell__main">
<Outlet />
</main>
<AppShellFooter />
</div>
);
}

View File

@@ -0,0 +1,38 @@
import { useEffect, useState } from "react";
import { getHealth } from "../../api";
export function AppShellFooter(): JSX.Element {
const [backendVersion, setBackendVersion] = useState<string | null>(null);
useEffect(() => {
const ac = new AbortController();
void getHealth({ signal: ac.signal })
.then((h) => {
if (ac.signal.aborted) {
return;
}
setBackendVersion(h.version);
})
.catch(() => {
if (ac.signal.aborted) {
return;
}
setBackendVersion("недоступна");
});
return () => ac.abort();
}, []);
const backendLabel = backendVersion === null ? "…" : backendVersion;
return (
<footer className="app-shell__footer">
<p className="app-shell__footer-versions">
Версия клиента: {__FRONTEND_VERSION__}
<span className="app-shell__footer-sep" aria-hidden="true">
{" · "}
</span>
Версия сервера: {backendLabel}
</p>
</footer>
);
}