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