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

197 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# План: календарь забегов (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 (никогда не во фронт-бандле):**
```env
DB_HOST=
DB_PORT=
DB_NAME=
DB_USER=
DB_PASSWORD=
```
Дополнительно по необходимости: `PORT` или `API_PORT`, `NODE_ENV`, `CORS_ORIGIN`.
**Фронтенд (Vite):** только публичный адрес API, например:
```env
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 не используется** для хранения забегов или «дельт».
```mermaid
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` — число; `date``YYYY-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 приоритет у этого файла.*