diff --git a/CHANGELOG.md b/CHANGELOG.md index e2cfb0d..a637830 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,9 +11,34 @@ #### Added -- Управление паролем и сессиями: проверка текущего пароля, список активных сессий, отзыв выбранной или всех остальных сессий. - Фильтрация стартов по статусу, периоду и дистанции; backfill и обновление автоматически получаемых обложек. +### [1.5.3] — 2026-07-12 + +#### Fixed + +- Миграции базы данных автоматически выполняются перед запуском backend-контейнера. + +### [1.5.2] — 2026-07-12 + +#### Added + +- Настраиваемое имя публичного профиля и раздельная видимость будущих и завершённых стартов. +- Публичные результаты финиша с учётом настроек видимости владельца. + +### [1.5.1] — 2026-07-12 + +#### Added + +- Публичные страницы пользователей и API опубликованного календаря стартов. + +### [1.5.0] — 2026-07-12 + +#### Added + +- Проверка и смена текущего пароля. +- Список активных сессий, отзыв выбранной сессии или всех остальных сессий. + ### [1.4.2] — 2026-07-12 #### Changed @@ -83,6 +108,12 @@ ## Frontend +### [0.10.2] — 2026-08-14 + +#### Fixed + +- По умолчанию завершённые старты отображаются от самых свежих к более старым; явно выбранная сортировка применяется без переопределения. + ### [0.10.1] — 2026-08-13 #### Changed @@ -101,17 +132,48 @@ - В конфигурацию TypeScript для Vite добавлены DOM-типы. -### [0.9.3] — 2026-07-14 +### [0.9.3] — 2026-07-13 -#### Added +#### Changed -- Экран «Аккаунт»: подтверждённый email, смена пароля и управление другими активными сессиями. -- На дашборде появились фильтруемые «Последние старты» с пагинацией и переходом в старт, ссылки из карточек рекордов и интерактивный график прогресса. -- Поиск, фильтры, сортировка и экспорт истории; график прогресса переведён на AG Charts. +- Фильтры календаря сворачиваются и показывают число активных условий; hero-блок дашборда стал компактнее. + +### [0.9.1] — 2026-07-13 #### Fixed -- Упрощён обзор календаря стартов и зарегистрированы модули AG Charts. +- Зарегистрированы необходимые модули AG Charts для корректного отображения графика. + +### [0.9.0] — 2026-07-13 + +#### Added + +- Поиск, фильтры и сортировка истории стартов с синхронизацией параметров в URL. +- Экспорт выбранных стартов в CSV и iCalendar. +- Интерактивный график прогресса на AG Charts. +- Фильтрация стартов по статусу, диапазону дат и дистанции. + +### [0.8.2] — 2026-07-12 + +#### Added + +- Имена публичных профилей и отдельные настройки видимости будущих и завершённых стартов. +- Отображение опубликованных результатов на публичной странице. + +### [0.8.1] — 2026-07-12 + +#### Added + +- Публичная страница пользователя с опубликованным календарём стартов. +- Управление видимостью публичного профиля в аккаунте. + +### [0.8.0] — 2026-07-12 + +#### Added + +- Экран «Аккаунт»: подтверждённый email, смена пароля и управление активными сессиями. +- Фильтруемые «Последние старты» с пагинацией и переходом в старт на дашборде. +- Ссылки из карточек рекордов и интерактивный график прогресса. ### [0.7.1] — 2026-07-12 @@ -165,6 +227,25 @@ - Редизайн дашборда и визуального представления стартов. +### [0.4.3] — 2026-04-13 + +#### Fixed + +- В календаре скрыты всплывающие карточки для дней без стартов. + +### [0.4.2] — 2026-04-13 + +#### Fixed + +- Анимация наведения применяется ко всей строке старта, а не только к внутренней ссылке. + +### [0.4.1] — 2026-04-13 + +#### Fixed + +- Старт автоматически получает статус «Пробежал» при вводе корректного времени финиша. +- Исправлены переходы с дашборда и поведение списка и календаря стартов. + ### [0.4.0] — 2026-04-13 #### Added diff --git a/frontend/package-lock.json b/frontend/package-lock.json index bc6ee79..50e635c 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "calendar-run-frontend", - "version": "0.10.1", + "version": "0.10.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "calendar-run-frontend", - "version": "0.10.1", + "version": "0.10.2", "dependencies": { "ag-charts-community": "^14.0.0", "ag-charts-react": "^14.0.0", diff --git a/frontend/package.json b/frontend/package.json index 3e082a8..bd0c3e2 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "calendar-run-frontend", "private": true, - "version": "0.10.1", + "version": "0.10.2", "type": "module", "scripts": { "dev": "vite", diff --git a/frontend/src/pages/RacesPage.tsx b/frontend/src/pages/RacesPage.tsx index fb75fd3..3e1158a 100644 --- a/frontend/src/pages/RacesPage.tsx +++ b/frontend/src/pages/RacesPage.tsx @@ -10,7 +10,7 @@ import { getRaceStatusClassName, getRaceStatusLabel, parseRaceDate, - sortByDateAsc, + sortByDateDesc, downloadTextFile, racesToCsv, racesToIcs, @@ -37,6 +37,7 @@ const VIEW_STORAGE_KEY = "races-view-mode"; type ViewMode = "list" | "calendar"; type RaceListTab = "upcoming" | "completed"; type RaceSort = "dateAsc" | "dateDesc" | "titleAsc" | "distanceAsc"; +type RaceSortOption = RaceSort | ""; function yearSelectOptions(races: Race[], selectedYear = ""): number[] { const current = new Date().getFullYear(); @@ -146,7 +147,7 @@ export function RacesPage(): JSX.Element { const [distanceMin, setDistanceMin] = useState(() => searchParams.get("distanceMin") ?? ""); const [distanceMax, setDistanceMax] = useState(() => searchParams.get("distanceMax") ?? ""); const [searchQuery, setSearchQuery] = useState(() => searchParams.get("q") ?? ""); - const [sortOrder, setSortOrder] = useState(() => (searchParams.get("sort") as RaceSort | null) ?? "dateAsc"); + const [sortOrder, setSortOrder] = useState(() => (searchParams.get("sort") as RaceSort | null) ?? ""); const [viewMode, setViewMode] = useState(() => searchParams.get("view") === "calendar" ? "calendar" : readInitialViewMode()); const [activeListTab, setActiveListTab] = useState("upcoming"); const [isAdvancedFiltersOpen, setIsAdvancedFiltersOpen] = useState(() => @@ -263,7 +264,7 @@ export function RacesPage(): JSX.Element { if (distanceMin) next.set("distanceMin", distanceMin); if (distanceMax) next.set("distanceMax", distanceMax); if (searchQuery) next.set("q", searchQuery); - if (sortOrder !== "dateAsc") next.set("sort", sortOrder); + if (sortOrder) next.set("sort", sortOrder); setSearchParams(next, { replace: true }); }, [dateFrom, dateTo, distanceMax, distanceMin, monthFilter, searchQuery, setSearchParams, sortOrder, statusFilter, viewMode, yearFilter]); @@ -281,9 +282,11 @@ export function RacesPage(): JSX.Element { const { upcoming, completed } = useMemo( () => ({ upcoming: filteredRaces.filter((race) => race.status !== "completed"), - completed: sortByDateAsc(filteredRaces.filter((race) => race.status === "completed")), + completed: sortOrder + ? filteredRaces.filter((race) => race.status === "completed") + : sortByDateDesc(filteredRaces.filter((race) => race.status === "completed")), }), - [filteredRaces], + [filteredRaces, sortOrder], ); const statusMessage = useMemo(() => { if (errorMessage && !isLoading) { @@ -309,7 +312,7 @@ export function RacesPage(): JSX.Element { dateTo, distanceMin, distanceMax, - sortOrder !== "dateAsc" ? sortOrder : "", + sortOrder, ].filter(Boolean).length; if (errorMessage && races.length === 0 && !isLoading) { @@ -478,7 +481,8 @@ export function RacesPage(): JSX.Element {