Initial commit: project plan and seed data

Made-with: Cursor
This commit is contained in:
Anton
2026-04-01 13:42:37 +03:00
commit 88a448dd8e
3 changed files with 377 additions and 0 deletions

196
PLAN.md Normal file
View File

@@ -0,0 +1,196 @@
# План: календарь забегов (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 приоритет у этого файла.*