Initial commit: project plan and seed data
Made-with: Cursor
This commit is contained in:
196
PLAN.md
Normal file
196
PLAN.md
Normal 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 приоритет у этого файла.*
|
||||
Reference in New Issue
Block a user