# 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 и дизайн-токены; ориентир по духу — [`agent-frontend-ui-instructions.md`](agent-frontend-ui-instructions.md). ## Критерии готовности текущей итерации - Документация согласована с кодом: [`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-БД.