Docs: sync booking-requests + Bookings IA + image_url after Back#59/Front#30. Fixes EventHub/EventHubSpec#12
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
---
|
||||
description: EventHub — локальные сборки/тесты только через WSL (Windows toolchain сломан)
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
# EventHub: рабочее окружение агента
|
||||
|
||||
На этой машине **не использовать** нативный Windows для:
|
||||
|
||||
| Стек | Нельзя (Windows) | Нужно |
|
||||
|------|------------------|--------|
|
||||
| Front / FrontAdmin | `npm`, `npx`, Playwright | **WSL** `npm` |
|
||||
| Back (Erlang) | Windows `erl` / `rebar3` | **WSL** + `scripts/wsl-dev-env.sh` |
|
||||
|
||||
## Front (EventHubFront)
|
||||
|
||||
```bash
|
||||
wsl -e bash -lc 'cd /mnt/c/Users/alexc/IdeaProjects/eventHub/EventHubFront && npm run lint && npm run build && npm run test:e2e'
|
||||
```
|
||||
|
||||
## FrontAdmin
|
||||
|
||||
```bash
|
||||
wsl -e bash -lc 'cd /mnt/c/Users/alexc/IdeaProjects/eventHub/EventHubFrontAdmin && npm run lint && npm run build'
|
||||
```
|
||||
|
||||
## Back
|
||||
|
||||
```bash
|
||||
wsl -e bash -lc 'cd /mnt/c/Users/alexc/IdeaProjects/eventHub/EventHubBack && source scripts/wsl-dev-env.sh && rebar3 eunit'
|
||||
```
|
||||
|
||||
Подробности: `EventHubFront/.cursor/rules/npm-wsl.mdc`, `EventHubBack/.cursor/rules/otp-rebar-wsl.mdc`.
|
||||
@@ -0,0 +1,29 @@
|
||||
---
|
||||
description: Один агент — один репозиторий; не смешивать Front и Back
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
# EventHub: отдельные агенты по репозиториям
|
||||
|
||||
## Правило
|
||||
При работе с **несколькими репозиториями** EventHub запускай **отдельного агента на каждый репо**. Не смешивай Front и Back (и Spec/FrontAdmin) в одном агенте.
|
||||
|
||||
| Репо | Зона |
|
||||
|------|------|
|
||||
| `EventHubFront` | клиентский SPA |
|
||||
| `EventHubBack` | Erlang API |
|
||||
| `EventHubSpec` | спеки, UI-PARITY, контракты |
|
||||
| `EventHubFrontAdmin` | admin SPA |
|
||||
|
||||
## Запрещено
|
||||
- Один агент правит и Front, и Back в одной сессии
|
||||
- Коммиты/`Refs`/`Fixes` не в тот репо (Front-код → issue Back, и наоборот)
|
||||
- Закрывать/комментировать issue чужого репо «заодно»
|
||||
|
||||
## Как делать
|
||||
1. Спека/issue → агент **Spec** (или короткий foreground), если только доки.
|
||||
2. API → отдельный агент **Back** (`Back#N`, eunit, push Back).
|
||||
3. UI → отдельный агент **Front** (`Front#N`, lint/IFT, push Front).
|
||||
4. Родитель-координатор только маршрутизирует; не пишет код сразу в два репо.
|
||||
|
||||
Кросс-репо фича (например Bookings inbox): **сначала Back**, потом **Front** (или параллельно двумя агентами), Spec — третьим или после стабилизации API.
|
||||
@@ -0,0 +1,13 @@
|
||||
---
|
||||
description: Shell — только WSL/bash; PowerShell-экранирование запрещено для сложного
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
# Shell: WSL, не PowerShell
|
||||
|
||||
Агенты **не должны** гонять сложные one-liner’ы в PowerShell (ломается экранирование).
|
||||
|
||||
Канон: файл `.sh` + `wsl -e bash /mnt/c/.../script.sh`, либо
|
||||
`wsl -e bash -lc 'простая команда без вложенных двойных кавычек'`.
|
||||
|
||||
Полный текст: `EventHubBack/.cursor/rules/shell-wsl-not-powershell.mdc`.
|
||||
+13
-2
@@ -171,6 +171,8 @@ 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 — только свои слоты).
|
||||
|
||||
#### Фаза 2 (вне текущего контракта реализации)
|
||||
- `calendar_share` (`read` | `write` | `admin`): не путать с follow, specialist_invite и платной subscription.
|
||||
@@ -453,10 +455,18 @@ src/
|
||||
`nickname`, `timezone`, `phone`, `avatar_url`, `preferences`; смена пароля —
|
||||
пара `current_password` + `password` (неверный текущий → `403`). Нельзя менять
|
||||
email/role/status; неизвестные поля → `400`. Ответ — полный профиль как GET.
|
||||
- `GET /v1/user/bookings` — бронирования пользователя.
|
||||
- `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`,
|
||||
`specialist_id`). Confirm/decline — существующий `PUT /v1/bookings/:id`.
|
||||
- `GET /v1/user/reviews` — отзывы пользователя.
|
||||
- `GET /v1/user/following` — календари, которые пользователь отслеживает (follow).
|
||||
- `GET /v1/search` — поиск; пустой запрос (только auth + пагинация/`type`) — discovery tops.
|
||||
В calendar-результатах — `image_url` (если задан на календаре). Upload/multipart
|
||||
**не** входит в контракт: поле URL.
|
||||
- `GET /v1/calendars` — список календарей.
|
||||
- `POST /v1/calendars` — создать календарь (`commercial` → подписка/trial; иначе `402`).
|
||||
- `GET /v1/calendars/:id` — календарь (`following`, `booking_open` для текущего контекста).
|
||||
@@ -482,7 +492,8 @@ src/
|
||||
- `POST /v1/events/:id/bookings` — запись на событие.
|
||||
- `GET /v1/events/:id/bookings` — список бронирований события (владелец).
|
||||
- `GET /v1/bookings/:id` — статус бронирования.
|
||||
- `PUT /v1/bookings/:id` — подтвердить/отклонить бронирование (владелец).
|
||||
- `PUT /v1/bookings/:id` — подтвердить/отклонить (`confirm`|`decline`); владелец —
|
||||
любые booking календаря; active specialist — только события со своим `specialist_id`.
|
||||
- `DELETE /v1/bookings/:id` — отменить бронирование (участник).
|
||||
- `POST /v1/reviews` — создать отзыв.
|
||||
- `GET /v1/reviews` — список отзывов (поле `my_vote` для текущего пользователя).
|
||||
|
||||
+19
-5
@@ -51,6 +51,9 @@ Deep-link на календарь, владельцем которого явл
|
||||
|
||||
- Главная сущность — **календарь**. Центр UI — виджет с видами **месяц** (default) / **неделя** / **день**.
|
||||
- Главные вкладки: **Календарь** (`/`, `/c/:id`), **Найти** (`/search`), **Записи** (`/bookings`), **Ещё** (`/more`).
|
||||
- **Записи** (`/bookings`) — grouped inbox: «К подтверждению» (owner/specialist pending + Confirm/Decline)
|
||||
и «Мои записи» (participant); пустые группы скрывать. Confirm на карточке события остаётся.
|
||||
- В **Ещё**: пункт входящих specialist invites скрыт при `pending=0` (deep-link `/invites` работает).
|
||||
- Контекст виджета: свой календарь (селектор) или чужой (browse после поиска). Чужой `personal` —
|
||||
только просмотр; чужой `commercial` с `booking_open=true` — запись; при `booking_open=false`
|
||||
(restricted / нет подписки владельца) — просмотр + сообщение «Запись временно недоступна».
|
||||
@@ -96,9 +99,12 @@ Deep-link на календарь, владельцем которого явл
|
||||
- `/c/:calendarId` — workspace с календарём
|
||||
- `/c/:calendarId/e/:eventId` — workspace + карточка события
|
||||
- `/search` — поиск календарей/событий
|
||||
- `/bookings` — мои бронирования (deep-link в `/c/.../e/...`)
|
||||
- `/bookings` — grouped inbox: (A) к подтверждению (owner/specialist pending) +
|
||||
(B) мои записи участника; deep-link в `/c/.../e/...`
|
||||
- `/calendars` — управление своими календарями (CRUD)
|
||||
- `/more` — вторичное меню + mood switcher
|
||||
- `/more` — вторичное меню + mood switcher; пункт specialist invites **скрыт**,
|
||||
если входящих pending = 0 (deep-link `/invites?token=` работает)
|
||||
- `/invites` — inbox входящих specialist invites (не смешивать с `/bookings`)
|
||||
- `/following` — отслеживаемые чужие календари (follow; не платная Подписка)
|
||||
- `/reviews`, `/subscription`, `/tickets`, `/profile`
|
||||
|
||||
@@ -116,6 +122,7 @@ Redirects: `/discover` → `/search`; `/calendars/:id` → `/c/:id`; `/calendars
|
||||
|
||||
### 5.3. Поиск (участник)
|
||||
`GET /v1/search` — фильтры (`type`, `q`, `tags`, даты, `lat`/`lon`/`radius`, sort, order, пагинация). Без `q`/дат/tags/geo/sort — топы календарей и событий; иначе — поиск. В списке бейдж типа — `calendar`|`event` (не `personal`/`commercial`). Даты, теги, geo и сортировка свёрнуты по умолчанию. Переход к `/c/:id` или `/c/:id/e/:eventId`.
|
||||
В результатах calendar (и event, если Back отдаёт) может быть `image_url` — Discover показывает cover; иначе mood wash + initials. **Upload API нет** — поле URL (seed / ручная установка).
|
||||
|
||||
### 5.4. Календари
|
||||
- Список своих: `GET /v1/calendars`
|
||||
@@ -132,8 +139,10 @@ Redirects: `/discover` → `/search`; `/calendars/:id` → `/c/:id`; `/calendars
|
||||
список active + исходящие pending; deactivate/remove принятых.
|
||||
Выбор `specialist_id` в форме события (только `active`).
|
||||
Баннер «подписка истекла / истекает» при `booking_open=false`.
|
||||
- Invitee: inbox входящих `GET /v1/user/specialist-invites` (More / уведомления) —
|
||||
- Invitee: inbox входящих `GET /v1/user/specialist-invites` на `/invites` —
|
||||
принять / отклонить; deep-link из email → accept по `token` после логина.
|
||||
В More пункт «Приглашения» показывается **только** при `pending > 0`
|
||||
(не путать с booking-заявками на `/bookings`).
|
||||
|
||||
### 5.5. События
|
||||
- Список: `GET /v1/calendars/:calendar_id/events`
|
||||
@@ -145,8 +154,13 @@ Redirects: `/discover` → `/search`; `/calendars/:id` → `/c/:id`; `/calendars
|
||||
### 5.6. Запись (bookings)
|
||||
- Участник: `POST /v1/events/:id/bookings` только при `booking_open`; иначе UI-сообщение /
|
||||
ответ `403`; `GET /v1/user/bookings`, `DELETE /v1/bookings/:id`, `GET /v1/bookings/:id`
|
||||
- Владелец и specialist: `GET /v1/events/:id/bookings`, `PUT /v1/bookings/:id`
|
||||
`{ action: confirm | decline }` (specialist — только свои события, см. BackSpec §2.1.2)
|
||||
- Владелец и specialist:
|
||||
- на карточке события: `GET /v1/events/:id/bookings` + Confirm/Decline (**обязательно**)
|
||||
- inbox `/bookings` группа «К подтверждению»: `GET /v1/user/booking-requests`
|
||||
(pending, где user — owner календаря или `event.specialist_id`); те же
|
||||
`PUT /v1/bookings/:id` `{ action: confirm | decline }`
|
||||
- UI `/bookings`: две группы (пустые скрывать): **К подтверждению** + **Мои записи**
|
||||
(upcoming/past participant). `specialist_invite` сюда **не** попадает.
|
||||
- Статусы: `pending` | `confirmed` | `cancelled`; confirmation: `auto` | `manual` | timeout
|
||||
- Pending резервирует capacity (как на бэке)
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
# Stage Popular cleanup notes
|
||||
|
||||
Popular / Discover empty-search feed = ratings tops (`GET /v1/search` without `q`/filters).
|
||||
E2E и smoke иногда оставляют commercial-календари с рейтингами, из‑за чего лента засоряется.
|
||||
|
||||
## Правила
|
||||
|
||||
1. **Не** удалять «живые» smoke-календари пилота без явного фильтра.
|
||||
2. Чистить только по безопасным маркерам (название/тег/описание содержат `e2e`, `E2E`, `playwright`, `ift-litter`, или owner = известный e2e user).
|
||||
3. Soft-delete через API (`DELETE /v1/calendars/:id` владельцем / admin), не ручной wipe Mnesia.
|
||||
4. Front already tracks calendars created in-session via `e2e/helpers/standCleanup.ts` — предпочитать это для новых тестов.
|
||||
|
||||
## Suggested filter (admin / ops)
|
||||
|
||||
- title/tags match: `(?i)e2e|playwright|ift-litter|tmp-`
|
||||
- created recently + zero real bookings, if policy allows
|
||||
- exclude calendars used by documented stage smoke accounts
|
||||
|
||||
## Upload / cover images
|
||||
|
||||
`image_url` на календаре отдаётся в search. **Upload API нет** — seed URL вручную или через admin/update поля. Discover показывает cover если URL непустой, иначе mood wash + initials.
|
||||
|
||||
Refs: EventHubSpec#12, EventHubBack#59, EventHubFront#30.
|
||||
@@ -362,6 +362,7 @@ error rate > 1% **или** mean request > 500 ms (hold ≥2 мин в фазе)
|
||||
- После успешных тестов — обновить спеку, если менялся внешний контракт.
|
||||
- Одна задача — один логический объём работы.
|
||||
- Код фичи — в репозитории-владельце; `EventHubSpec` — для документации и процесса.
|
||||
- **Один агент — один репо** (не смешивать Front/Back/Spec/FrontAdmin в одной сессии): `.cursor/rules/one-repo-per-agent.mdc`.
|
||||
- Метки во всех репозиториях держать одинаковыми (`sync-labels.ps1`).
|
||||
- Секреты и токены не коммитить; Gitea token — только в env / `~/.cursor/secrets/`.
|
||||
- **Git commit/push — через WSL** (`~/.cursor/eventhub/git-commit.sh`, `git-push.sh`); **push — только после подтверждения пользователя** (см. §2.1).
|
||||
@@ -377,6 +378,7 @@ error rate > 1% **или** mean request > 500 ms (hold ≥2 мин в фазе)
|
||||
|
||||
| Дата | Изменение |
|
||||
|------|-----------|
|
||||
| 2026-07-27 | Spec#12: booking-requests + Bookings IA + image_url; правило one-repo-per-agent |
|
||||
| 2026-07-27 | §6 / STAGE-BROWSER: канон — прямой HTTPS `*.calentiq.com`; proxy `:8787` только DNS-fallback |
|
||||
| 2026-07-22 | §6: Stage UI в Cursor Browser — `STAGE-BROWSER.md` + alias proxy |
|
||||
| 2026-07-22 | §2.1: Agent shell — не PowerShell для сложного; `.sh` + WSL; Front push только после lint/build/IFT |
|
||||
|
||||
@@ -17,7 +17,7 @@ SoT: [`boards/calentiq-ui-board-final.png`](boards/calentiq-ui-board-final.png).
|
||||
| Pixel-perfect calendar | denser board mock + Today/Upcoming rail | Agenda rail (§B): title-case Сегодня/Предстоящие + count, time-first rows; month chips/cell wash (§B/D) по mood; не 1:1 Figma |
|
||||
| Discover cards | soft cards + media thumb + CTA | mood media plane (wash/initials + `image_url` when present) + soft-card rhythm + CTA; без full photo-hero grid |
|
||||
| CONTROL booking §E | отдельные mock-карточки «слот/специалист» | `EventActionCard` / specialists / bookings: steel rail (усилен), avatar, available label, Confirm/Decline; не pixel-perfect board mocks |
|
||||
| Bookings list §D | список заявок с Confirm/Decline на `/bookings` | `/bookings` = заявки **участника** (Отменить); owner Confirm/Decline — в карточке события (`EventActionCard` / Заявки) |
|
||||
| Bookings list §D | список заявок с Confirm/Decline на `/bookings` | `/bookings` = **grouped inbox**: (A) К подтверждению — pending owner/specialist + Confirm/Decline; (B) Мои записи — participant upcoming/past + Cancel; empty groups hidden. Event card Confirm/Decline **остаётся** обязательным |
|
||||
| Login §D phone chrome | три phone frames + (на борде) Google CTA | brand-first hero + mood `--auth-*`; email/password only; mood switcher на login (иконки); taglines mood cards — на `/more`; **без** Google OAuth |
|
||||
| Favicon set | 3 отдельных favicon файла на борде | mood appicon через `applyMood` |
|
||||
| Desktop nav | phone bottom tabs на борде | desktop: top tabs (Календарь / Найти / Записи / Ещё); bottom nav — mobile |
|
||||
@@ -32,7 +32,30 @@ IA без sidebar / без Creator mood — совпадает с non-goals (п
|
||||
|
||||
**Polish in this pass:** agenda copy/casing/counts + time-first rows; CONTROL steel rail emphasis; neutral Discover cover in mock (не flow-appicon во всех moods).
|
||||
|
||||
**Remaining intentional:** no pixel-perfect phone chrome; Discover без photo-hero grid; owner Confirm/Decline не на `/bookings` list.
|
||||
**Remaining intentional:** no pixel-perfect phone chrome; Discover без photo-hero grid (cover via `image_url` when API returns it); §E specialist schedule card vs panel — не pixel-perfect board mocks (follow-up).
|
||||
|
||||
### Bookings inbox + More invites (locked 2026-07-27)
|
||||
|
||||
Продуктовые решения (не deviation — канон):
|
||||
|
||||
1. **Event card Confirm/Decline** остаётся обязательным (quick approve на слоте).
|
||||
2. **`/bookings`** — grouped inbox, не только participant:
|
||||
- **Группа A — К подтверждению:** pending bookings, где пользователь calendar **owner** или **assigned specialist** (`event.specialist_id` = user). Confirm/Decline те же, что на `EventActionCard`.
|
||||
- **Группа B — Мои записи:** participant bookings (upcoming/past + Cancel) как раньше.
|
||||
- Пустые группы скрывать (`count=0`).
|
||||
- Опционально подпись owner vs specialist внутри A; иначе одна секция «К подтверждению».
|
||||
3. **`specialist_invite` в More:** скрывать пункт меню, если **0 pending** входящих. Deep-link `/invites?token=` работает. Исходящие invite остаются в `SpecialistsPanel`.
|
||||
4. **Не** класть строки `specialist_invite` в `/bookings`.
|
||||
|
||||
API: `GET /v1/user/booking-requests` (Back) + существующий `PUT /v1/bookings/:id`.
|
||||
|
||||
### Follow-ups (вне этого fix-pass или частично)
|
||||
|
||||
| Тема | Статус |
|
||||
|------|--------|
|
||||
| Discover `image_url` в search serialization | Back отдаёт поле; upload API **нет** — только URL field / seed |
|
||||
| Stage Popular clutter (e2e calendars) | filter notes: см. `EventHubSpec/STAGE-POPULAR-CLEANUP.md`; не трогать реальные smoke данные без фильтра по e2e-паттернам |
|
||||
| §E specialist schedule card vs panel | deviation: EventActionCard + SpecialistsPanel; отдельный pixel mock — Future |
|
||||
|
||||
## Mood concept = product surface
|
||||
|
||||
|
||||
Reference in New Issue
Block a user