Files
runners-calendar/PLAN.md
2026-04-01 13:42:37 +03:00

12 KiB
Raw Blame History

План: календарь забегов (SPA + API + PostgreSQL)

Консолидированный план реализации с актуальными решениями. После инициализации git — работать в отдельной ветке (например feature/race-calendar-app).

1. Цели продукта

  1. Расписание по месяцу — выбор месяца/года, список забегов.
  2. Расписание на год — календарная сетка года, отметки на датах со стартами; по клику на дату — модалка/панель со списком забегов и переход на карточку.
  3. Добавлять забеги запланированные и уже прошедшие.
  4. Для прошедших — ввод/редактирование результата и стартового номера; поля можно дописать позже.
  5. Личные рекорды на главной по дистанциям: 1 км, 5 км, 10 км, 15 км, 21.1 км, 42.2 км — обновляются, если в забеге указан более быстрый результат на «подходящей» дистанции.
  6. Старты (организатор): дата и время старта, расписание кластеров, выдача номеров — ручной ввод; обязательна возможность указать официальную ссылку на страницу организатора (автопарсинг сайтов не входит в объём).
  7. Авторизация не требуется.

2. Исходные данные в репозитории

  • Файл import/races_2026_calendar.csv — колонки date, month, day, event, distance_km.
  • Назначение CSV: один раз — как вход для seed-скрипта, который пишет строки в PostgreSQL.
  • В рантайме ни фронт, ни API этот CSV не читают. После успешного seed файл может оставаться в репо только как архив/референс.

3. Стек и структура репозитория

  • Монорепозиторий: frontend/ (Vite + React 18 + TypeScript + react-router-dom) и backend/ (Node.js + Fastify или Express).
  • БД: PostgreSQL; схема — SQL-миграции в backend/ (или согласованный каталог миграций).
  • Локально: docker-compose.yml с сервисом Postgres.
  • Стили: CSS + BEM + CSS variables (минимальная дизайн-система: цвета, отступы, типографика).
  • Даты/время: date-fns или Intl, русская локаль там, где нужно.

3.1 Переменные окружения

Только на сервере API (никогда не во фронт-бандле):

DB_HOST=
DB_PORT=
DB_NAME=
DB_USER=
DB_PASSWORD=

Дополнительно по необходимости: PORT или API_PORT, NODE_ENV, CORS_ORIGIN.

Фронтенд (Vite): только публичный адрес API, например:

VITE_API_BASE_URL=http://localhost:3001

В коде: import.meta.env.VITE_API_BASE_URL.

Шаблон без секретов — корневой .env.example; описание — в docs/frontend.md и docs/backend.md.

4. Источник правды и поток данных

4.1 Рантайм

Единственный источник правды для календаря и карточек в работающем приложении — PostgreSQL. SPA общается только с HTTP API.

localStorage не используется для хранения забегов или «дельт».

flowchart LR
  User[User]
  SPA[React SPA]
  API[Node API]
  DB[(PostgreSQL)]
  User --> SPA
  SPA -->|"fetch VITE_API_BASE_URL"| API
  API -->|"DB_* connection"| DB

4.2 Вне рантайма (разово)

