Docs: sync Front#39 Mood chip + Lens strip. Fixes EventHub/EventHubSpec#16

Refs EventHub/EventHubFront#39 (c2cb754 / d38a927).
This commit is contained in:
2026-07-28 21:22:03 +03:00
parent a7132b9b64
commit 86e53b5f0b
4 changed files with 57 additions and 11 deletions
+26 -10
View File
@@ -38,7 +38,7 @@ Deep-link на календарь, владельцем которого явл
- Сборщик: Vite
- UI: React 19 + Tailwind CSS 4 + shadcn/Radix
- Типографика: Syne (display) + Manrope (UI)
- Состояние: Zustand (auth + mood + calendar context), TanStack React Query v5 (сервер)
- Состояние: Zustand (auth + mood + lens + calendar context), TanStack React Query v5 (сервер)
- HTTP: Axios с интерсепторами
- Формы: React Hook Form + Zod
- Дата/время: dayjs (ru locale)
@@ -55,8 +55,14 @@ Deep-link на календарь, владельцем которого явл
и «Мои записи» (participant); пустые группы скрывать. Confirm на карточке события остаётся.
Past/expired: бейдж `expired` («истекла»), Cancel скрыт; empty-state с CTA Discover / календари.
Время на list-карточках — вторичное к title (`.eh-book-list-time`).
- В **Ещё**: пункт входящих specialist invites скрыт при `pending=0`; при `pending > 0`
badge со счётчиком (deep-link `/invites` работает).
- В **Ещё**: язык (`LanguageSwitcher`); пункт входящих specialist invites скрыт при
`pending=0`; при `pending > 0` badge со счётчиком (deep-link `/invites` работает).
**Mood в Ещё нет** — только chip в chrome workspace календаря (Front#39).
- **Mood + Lens (workspace, Front#39):** в chrome календаря — mood chip (FLOW/MOMENTUM/
CONTROL → sheet picker); отдельно lens «Обзор / Сегодня / Записи» (desktop segment /
mobile sheet); над сеткой — collapsible lens strip (Front-only derive из уже
загруженных events/bookings). Без flip календаря; без 4-го CREATOR. Strip по
умолчанию раскрыт на desktop, свёрнут на mobile. Нет на HTML-архиве месяца.
- Контекст виджета: свой календарь (селектор) или чужой (browse после поиска). Чужой `personal`
только просмотр; чужой `commercial` с `booking_open=true` — запись; при `booking_open=false`
(restricted / нет подписки владельца) — просмотр + сообщение «Запись временно недоступна».
@@ -89,7 +95,7 @@ Deep-link на календарь, владельцем которого явл
## 3.2. Настроения (mood themes) / режимы CalenTIQ
Публичный бренд UI: **CalenTIQ** (см. [BRANDING.md](BRANDING.md)). Внутренние ключи API/preferences без изменений.
Публичный бренд UI: **CalenTIQ** (см. [BRANDING.md](BRANDING.md)). Внутренние ключи API/preferences без изменений. Три mood только — **без CREATOR**, без flip календаря.
Пользователь переключает визуальный режим; атрибут `html[data-mood]`. Для гостя и при отсутствии `preferences.mood` — дефолт `calm` (в профиль при логине не пишется). После входа смена mood сохраняется в `preferences.mood` через `PATCH /v1/user/me`.
@@ -99,9 +105,19 @@ Deep-link на календарь, владельцем которого явл
| MOMENTUM | `energetic` | тёмный ink + coral `#FF6A3D` |
| CONTROL | `business` | charcoal + steel `#324A5F`, более жёсткие радиусы |
Переключатели mood и языка: на экранах auth (`/login`, `/register`, `/verify`) и в `/more`. В шапке workspace — wordmark CalenTIQ (Time Arc), **nickname** (не raw email) и выход (без mood/lang). Desktop chrome — одна строка: logo | nav | logout.
**Где mood:**
- Auth (`/login`, `/register`, `/verify`, forgot/reset) — compact `MoodSwitcher` (+ language).
- Calendar workspace — primary control: **mood chip** → dialog/sheet (не в `/more`).
- App shell header — wordmark + nickname + logout (**без** mood/lang). Desktop: logo \| nav \| logout.
Язык UI (`ru`/`en`): до логина — `navigator.language`; после входа — поле `language` профиля (`PATCH /v1/user/me`). Если в профиле язык пуст — при логине записывается текущий (браузерный/выбранный на экране входа). Язык также можно сменить в форме `/profile`.
**Lens** (отдельно от mood и от view month/week/day): `overview` / `today` / `bookings`
(UI: Обзор / Сегодня / Записи). 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 (`ru`/`en`): до логина — `navigator.language`; после входа — поле `language` профиля (`PATCH /v1/user/me`). Если в профиле язык пуст — при логине записывается текущий (браузерный/выбранный на экране входа). Язык также в `/more` и в форме `/profile`.
## 4. Маршруты
@@ -116,8 +132,8 @@ Deep-link на календарь, владельцем которого явл
- `/bookings` — grouped inbox: (A) к подтверждению (owner/specialist pending) +
(B) мои записи участника; deep-link в `/c/.../e/...`
- `/calendars` — управление своими календарями (CRUD)
- `/more` — вторичное меню + mood switcher; пункт specialist invites **скрыт**,
если входящих pending = 0; при pending > 0 — badge со счётчиком
- `/more` — вторичное меню + language switcher (**без** mood); пункт specialist
invites **скрыт**, если входящих pending = 0; при pending > 0 — badge со счётчиком
(deep-link `/invites?token=` работает)
- `/invites` — inbox входящих specialist invites (не смешивать с `/bookings`)
- `/following` — отслеживаемые чужие календари (follow; не платная Подписка)
@@ -125,7 +141,7 @@ Deep-link на календарь, владельцем которого явл
Redirects: `/discover``/search`; `/calendars/:id``/c/:id`; `/calendars/:id/events/:eventId``/c/:id/e/:eventId`.
Состояние вида: Zustand `calendarContextStore` (`view`, `cursorDate`, last own `calendarId` в `localStorage`).
Состояние вида: Zustand `calendarContextStore` (`view`, `cursorDate`, last own `calendarId` в `localStorage`); lens — `lensStore` + `localStorage` (см. §3.2).
## 5. Модули
@@ -133,7 +149,7 @@ Redirects: `/discover` → `/search`; `/calendars/:id` → `/c/:id`; `/calendars
Регистрация, верификация, вход, восстановление сессии.
### 5.2. Профиль
`GET /v1/user/me` + форма на `/profile`. Редактируемые через `PATCH /v1/user/me`: `nickname`, `phone`, `timezone`, `avatar_url`, `language`; смена пароля — `current_password` + `password`. Read-only в UI: `id`, `email`, `role`, `status`. Mood — отдельно в `/more` (`preferences.mood`). Язык при первом визите — из браузера; при первом логине без `language` — пишется в профиль. Mood по умолчанию `calm`, при логине не сидится.
`GET /v1/user/me` + форма на `/profile`. Редактируемые через `PATCH /v1/user/me`: `nickname`, `phone`, `timezone`, `avatar_url`, `language`; смена пароля — `current_password` + `password`. Read-only в UI: `id`, `email`, `role`, `status`. Mood — chip в calendar workspace (+ compact на auth); persist `preferences.mood` через `PATCH /v1/user/me` (**не** в `/more`). Язык при первом визите — из браузера; при первом логине без `language` — пишется в профиль. Mood по умолчанию `calm`, при логине не сидится. Lens — только localStorage (не профиль).
### 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`.
+9
View File
@@ -81,6 +81,15 @@ Canvas: `eventhub-front-ux-review` → `eventhub-front-ux-rewalk` → **`eventhu
Трекер: [EventHubFront#35](https://git.sabilin.com/EventHub/EventHubFront/issues/35).
## Mood + Lens workspace (Front#39, Jul 2026) — shipped
- [x] Mood chip в chrome календаря (sheet); убран из Ещё; 3 moods, без flip/CREATOR
- [x] Lens Обзор / Сегодня / Записи отдельно от mood и view modes
- [x] Strip над сеткой (Front-derive); mobile collapsed; desktop expanded
- [x] Persist: mood → `preferences.mood`; language → `user.language`; lens → localStorage
Трекер: [EventHubFront#39](https://git.sabilin.com/EventHub/EventHubFront/issues/39) (`c2cb754` / `d38a927`).
## Future (не блокер 10)
- [x] Swipe month/week + long-press create в календаре ([EventHubFront#24](https://git.sabilin.com/EventHub/EventHubFront/issues/24))
+1
View File
@@ -393,6 +393,7 @@ error rate > 1% **или** mean request > 500 ms (hold ≥2 мин в фазе)
| Дата | Изменение |
|------|-----------|
| 2026-07-28 | Spec sync Front#39: Mood chip в workspace (не Ещё); Lens Обзор/Сегодня/Записи + strip; mood→DB, lens→localStorage |
| 2026-07-28 | Spec sync Front#35: D9 B guest specialist schedule; confirmed overlay на personal; SpecialistsPanel density; F6 EventActionCard |
| 2026-07-27 | Spec sync Back#61: `specialist_id` на POST event; commercial без active sub → 402; trial только `start_trial` |
| 2026-07-27 | `agent-pitfalls.mdc` §6: экономия токенов (чат, scope, subagents, режимы) |
+21 -1
View File
@@ -18,7 +18,9 @@ SoT: [`boards/calentiq-ui-board-final.png`](boards/calentiq-ui-board-final.png).
| 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` = **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 |
| Login §D phone chrome | три phone frames + (на борде) Google CTA | brand-first hero + mood `--auth-*`; email/password only; compact mood на auth; taglines — в mood sheet workspace (не в `/more`); **без** Google OAuth |
| Mood placement | board §C switcher cards | **Front#39:** mood chip в chrome calendar workspace (+ compact auth); **не** в `/more`; 3 moods, без flip / CREATOR |
| Lens + strip | board Today/Upcoming rail only | **Front#39:** отдельный lens Обзор/Сегодня/Записи + strip над сеткой (Front-derive); localStorage only; soft-default по mood |
| Favicon set | 3 отдельных favicon файла на борде | mood appicon через `applyMood` |
| Desktop nav | phone bottom tabs на борде | desktop: **одна строка** chrome — logo \| nav (Календарь / Найти / Записи / Ещё) \| nickname + logout; bottom nav — mobile |
| Header identity | — | nickname primary (не raw email); email только в `title` tooltip |
@@ -43,6 +45,8 @@ IA без sidebar / без Creator mood — совпадает с non-goals (п
**Front#35 (2026-07-28, SHA `57cfcf3`):** D9 модель B (guest roster → schedule filter); confirmed foreign overlay на personal; SpecialistsPanel mobile density; F6 EventActionCard past+pending → expired / no Cancel, Book скрыт на past.
**Front#39 (2026-07-28, SHA `c2cb754` / `d38a927`):** Mood chip только в calendar workspace (убран из Ещё); Lens Обзор/Сегодня/Записи + strip над сеткой во всех 3 moods; mobile sheets + strip collapsed; mood→DB, lens→localStorage; без flip / CREATOR.
**Remaining intentional:** no pixel-perfect phone chrome; Discover без photo-hero grid (cover via `image_url` when API returns it; broken URL → wash+initials); §E specialist schedule card vs panel — не pixel-perfect board mocks (follow-up). Product flow D9 B — documented; board pixel mock — Future.
### Bookings inbox + More invites (locked 2026-07-27)
@@ -79,6 +83,20 @@ API: `GET /v1/user/booking-requests` (Back) + существующий `PUT /v1/
изменений.
3. **SpecialistsPanel density (mobile):** имя/теги сверху, actions снизу full-width.
### Mood + Lens (locked Front#39)
1. **Mood** = визуальная атмосфера (`html[data-mood]`, FLOW/MOMENTUM/CONTROL). Primary UX —
chip → sheet в chrome **calendar workspace**; compact на auth. **Нет** в `/more`.
Persist: `preferences.mood` через `PATCH /v1/user/me`. Без flip; без CREATOR.
2. **Lens** = отдельный контентный режим (`overview` / `today` / `bookings`), не mood и не
view month/week/day. Desktop — segment; mobile — chip → sheet. Soft-default при смене
mood, если lens не pinned: FLOW→overview, MOMENTUM→today, CONTROL→bookings.
3. **Strip** над сеткой (не на HTML-архиве месяца): Front-only derive — free gaps /
события сегодня / upcoming bookings. Desktop раскрыт по умолчанию; mobile свёрнут
(tap раскрывает). Сетка всегда видна.
4. **Persistence:** mood → DB; language → `user.language`; lens → `localStorage`
(`eh.calendar.lens` + pinned). Без нового Back API.
### Follow-ups (вне этого fix-pass или частично)
| Тема | Статус |
@@ -88,6 +106,7 @@ API: `GET /v1/user/booking-requests` (Back) + существующий `PUT /v1/
| §E specialist schedule card vs panel | deviation: EventActionCard + SpecialistsPanel; pixel mock — Future; **product D9 B** — done Front#35 |
| Desktop one-row chrome | **done** Front#31 — logo \| nav \| nickname+logout |
| D9 B + confirmed personal overlay | **done** Front#35 (`57cfcf3`) |
| Mood chip + Lens strip | **done** Front#39 (`c2cb754` / `d38a927`) |
## Mood concept = product surface
@@ -215,6 +234,7 @@ CSS-токены и chrome на mood beyond primary hex: surfaces, gradients, ev
| — | P1/P2 UX polish + expired + desktop chrome | [Front#31](https://git.sabilin.com/EventHub/EventHubFront/issues/31) |
| — | Past-pending → `expired` | [Back#60](https://git.sabilin.com/EventHub/EventHubBack/issues/60) |
| — | P2: D9 B + confirmed personal + density/F6 | [Front#35](https://git.sabilin.com/EventHub/EventHubFront/issues/35) |
| — | Mood chip + Lens strip (workspace) | [Front#39](https://git.sabilin.com/EventHub/EventHubFront/issues/39) |
Эпик: [Spec#10](https://git.sabilin.com/EventHub/EventHubSpec/issues/10).