docs: Time Arc v3 following and discover when. Refs EventHub/EventHubFront#52
This commit is contained in:
+68
-40
@@ -59,39 +59,50 @@ Deep-link на календарь, владельцем которого явл
|
|||||||
`pending=0`; при `pending > 0` — badge со счётчиком (deep-link `/invites` работает).
|
`pending=0`; при `pending > 0` — badge со счётчиком (deep-link `/invites` работает).
|
||||||
**Mood в Ещё нет** — chip в AppShell рядом с BrandWordmark (Front#40; ранее Front#39
|
**Mood в Ещё нет** — chip в AppShell рядом с BrandWordmark (Front#40; ранее Front#39
|
||||||
в chrome workspace).
|
в chrome workspace).
|
||||||
- **Mood + Lens (Front#39/#40 + controls IA):** mood chip (FLOW/MOMENTUM/CONTROL → sheet picker) —
|
- **Mood + Lens (Front#39/#40 + Front#46):** mood chip (FLOW/MOMENTUM/CONTROL → sheet picker) —
|
||||||
в **AppShell** рядом с логотипом (**не** в toolbar Month/Week/Day). Workspace chrome
|
в **AppShell** рядом с логотипом (**не** в toolbar Month/Week/Day). **Lens** — инфо-табло
|
||||||
разнесён по зонам: (1) селектор календаря; (2) период + вид Месяц/Неделя/День;
|
**под строкой AppShell** (logo/mood/nav) на calendar workspace: сегменты Обзор / Сегодня /
|
||||||
(3) owner schedule actions («+ Новое событие», «Заполнить расписание» — отдельный ряд);
|
Заявки + метрики/chips (не рядом с ViewMode). Soft-default по mood **без auto-pin** при
|
||||||
(4) lens «Обзор / На сегодня / Заявки» — **в шапке** collapsible lens strip (underline/text
|
клике на вкладку; при смене mood — `DEFAULT_LENS_BY_MOOD`. Workspace chrome: (1) селектор;
|
||||||
tabs на desktop, sheet на mobile; **не** рядом с ViewMode chips). Strip — Front-only derive
|
(2) период + вид Месяц/Неделя/День; (3) owner actions — для commercial только
|
||||||
из уже загруженных events/bookings. Без flip календаря; без 4-го CREATOR. Strip по умолчанию
|
«+ Новое событие» («Заполнить расписание» — во вкладке Студия). Без flip / CREATOR.
|
||||||
раскрыт на desktop, свёрнут на mobile. Нет на HTML-архиве месяца.
|
Нет lens на HTML-архиве месяца / не-calendar routes.
|
||||||
- **Week view (Front#40):** одна строка day-headers (без дубля заголовков
|
- **Week view (Front#40):** одна строка day-headers (без дубля заголовков
|
||||||
`WeekDayColumn`); горизонтальный скролл через `.eh-cal-week-scroll` (mobile ~390).
|
`WeekDayColumn`); горизонтальный скролл через `.eh-cal-week-scroll` (mobile ~390).
|
||||||
- Контекст виджета: свой календарь (селектор) или чужой (browse после поиска). Чужой `personal` —
|
- Контекст виджета: свой календарь (селектор) или browse чужого (после поиска). Чужой `personal` —
|
||||||
только просмотр; чужой `commercial` с `booking_open=true` — запись; при `booking_open=false`
|
только просмотр; чужой `commercial` с `booking_open=true` — запись; при `booking_open=false`
|
||||||
(restricted / нет подписки владельца) — просмотр + сообщение «Запись временно недоступна».
|
— просмотр + «Запись временно недоступна». Бейдж «чужой» **не** показывать; CTA
|
||||||
- **Свой default personal:** поверх сетки — overlay confirmed записей на чужие commercial
|
**«Мой календарь»** + `ChevronLeft` → `/default`.
|
||||||
слоты (Front-only: `GET /v1/user/bookings` → events/calendars). Заголовок
|
- **Единственный personal** (Back: title `Default`, UI «По умолчанию»): overlay confirmed
|
||||||
`событие · студия · специалист`; клик → `/c/:foreignCalId/e/:eventId` (не в своём cal).
|
записей на чужие commercial + specialist duty (Front#35/#43) — **только** на нём, не на
|
||||||
Дополнительно (Front#43): overlay **своих** рабочих слотов commercial, где
|
студиях. Create calendar UI — только тип **Студия** (`commercial`). Селектор: «По умолчанию»
|
||||||
`specialist_id` = текущий user — gate через accepted `GET /v1/user/specialist-invites`
|
+ студии.
|
||||||
+ проверка active в `GET …/specialists`, затем `GET …/events?from&to`; title
|
|
||||||
`событие · студия`. Personal владельца без accepted invite / без своих слотов
|
|
||||||
чужой roster **не** зеркалит.
|
|
||||||
- **Guest на чужом commercial (D9 модель B):** roster карточек активных специалистов →
|
- **Guest на чужом commercial (D9 модель B):** roster карточек активных специалистов →
|
||||||
тап → сетка только со слотами `event.specialist_id` = выбранный; «Все слоты студии»
|
тап → сетка только со слотами `event.specialist_id` = выбранный; «Все слоты студии»
|
||||||
снимает фильтр. Это **не** chip-filter на общей сетке. Book как обычно — без
|
снимает фильтр. Это **не** chip-filter на общей сетке. Book как обычно — без
|
||||||
`specialist` в body заявки (слот уже привязан к спецу на событии).
|
`specialist` в body заявки (слот уже привязан к спецу на событии).
|
||||||
- Agenda rail: empty copy зависит от типа календаря — personal «Нет событий», commercial
|
В режиме «Все» — тот же **studio-агрегат** слотов, что у owner «Студия» (см. ниже);
|
||||||
«Нет слотов».
|
фильтр D9 **без** второго UI режима.
|
||||||
- Выбор события открывает карточку действий: mobile — bottom sheet; desktop — боковая панель. Действия зависят от роли (owner / participant).
|
- **Owner commercial workspace — режимы Студия / Мастер:**
|
||||||
- Без выбранного события — панель «О календаре» (описание, title/meta, рейтинг, отзывы).
|
- **Студия** — агрегат слотов по `start_time` + `duration`: одна ячейка с заливкой
|
||||||
Форма отзыва на календарь/событие — только при confirmed booking; жалоба доступна без записи.
|
`booked/total`, label `HH:mm · K/N свободно`; клик → popover мастеров со статусами
|
||||||
На чужом календаре — CTA **«Отслеживать» / Follow** (не путать с платной «Подписка»);
|
(занятость по `booking_occupancy` / active bookings, BackSpec §6 events).
|
||||||
follow не открывает форму отзыва.
|
- **Мастер** — фильтр сетки по `specialist_id`; sticky roster как guest D9 B
|
||||||
- На чужом `/c/:id` — CTA «Мой календарь» и таб Calendar не `aria-current` (intent `browse`).
|
(**owner тоже** использует roster-фильтр, не только guest).
|
||||||
|
- **Месяц + Студия:** density markers в ячейках дня, **не** список чипов слотов.
|
||||||
|
- **Personal:** без studio-агрегата и без вкладки «Команда» / Team.
|
||||||
|
- ★ `0.0` на free-слотах **не** показывать.
|
||||||
|
- Agenda / rail desktop (commercial owner): вкладки **Расписание | Команда | Студия** в
|
||||||
|
общем `.eh-owner-rail-panel` **фиксированной высоты** (вкладки не прыгают). Команда:
|
||||||
|
invite свёрнут за «Пригласить». **Студия** = hub: about + «Заполнить расписание» +
|
||||||
|
«Редактировать» (+ link `/calendars`); Delete только на `/calendars`. Mobile: segmented
|
||||||
|
**День | Команда | Студия** под grid. Списки — `ScrollRegion` (полоса скрыта, стрелки
|
||||||
|
по краям при overflow); dialogs — thin scrollbar on hover.
|
||||||
|
- Agenda empty: personal «Нет событий»; commercial «Свободных окон нет». UI-тип commercial —
|
||||||
|
«Студия» (не «коммерческий»).
|
||||||
|
- Выбор события открывает карточку действий: mobile — bottom sheet; desktop — боковая панель.
|
||||||
|
- На browse `/c/:id` — CTA «Мой календарь» (+ иконка) → `/default`; tab Calendar не
|
||||||
|
`aria-current`. Follow на чужом commercial без изменений.
|
||||||
- Список отслеживаемых: `/following` (из «Ещё»).
|
- Список отслеживаемых: `/following` (из «Ещё»).
|
||||||
- Открытие события — `navigate` push (не `replace`), чтобы Back возвращал к календарю.
|
- Открытие события — `navigate` push (не `replace`), чтобы Back возвращал к календарю.
|
||||||
- Owner: WS subscribe на активный календарь; `PUT` события из карточки; `PUT` календаря на `/calendars`.
|
- Owner: WS subscribe на активный календарь; `PUT` события из карточки; `PUT` календаря на `/calendars`.
|
||||||
@@ -127,12 +138,10 @@ Deep-link на календарь, владельцем которого явл
|
|||||||
Time Arc appicon — только favicon, не в switcher.
|
Time Arc appicon — только favicon, не в switcher.
|
||||||
|
|
||||||
**Lens** (отдельно от mood и от view month/week/day): `overview` / `today` / `bookings`
|
**Lens** (отдельно от mood и от view month/week/day): `overview` / `today` / `bookings`
|
||||||
(UI: Обзор / На сегодня / Заявки). Переключатель — в шапке lens strip (не в toolbar рядом
|
(UI: Обзор / Сегодня / Заявки). Инфо-табло **под AppShell** на calendar routes (не в
|
||||||
с Месяц/Неделя/День). Persist только `localStorage` (`eh.calendar.lens` +
|
toolbar ViewMode). Persist: session preference + soft LS; клик по вкладке **не** ставит
|
||||||
`eh.calendar.lens.pinned`); **не** поле профиля / Back API. Soft-default при смене mood,
|
pin навсегда; при смене mood — soft-default FLOW→overview, MOMENTUM→today,
|
||||||
если lens не pinned пользователем: FLOW→overview, MOMENTUM→today, CONTROL→bookings.
|
CONTROL→bookings. Контент: free gaps / события сегодня / upcoming bookings (Front-derive).
|
||||||
Strip над сеткой: free gaps сегодня / события сегодня / upcoming bookings (derive
|
|
||||||
на клиенте из уже загруженных данных).
|
|
||||||
|
|
||||||
Язык UI (`ru`/`en`): до логина — `navigator.language`; после входа — поле `language` профиля (`PATCH /v1/user/me`). Если в профиле язык пуст — при логине записывается текущий (браузерный/выбранный на экране входа). Язык также в `/more` и в форме `/profile`.
|
Язык UI (`ru`/`en`): до логина — `navigator.language`; после входа — поле `language` профиля (`PATCH /v1/user/me`). Если в профиле язык пуст — при логине записывается текущий (браузерный/выбранный на экране входа). Язык также в `/more` и в форме `/profile`.
|
||||||
|
|
||||||
@@ -144,19 +153,23 @@ Strip над сеткой: free gaps сегодня / события сегод
|
|||||||
|
|
||||||
**v2 (Front#51):** отказ от дуги (тап другой свободной ячейки / свайп недели) пишет штраф часу и мастеру в `localStorage` (`eh.timeArc.skips`) — дуга переезжает. ≥2 confirmed записей к одному `specialist_id` бустят его слоты. Месяц: тепло `data-time-arc-heat` на днях со скоренным часом. Personal: призрак «тот же час +7д» после прошедшего confirmed визита; тап открывает студию.
|
**v2 (Front#51):** отказ от дуги (тап другой свободной ячейки / свайп недели) пишет штраф часу и мастеру в `localStorage` (`eh.timeArc.skips`) — дуга переезжает. ≥2 confirmed записей к одному `specialist_id` бустят его слоты. Месяц: тепло `data-time-arc-heat` на днях со скоренным часом. Personal: призрак «тот же час +7д» после прошедшего confirmed визита; тап открывает студию.
|
||||||
|
|
||||||
|
**v3 (дуга между календарями):** `/following` сортирует студии по ближайшему Time Arc; тап открывает неделю студии с курсором на этом дне (`data-time-arc` как в v1). Discover остаётся списком: на строке calendar — «когда» (`data-time-arc-when`), без общей сетки нескольких студий. Ghosts владельца считаются по активной студии (переключение календаря не смешивает паттерны). По-прежнему без LLM / Back AI.
|
||||||
|
|
||||||
## 4. Маршруты
|
## 4. Маршруты
|
||||||
|
|
||||||
Публичные:
|
Публичные:
|
||||||
- `/login`, `/register`, `/verify`
|
- `/login`, `/register`, `/verify`
|
||||||
|
|
||||||
Защищённые (`ProtectedRoute`):
|
Защищённые (`ProtectedRoute`):
|
||||||
- `/` — Calendar Workspace (автовыбор своего календаря или empty-state)
|
- `/default` — resolve единственного personal → `/c/:id` (post-login и tab «Календарь»)
|
||||||
|
- `/default/e/:eventId` — то же + карточка события
|
||||||
|
- `/` — alias → `/default`
|
||||||
- `/c/:calendarId` — workspace с календарём
|
- `/c/:calendarId` — workspace с календарём
|
||||||
- `/c/:calendarId/e/:eventId` — workspace + карточка события
|
- `/c/:calendarId/e/:eventId` — workspace + карточка события
|
||||||
- `/search` — поиск календарей/событий
|
- `/search` — поиск календарей/событий
|
||||||
- `/bookings` — grouped inbox: (A) к подтверждению (owner/specialist pending) +
|
- `/bookings` — grouped inbox: (A) к подтверждению (owner/specialist pending) +
|
||||||
(B) мои записи участника; deep-link в `/c/.../e/...`
|
(B) мои записи участника; deep-link в `/c/.../e/...`
|
||||||
- `/calendars` — управление своими календарями (CRUD)
|
- `/calendars` — управление своими календарями (create только Студия; personal delete UI скрыт)
|
||||||
- `/more` — вторичное меню + language switcher (**без** mood); пункт specialist
|
- `/more` — вторичное меню + language switcher (**без** mood); пункт specialist
|
||||||
invites **скрыт**, если входящих pending = 0; при pending > 0 — badge со счётчиком
|
invites **скрыт**, если входящих pending = 0; при pending > 0 — badge со счётчиком
|
||||||
(deep-link `/invites?token=` работает)
|
(deep-link `/invites?token=` работает)
|
||||||
@@ -193,24 +206,39 @@ cover (`.eh-discover-media--photo`); иначе mood wash + initials (`.eh-disco
|
|||||||
- Создание / апгрейд `commercial` без **уже active** sub/trial → `402` → `/subscription`
|
- Создание / апгрейд `commercial` без **уже active** sub/trial → `402` → `/subscription`
|
||||||
(trial — явный `start_trial`, не auto при create); после оплаты —
|
(trial — явный `start_trial`, не auto при create); после оплаты —
|
||||||
возврат к созданию/редактированию
|
возврат к созданию/редактированию
|
||||||
- Владелец commercial: специалисты **через invite** (не сырой `user_id`):
|
- Владелец commercial: команда — owner-строка specialist (создаётся бэком при create
|
||||||
|
commercial) + остальные **через invite** (не сырой `user_id`):
|
||||||
typeahead `GET /v1/users/lookup` и/или email → `POST …/specialist-invites`;
|
typeahead `GET /v1/users/lookup` и/или email → `POST …/specialist-invites`;
|
||||||
список active + исходящие pending; deactivate/remove принятых.
|
список + исходящие pending; deactivate/remove принятых (**не** owner).
|
||||||
Выбор `specialist_id` в форме события (только `active`).
|
Выбор `specialist_id` в форме события (только `active`).
|
||||||
Баннер «подписка истекла / истекает» при `booking_open=false`.
|
Баннер «подписка истекла / истекает» при `booking_open=false`.
|
||||||
`SpecialistsPanel` (owner): mobile density — имя/теги сверху, actions снизу
|
`SpecialistsPanel` (owner): во вкладке **Команда** rail (desktop) / segmented
|
||||||
|
(mobile) — **не** внутри About; **owner всегда первым**, бейдж «Владелец»,
|
||||||
|
галочка «Я специалист» ↔ `PUT status` active|inactive; edit name/specialization;
|
||||||
|
Delete для owner скрыт. Mobile density — имя/теги сверху, actions снизу
|
||||||
full-width (invite/deactivate не ломать); desktop — вертикальный rail + scroll.
|
full-width (invite/deactivate не ломать); desktop — вертикальный rail + scroll.
|
||||||
|
Режимы сетки **Студия** (агрегат `start_time`+`duration`, заливка booked/total,
|
||||||
|
label `HH:mm · K/N свободно`, popover мастеров) и **Мастер** (фильтр
|
||||||
|
`specialist_id` + sticky roster как D9 B) — см. §3.1. Месяц в Студии —
|
||||||
|
density markers. Occupancy ячеек/popover — из API `booking_occupancy`
|
||||||
|
(`"free"` | `"pending"` | `"confirmed"`, BackSpec events); ★ `0.0` на free
|
||||||
|
не показывать. Personal — без агрегата / без вкладки Команда.
|
||||||
- Guest browse commercial: roster активных → фильтр расписания по `specialist_id`
|
- Guest browse commercial: roster активных → фильтр расписания по `specialist_id`
|
||||||
(D9 B; см. §3.1); owner panel не смешивать с guest roster.
|
(D9 B; см. §3.1); в «Все» — studio-агрегат как у owner; owner panel (invite/CRUD)
|
||||||
|
не смешивать с guest roster.
|
||||||
- Invitee: inbox входящих `GET /v1/user/specialist-invites` на `/invites` —
|
- Invitee: inbox входящих `GET /v1/user/specialist-invites` на `/invites` —
|
||||||
принять / отклонить; deep-link из email → accept по `token` после логина.
|
принять / отклонить; deep-link из email → accept по `token` после логина.
|
||||||
В More пункт «Приглашения» показывается **только** при `pending > 0` с badge
|
В More пункт «Приглашения» показывается **только** при `pending > 0` с badge
|
||||||
счётчика (не путать с booking-заявками на `/bookings`).
|
счётчика (не путать с booking-заявками на `/bookings`).
|
||||||
|
|
||||||
### 5.5. События
|
### 5.5. События
|
||||||
- Список: `GET /v1/calendars/:calendar_id/events`
|
- Список: `GET /v1/calendars/:calendar_id/events` — для commercial в JSON события
|
||||||
|
опционально `booking_occupancy` (`"free"` | `"pending"` | `"confirmed"`;
|
||||||
|
приоритет confirmed > pending > free; см. BackSpec §6). Studio-агрегат /
|
||||||
|
popover мастеров опираются на это поле (+ capacity/bookings при необходимости).
|
||||||
- CRUD владельца: `POST/PUT/DELETE` (в т.ч. опциональный `specialist_id`)
|
- CRUD владельца: `POST/PUT/DELETE` (в т.ч. опциональный `specialist_id`)
|
||||||
- Детали: `GET /v1/events/:id` (отображение специалиста, если задан)
|
- Детали: `GET /v1/events/:id` (отображение специалиста, если задан;
|
||||||
|
тот же `booking_occupancy`)
|
||||||
- Вхождения: `GET /v1/events/:id/occurrences`
|
- Вхождения: `GET /v1/events/:id/occurrences`
|
||||||
- Отмена вхождения: `DELETE /v1/events/:id/occurrences/:start_time`
|
- Отмена вхождения: `DELETE /v1/events/:id/occurrences/:start_time`
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user