feat: align docs with code, finish_place, registered status, UI filters, tests, CI
Some checks failed
CI / build-and-test (pull_request) Has been cancelled

- Add PLAN.md and sync backend docs, .env.example, API doc (404 details)
- Document mock DB and PORT/API_PORT in docs/backend.md; README monorepo + frontend/.env.example
- Migration 002: finish_place column, status registered; mapper and mock DB updated
- Frontend: registered status, finishPlace, calendar year/month filters, pace sparkline
- Extract createApp for tests; supertest + tsx; GitHub Actions CI

Made-with: Cursor
This commit is contained in:
Vaka.pro
2026-04-06 22:20:31 +03:00
parent 1ffc3a65eb
commit a2dcf67396
27 changed files with 1410 additions and 286 deletions

View File

@@ -87,6 +87,7 @@ GET /races?year=2026&month=5
"bibPickup": null,
"bibNumber": null,
"finishTime": null,
"finishPlace": null,
"notes": null,
"createdAt": "2026-03-31T12:00:00.000Z",
"updatedAt": null
@@ -102,10 +103,10 @@ GET /races?year=2026&month=5
**Ответ 200:** объект `Race` (см. модель ниже).
**Ответ 404:**
**Ответ 404:** тело JSON, поле `details` — массив пояснений (можно показывать в UI или игнорировать).
```json
{ "error": "not_found" }
{ "error": "not_found", "details": ["Race not found"] }
```
---
@@ -129,6 +130,7 @@ GET /races?year=2026&month=5
"bibPickup": null,
"bibNumber": null,
"finishTime": null,
"finishPlace": null,
"notes": null
}
```
@@ -160,12 +162,13 @@ GET /races?year=2026&month=5
```json
{
"finishTime": "1:45:30",
"finishPlace": "12/340",
"bibNumber": "1234",
"status": "completed"
}
```
**Допустимые поля:** `date`, `title`, `distanceKm`, `status`, `officialUrl`, `startTime`, `clusterSchedule`, `bibPickup`, `bibNumber`, `finishTime`, `notes`.
**Допустимые поля:** `date`, `title`, `distanceKm`, `status`, `officialUrl`, `startTime`, `clusterSchedule`, `bibPickup`, `bibNumber`, `finishTime`, `finishPlace`, `notes`.
**Ответ 200:** обновлённый объект `Race`.
@@ -178,7 +181,7 @@ GET /races?year=2026&month=5
**Ответ 404:**
```json
{ "error": "not_found" }
{ "error": "not_found", "details": ["Race not found"] }
```
---
@@ -192,7 +195,7 @@ GET /races?year=2026&month=5
**Ответ 404:**
```json
{ "error": "not_found" }
{ "error": "not_found", "details": ["Race not found"] }
```
## 4. Модель `Race` (camelCase)
@@ -203,13 +206,14 @@ GET /races?year=2026&month=5
| `date` | string | да | да | `YYYY-MM-DD` |
| `title` | string | да | да | Название забега |
| `distanceKm` | number | да | да | Дистанция в км |
| `status` | string \| null | нет | да | `"planned"` / `"completed"` |
| `status` | string \| null | нет | да | `"planned"` / `"registered"` / `"completed"` |
| `officialUrl` | string \| null | нет | да | URL организатора |
| `startTime` | string \| null | нет | да | Время старта, напр. `"09:30"` |
| `clusterSchedule` | string \| null | нет | да | Расписание кластеров |
| `bibPickup` | string \| null | нет | да | Выдача номеров |
| `bibNumber` | string \| null | нет | да | Стартовый номер |
| `finishTime` | string \| null | нет | да | Финишное время `H:MM:SS` |
| `finishTime` | string \| null | нет | да | Финишное время `H:MM:SS` или `MM:SS` |
| `finishPlace` | string \| null | нет | да | Место на финише (произвольная строка) |
| `notes` | string \| null | нет | да | Заметки |
| `createdAt` | string | — | — | ISO timestamp (read-only) |
| `updatedAt` | string \| null | — | — | ISO timestamp (read-only) |
@@ -229,6 +233,6 @@ Seed-скрипт (`npm run seed` в `backend/`) выполняет **upsert**
## 7. Поведение при недоступной БД
- `GET /health` — всегда `200` (не проверяет БД).
- `GET /ready``503 { "error": "database_unavailable", "db": "disconnected" }`.
- `GET /ready` при недоступной БД: `503 { "error": "database_unavailable", "db": "disconnected" }`. В режиме **`CALENDAR_RUN_MOCK_DB`** (dev/CI без Postgres) readiness возвращает успех без реального подключения — см. `docs/backend.md`.
- Все остальные маршруты — `503 { "error": "database_unavailable" }`.
- В логах сервера: строка ошибки с контекстом маршрута.