Docs: sync booking expired (Back#60) + Front#31 UX polish. Fixes EventHub/EventHubSpec#13

This commit is contained in:
2026-07-27 17:53:23 +03:00
parent 7c21fa2aef
commit 546f888487
4 changed files with 74 additions and 29 deletions
+28 -14
View File
@@ -95,13 +95,19 @@ pending владельца, **не** меняет `calendar.type`. Legacy `downg
- `POST /v1/events/:id/bookings` только если календарь события commercial, `booking_open=true`,
событие `active`, есть свободная вместимость.
- **Pending занимает capacity** наравне с confirmed (защита от overbook при auto/timeout).
- Capacity: число booking со статусом `pending` | `confirmed`; `cancelled` не считаются.
- Capacity: число booking со статусом `pending` | `confirmed`; `cancelled` и `expired` не считаются.
- Политика `confirmation` календаря при создании booking:
- `auto` → сразу `confirmed` + `confirmed_at`;
- `manual``pending`; confirm/decline — владелец или specialist (см. ниже);
- `{timeout, N}``pending`; через N секунд без решения: auto-confirm, если ещё есть
capacity, иначе `cancelled` (`timeout_full`).
- Участник: `DELETE /v1/bookings/:id` — отмена своей pending/confirmed.
- **Past-pending → `expired`:** если событие уже началось (`now >= event.start_time`), а booking
ещё `pending`, статус переводится в `expired` (lazy при чтении списков/`GET` booking и в
`process_timeout_bookings`). `expired` не actionable: Confirm/Decline → `409` (`Booking expired`).
- **Inbox:** `GET /v1/user/booking-requests` возвращает только ещё actionable pending
(до старта события); past-pending помечает `expired` и **не** включает в ответ.
- Участник: `DELETE /v1/bookings/:id` — отмена своей pending/confirmed; для уже `expired`
no-op успех (как для `cancelled`).
- WS: `booking_update` участнику и владельцу (и specialist при confirm на «своём» событии).
#### Специалисты
@@ -171,8 +177,9 @@ Legacy: прямой `POST /v1/calendars/:id/specialists` **не использ
(иначе `400`).
- **Confirm/decline booking:** владелец — любые booking календаря; `active` specialist — только
booking на событиях, где `event.specialist_id` = его `user_id`.
- **Inbox к подтверждению:** `GET /v1/user/booking-requests` агрегирует pending по тем же
правилам (owner — все события своих календарей; specialist — только свои слоты).
- **Inbox к подтверждению:** `GET /v1/user/booking-requests` агрегирует **actionable** pending
по тем же правилам (owner — все события своих календарей; specialist — только свои слоты);
past-pending → `expired` и из ответа исключается.
#### Фаза 2 (вне текущего контракта реализации)
- `calendar_share` (`read` | `write` | `admin`): не путать с follow, specialist_invite и платной subscription.
@@ -247,12 +254,17 @@ Legacy: прямой `POST /v1/calendars/:id/specialists` **не использ
- Запись доступна только на событиях **commercial**-календаря с `booking_open=true` (§2.1.2).
- На personal → `403` (`personal_calendar`); при restricted commercial → `403` (`subscription_inactive`).
- В зависимости от `confirmation` календаря: auto / manual / timeout (§2.1.2).
- Статусы: `pending`, `confirmed`, `cancelled`.
- **Capacity:** лимит слотов считают `pending` + `confirmed` (pending резервирует место).
- Участник может отменить свою запись (`DELETE`).
- Статусы: `pending`, `confirmed`, `cancelled`, `expired`.
- **Capacity:** лимит слотов считают `pending` + `confirmed` (pending резервирует место;
`cancelled` / `expired` не занимают).
- **Истечение:** past-pending (событие уже началось) → `expired` (lazy + timeout job); не
путать с `specialist_invite.status=expired`.
- Участник может отменить свою запись (`DELETE`) для pending/confirmed; `expired`/`cancelled`
идемпотентный успех.
- Confirm/decline: владелец календаря — любые заявки; active specialist — только на событиях
со своим `specialist_id` (§2.1.2).
со своим `specialist_id` (§2.1.2). На `expired` (и после lazy-mark) → `409`.
- При подтверждении фиксируется `confirmed_at`.
- Inbox `GET /v1/user/booking-requests` — только pending до старта события (§2.1.2).
### 2.4. Отзывы и рейтинги
- Пользователи могут оставлять отзывы (рейтинг 1–5 и комментарий) на события или календари.
@@ -456,11 +468,11 @@ src/
пара `current_password` + `password` (неверный текущий → `403`). Нельзя менять
email/role/status; неизвестные поля → `400`. Ответ — полный профиль как GET.
- `GET /v1/user/bookings` — бронирования пользователя **как участника**.
- `GET /v1/user/booking-requests` — pending-заявки к подтверждению, где текущий
пользователь — **owner** календаря события или **assigned specialist**
(`event.specialist_id` = user и specialist active). Ответ — массив объектов
booking + `role` (`owner`|`specialist`) + вложенный `event`
(`id`, `calendar_id`, `calendar_title`, `title`, `start_time`, `duration`,
- `GET /v1/user/booking-requests` **actionable** pending-заявки к подтверждению, где
текущий пользователь — **owner** календаря события или **assigned specialist**
(`event.specialist_id` = user и specialist active). Past-pending помечается `expired` и
**не** попадает в ответ. Ответ — массив объектов booking + `role` (`owner`|`specialist`) +
вложенный `event` (`id`, `calendar_id`, `calendar_title`, `title`, `start_time`, `duration`,
`specialist_id`). Confirm/decline — существующий `PUT /v1/bookings/:id`.
- `GET /v1/user/reviews` — отзывы пользователя.
- `GET /v1/user/following` — календари, которые пользователь отслеживает (follow).
@@ -494,7 +506,9 @@ src/
- `GET /v1/bookings/:id` — статус бронирования.
- `PUT /v1/bookings/:id` — подтвердить/отклонить (`confirm`|`decline`); владелец —
любые booking календаря; active specialist — только события со своим `specialist_id`.
- `DELETE /v1/bookings/:id` — отменить бронирование (участник).
На past-pending / уже `expired``409` (`Booking expired`); при полной вместимости на
confirm → `409` (`Event is full`).
- `DELETE /v1/bookings/:id` — отменить бронирование (участник; `expired`/`cancelled` — no-op `200`).
- `POST /v1/reviews` — создать отзыв.
- `GET /v1/reviews` — список отзывов (поле `my_vote` для текущего пользователя).
- `GET /v1/reviews/:id` — отзыв по ID (`my_vote`).