Seed-скрипт (запуск вручную при развёртывании/обновлении стартового набора):

  • Читает import/*.csv и/или опционально public/data/races.json.
  • Выполняет INSERT / upsert в таблицу races.
  • Не вызывается из SPA и не выполняется на каждом HTTP-запросе.

Промежуточная ступень CSV → races.json для работы SPA не обязательна: seed может писать в БД напрямую из CSV и/или из JSON.

4.3 Разовый перенос CSV → БД

  • Парсинг: заголовок; кавычки в event; distance_km — число; dateYYYY-MM-DD.
  • После переноса приложение в обычном режиме не использует import/races_2026_calendar.csv. Новые старты — через UI/API (или отдельная админ-фича импорта, если появится позже).

5. Модель данных (БД и API)

Один набор полей — в таблице, в JSON тел запросов/ответов API и в опциональном файле для seed. Согласовать camelCase в API и snake_case в SQL в docs/backend.md.

Поле Обяз. Примечание
id да Стабильный ключ, напр. 2026-05-03-kazan-marathon
date да День старта YYYY-MM-DD
title да Название
distanceKm да Число км
status нет planned / completed; иначе можно вывести из даты
officialUrl нет Ссылка на организатора
startTime нет Напр. 09:30
clusterSchedule нет Многострочный текст
bibPickup нет Выдача номеров
bibNumber нет Стартовый номер
finishTime нет Время H:MM:SS / HH:MM:SS; для PR — перевод в секунды
notes нет Заметки

Опционально в миграциях: created_at, updated_at.

Операции: GET список/фильтры по году-месяцу при необходимости, GET :id, POST, PATCH :id, при необходимости DELETE — зафиксировать в docs/backend.md.

6. Поведение SPA

6.1 Форма «Добавить забег»

  • Обязательно: дата, название, дистанция.
  • Если дата строго до сегодня (локальная дата пользователя) — показать результат и стартовый номер (необязательны при первом сохранении).
  • Если дата в будущем — поля результата скрыты.
  • Опционально: чекбокс «Уже прошёл» для случая сегодняшней даты, чтобы открыть поля результата.
  • Сохранение: POST на API → запись в БД.

6.2 Страница забега

  • Все поля модели; для прошедших — акцент на результат и номер.
  • Редактирование результата и номера в любой момент: PATCH на API, затем обновление состояния на клиенте (refetch / инвалидация кэша).

6.3 Личные рекорды

  • Дистанции: 1, 5, 10, 15, 21.1, 42.2 км.
  • Учитывать забеги с заполненным finishTime и дистанцией в пределах допуска к целевой (напр. 21.0975 → 21.1, 42.195 → 42.2).
  • «Чужие» дистанции (2 км, 6 км, 30 км…) в таблицу PR по умолчанию не включаются.
  • Расчёт на клиенте по данным, полученным с API.

6.4 Экраны

  1. Главная — PR, навигация «Месяц» / «Год», ближайшие старты.
  2. Месяц — селектор, список.
  3. Год — сетка, маркеры, клик по дате → модалка со списком → карточка.
  4. Карточка забега, форма добавления.
  5. Доступность модалки: фокус, Esc, контраст.

7. Документация

Создать каталог docs/:

Файл Назначение
docs/backend.md Docker, DB_*, миграции, seed, REST API, CORS
docs/frontend.md Структура frontend/, VITE_*, сборка, контракт с API
docs/ux-spa.md Сценарии, экраны, BEM, кратко про a11y

Корневой README.md: описание проекта, быстрый старт, ссылки на три файла выше.

8. Риски и ограничения

  • DB_* и пароли — только в окружении сервера API; в git — только .env.example без реальных секретов.
  • Актуальные данные после работы в UI — в БД; файлы в git не синхронизируются с правками автоматически. Надёжный бэкап — резервное копирование PostgreSQL.
  • Деплой: Postgres + процесс API с env + статическая раздача фронта (или иная схема хостинга — описать в README).
  • Данные организаторов (расписание стартов) — вручную + ссылка; без парсинга чужих сайтов в объёме первой версии.

9. Ключевые пути после реализации

  • docker-compose.yml, .env.example
  • backend/ — сервер, миграции, роуты
  • scripts/ или backend/scripts/разовый seed из CSV/JSON
  • import/races_2026_calendar.csv — только вход seed (не рантайм)
  • public/data/races.json — опциональный вход seed
  • frontend/src/api/ — клиент API
  • frontend/src/pages/, frontend/src/components/ — UI (BEM)
  • frontend/src/lib/distances.ts — PR
  • docs/*.md, README.md

10. Чеклист задач (implementation todos)

  1. Монорепо: frontend/ + backend/, BEM, токены, роутер.
  2. Postgres в docker-compose, миграции таблицы races, бэкенд читает DB_*.
  3. REST CRUD + разовый seed (CSV и/или JSON) → БД.
  4. Клиент API на фронте, типы, загрузка данных для экранов и PR.
  5. Экраны месяц и год, модалка по дате.
  6. Форма добавления с условными полями; карточка с PATCH результата/номера.
  7. Поля организатора на карточке.
  8. A11y модалки, мобильная вёрстка, смоук-сценарии.
  9. docs/ + README + .env.example.

11. Идеи на будущее (тематика бега)

  • Недельный объём / простой план подготовки.
  • Импорт/экспорт JSON для бэкапа через API.
  • Фильтры (город, дистанция), погода по дню старта.
  • График динамики PR.
  • Загрузка GPX / тренировки.
  • Браузерные напоминания за N дней до старта.
  • Сравнение результатов одной дистанции по годам.
  • PWA для офлайн-просмотра (read-only кэш не заменяет БД без продуманной синхронизации).

Документ создан как единый актуальный план в корне репозитория. При расхождениях с черновиками в IDE приоритет у этого файла.