# Calendar Run — план продукта Монорепозиторий: **backend** (Express + PostgreSQL) и **frontend** (React + Vite). Цель — календарь стартов с метриками бегуна: планирование, результаты, PR и сравнение. ## Вне объёма (намеренно) - Авторизация, мультипользовательность, личные кабинеты. - Парсинг сайтов организаторов и автозагрузка результатов. - Отдача статики SPA с того же процесса, что и API (фронт — отдельный Vite/build). ## Модель данных `Race` (API — camelCase) | Поле | Тип | Описание | | ----------------- | --------------------------------------------- | ----------------------------------------------- | | `id` | string | Стабильный ключ, например `{YYYY-MM-DD}-{slug}` | | `date` | string | `YYYY-MM-DD` | | `title` | string | Название | | `distanceKm` | number | Дистанция, км | | `status` | `planned` | `registered` | `completed` | null | Жизненный цикл старта | | `officialUrl` | string | null | Сайт организатора | | `startTime` | string | null | Время старта (строка, напр. `09:30`) | | `clusterSchedule` | string | null | Расписание кластеров | | `bibPickup` | string | null | Выдача номеров | | `bibNumber` | string | null | Стартовый номер | | `finishTime` | string | null | Результат `H:MM:SS` или `MM:SS` | | `finishPlace` | string | null | Место на финише (текст: «3», «3/120» и т.п.) | | `notes` | string | null | Заметки | | `createdAt` | string | ISO, read-only | | `updatedAt` | string | null | ISO, read-only | PostgreSQL: `snake_case` столбцы, маппинг в `[backend/src/mappers/race.ts](backend/src/mappers/race.ts)`. ## HTTP API (минимум) - `GET /health` — liveness без БД. - `GET /ready` — readiness (подключение к БД; в режиме mock считается доступной — только для dev/CI). - `GET /races` — список; query: `year`, `month` (целые; `month` 1–12). - `GET /races/:id`, `POST /races`, `PATCH /races/:id`, `DELETE /races/:id`. Ошибки: JSON, единый стиль (`validation_error`, `not_found`, `conflict`, `database_unavailable`). Подробности — `[docs/backend-api-for-frontend.md](docs/backend-api-for-frontend.md)`. ## Seed - Файл `[import/races_2026_calendar.csv](import/races_2026_calendar.csv)`. - Стабильный `id`, upsert по `id`. Повторный запуск безопасен. ## Режим без PostgreSQL (dev/CI) Переменная `CALENDAR_RUN_MOCK_DB=1` (или `true`): HTTP-обработчики используют заглушку пула **без** реальной БД. **Не использовать** для `npm run db:migrate` и `npm run seed` — нужен настоящий Postgres и `DB_`*. ## Frontend (SPA) - Маршруты: дашборд (`/`), список стартов (`/races`), карточка (`/races/:id`). - Дашборд: ближайший старт, последний результат, PR, сезон, PR по ключевым дистанциям, сравнение завершённых стартов, при необходимости — лёгкая визуализация прогресса. - Список: будущие / прошедшие; фильтрация по году и месяцу через API. - Стили: BEM и дизайн-токены (см. `frontend/src/styles/tokens.css`). ## Критерии готовности текущей итерации - Документация согласована с кодом: `[README.md](README.md)`, `[docs/backend.md](docs/backend.md)`, `[docs/backend-api-for-frontend.md](docs/backend-api-for-frontend.md)`. - Миграции и seed воспроизводимы; контракт API покрыт smoke-тестами в CI при необходимости с mock-БД.