# UI parity: EventHubFront ↔ CalenTIQ board План визуального выравнивания клиентского фронта с утверждённым бордом. SoT: [`boards/calentiq-ui-board-final.png`](boards/calentiq-ui-board-final.png). Эпик: [EventHubSpec#10](https://git.sabilin.com/EventHub/EventHubSpec/issues/10). **Статус:** workstreams 1–5 закрыты в коде Front (Refs Front#25–#29). Stage smoke на `https://stage.calentiq.com` — **done** (UI `0.2.29` / `79dadfb21f98`, API `0.2.158`). --- ## Known deviations from board (Front#29) Зафиксировано осознанно / пока не делаем (после visual pass ×3 moods + stage): | Зона | Борд | Сейчас | |------|------|--------| | 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` = **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; compact mood на auth; taglines — в mood sheet AppShell (не в `/more`); **без** Google OAuth | | Mood placement | board §C switcher cards | **Front#40:** mood chip в AppShell рядом с BrandWordmark (+ compact auth); **не** в `/more`, **не** в toolbar Month/Week/Day; 3 moods, без flip / CREATOR | | Lens + strip | board Today/Upcoming rail only | **Front#39 + controls IA:** lens Обзор/На сегодня/Заявки в **шапке strip** (underline tabs / mobile sheet); view Месяц/Неделя/День рядом с периодом; owner schedule actions отдельным рядом; localStorage only; soft-default по mood | | Week view headers | — | **Front#40:** одна строка day-headers (без дубля `WeekDayColumn`); scroll `.eh-cal-week-scroll` (mobile ~390) | | Favicon set | 3 отдельных favicon файла на борде | mood appicon через `applyMood` | | Desktop nav | phone bottom tabs на борде | desktop: **одна строка** chrome — logo + mood \| nav (Календарь / Найти / Записи / Ещё) \| nickname + logout; bottom nav — mobile | | Header identity | — | nickname primary (не raw email); email только в `title` tooltip | | Bookings past/expired | — | `expired` бейдж + Cancel скрыт; empty-state с CTA Discover / календари; soft list time | | Agenda empty copy | — | personal «Нет событий»; commercial «Нет слотов» | | More invites | пункт меню | скрыт при pending=0; badge счётчика при pending > 0 | | D9 guest specialist schedule | — | **модель B (Front#35):** roster карточек → тап → фильтр слотов по `event.specialist_id`; «Все слоты студии»; book без specialist в body; **не** chip-filter | | Confirmed на personal | — | Front-only overlay confirmed чужих commercial на default personal; title `событие · студия · спец`; клик → `/c/:foreignCalId/e/:eventId` | | SpecialistsPanel density | — | mobile: имя/теги сверху, actions снизу full-width (Front#33/#35) | | Stage | полный smoke на stage | **done** 2026-07-27: Login×3 / Calendar×3 + agenda / Discover / CONTROL commercial + Confirm·Decline / More moods | | Домены calentiq.* | — | публичный stage: `stage.calentiq.com` / `admin.stage.calentiq.com` (канон STAGE-BROWSER.md) | IA без sidebar / без Creator mood — совпадает с non-goals (проверено на stage). ### Visual pass ×3 moods (local + stage) **Checked (vite preview + Playwright shots + stage HTTPS):** Login §D, Calendar grid+agenda, Discover media, CONTROL slot/bookings (Confirm/Decline на event card), More mood cards — FLOW / MOMENTUM / CONTROL. **Polish in this pass:** agenda copy/casing/counts + time-first rows; CONTROL steel rail emphasis; neutral Discover cover in mock (не flow-appicon во всех moods). **Front#31 (2026-07-27):** desktop one-row chrome (logo|nav|logout); header nickname; bookings `expired` + soft list time + empty CTA; agenda personal vs commercial empty copy; Discover cover photo/fallback; More invites badge. **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 убран из Ещё; Lens Обзор/Сегодня/Записи + strip над сеткой во всех 3 moods; mobile sheets + strip collapsed; mood→DB, lens→localStorage; без flip / CREATOR. (Placement chip уточнён в Front#40.) **Controls IA (2026-07-29):** lens перенесён в шапку strip (labels: Обзор / На сегодня / Заявки); view рядом с периодом; owner schedule actions отдельным рядом. **Front#40 (2026-07-28, SHA mood `7c092d1` / week `db11c41`):** Mood chip в AppShell рядом с BrandWordmark (**не** в calendar toolbar Month/Week/Day); 3 moods; `PATCH preferences.mood`; без дубля в Ещё. Week view: одна строка day-headers + `.eh-cal-week-scroll` для mobile ~390. **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) Продуктовые решения (не 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** входящих; при pending > 0 — **badge** со счётчиком. Deep-link `/invites?token=` работает. Исходящие invite остаются в `SpecialistsPanel`. 4. **Не** класть строки `specialist_invite` в `/bookings`. 5. **Past-pending / `expired`:** Back#60 помечает past-pending → `expired` и исключает из `booking-requests`; Confirm/Decline → `409`. UI: бейдж «истекла», Cancel скрыт (Bookings + EventActionCard); defensive map past+pending → expired badge. Front#35 F6: на past Book скрыт; past+pending на карточке → expired label. API: `GET /v1/user/booking-requests` (Back) + существующий `PUT /v1/bookings/:id` (`409` на expired / full). ### Guest specialist schedule + confirmed overlay (locked Front#35) 1. **D9 модель B (guest на commercial):** roster активных специалистов (карточки) → тап → сетка только со слотами выбранного `event.specialist_id`; CTA «Все слоты студии» сбрасывает фильтр. **Не** chip-filter на shared grid. Book body без specialist. Owner `SpecialistsPanel` (invite/CRUD) не менять этим флоу. 2. **Confirmed на personal (Front-only):** `GET /v1/user/bookings` (confirmed) + догрузка events/calendars → overlay на **default personal** grid; title `event · studio · specialist`; клик → `/c/:foreignCalId/e/:eventId`. Back без изменений. 3. **SpecialistsPanel density (mobile):** имя/теги сверху, actions снизу full-width. ### Mood + Lens (locked Front#39 / placement Front#40) 1. **Mood** = визуальная атмосфера (`html[data-mood]`, FLOW/MOMENTUM/CONTROL). Primary UX — chip → sheet в **AppShell** рядом с BrandWordmark (Front#40); compact на auth. **Нет** в `/more`; **не** в toolbar Month/Week/Day. Persist: `preferences.mood` через `PATCH /v1/user/me`. Без flip; без CREATOR. 2. **Lens** = отдельный контентный режим (`overview` / `today` / `bookings`), не mood и не view month/week/day. UI: Обзор / На сегодня / Заявки. Переключатель — в шапке strip (underline/text tabs на desktop; chip → sheet на mobile), **не** рядом с ViewMode. Soft-default при смене mood, если lens не pinned: FLOW→overview, MOMENTUM→today, CONTROL→bookings. 3. **Strip** над сеткой (не на HTML-архиве месяца): Front-only derive — free gaps / события сегодня / upcoming bookings. Desktop раскрыт по умолчанию; mobile свёрнут (tap раскрывает). Сетка всегда видна. Workspace toolbar: контекст (селектор) / виджет (период + view) / owner schedule actions (отдельный ряд). 4. **Persistence:** mood → DB; language → `user.language`; lens → `localStorage` (`eh.calendar.lens` + pinned). Без нового Back API. 5. **Week view (Front#40):** одна строка day-headers (без дубля `WeekDayColumn`); горизонтальный скролл `.eh-cal-week-scroll` (mobile ~390). ### Follow-ups (вне этого fix-pass или частично) | Тема | Статус | |------|--------| | Discover `image_url` в search serialization | Back отдаёт поле; upload API **нет** — только URL field / seed; Front#31: photo vs fallback media classes + onError fallback | | 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; **product D9 B** — done Front#35 | | Desktop one-row chrome | **done** Front#31; mood chip у logo — Front#40 (`7c092d1`) | | D9 B + confirmed personal overlay | **done** Front#35 (`57cfcf3`) | | Mood chip + Lens strip | **done** Front#39 (`c2cb754` / `d38a927`); chip → AppShell Front#40 | | Week view single header + scroll | **done** Front#40 (`db11c41`) | ## Mood concept = product surface Mood — не переименование `calm|energetic|business` и не смена primary hex. Каждый mood — **отдельная атмосфера продукта** при той же IA: | Mood | Ощущение | Chrome / surfaces | Discover / booking feel | |------|----------|-------------------|-------------------------| | **FLOW** (`calm`) | спокойствие, баланс | soft sage panels, light wash, soft chips | мягкие карточки, спокойный CTA | | **MOMENTUM** (`energetic`) | энергия, скорость | ink `#111820`, coral `#FF6A3D`, denser chips | тёмный discover, яркий CTA | | **CONTROL** (`business`) | бизнес, доступность | steel `#324A5F`, cool neutrals | коммерческие карточки слот/специалист/CTA | Референс на борде: секции **B Mood Overview**, **C Mood Switcher**, **D Core Screens**, **NEW Discover**, **E Booking / Commercial**. Switcher — **branded cards** (иконка + короткий tagline), не plain list/tabs: - FLOW — *Find balance* - MOMENTUM — *Get more done* - CONTROL — *Grow the business* Опционально: mood-aware favicon из [`boards/calentiq-icon-system-final.png`](boards/calentiq-icon-system-final.png) / `mood-icons/`. Токены сейчас узкие ([`tokens/colors.json`](tokens/colors.json)); для parity нужны surfaces, gradients, chip/event styles, chrome — не только primary. --- ## Workstreams (mood-first) Порядок важен: сначала атмосфера и токены, затем экраны поверх них. ### 1. Mood system / atmosphere — **P0 / first** ✅ CSS-токены и chrome на mood beyond primary hex: surfaces, gradients, event chip styles; switcher как branded cards (icon + tagline); опционально mood favicon. **Acceptance** - [x] Per-mood token set: surface / wash / chip / border / CTA (не только primary) - [x] Mood switcher = 3 branded cards с иконкой и tagline с борда - [x] Смена mood меняет атмосферу chrome (не только accent button) - [x] API keys остаются `calm|energetic|business` - [x] Опционально: favicon следует активному mood **Design:** board §B, §C, §F; `mood-icons/`; `tokens/` **Done:** EventHubFront#25 — `index.css` mood tokens + `MoodSwitcher` cards + favicon via `applyMood`. ### 2. Calendar mood-chrome — **P0** ✅ Та же сетка/IA; soft panels (FLOW), denser chips (MOMENTUM), mood-tinted cells (CONTROL) — как на борде §B / §D Calendar. **Acceptance** - [x] Month/week выглядят различно по mood при той же навигации - [x] Event chips / cells тонированы mood-токенами - [x] Нет sidebar / смены layout ради «красоты» **Design:** board §B Mood Overview, §D Calendar **Done:** EventHubFront#26 — `--cal-*` tokens + `.eh-cal` / `.eh-chip` в `CalendarWidget`; IA без изменений. **Follow-up:** month grid §B/D — FLOW sage cell wash + chip limit 2; MOMENTUM denser/hotter chips limit 5; CONTROL steel cells + sharper chips limit 3; split time/title chips. ### 3. Discover mood variants — **P1** ✅ Визуальная дифференциация FLOW vs MOMENTUM vs CONTROL: soft cards + CTA; соответствие борду (NEW Discover / §D Discover), не as-is скринам. **Acceptance** - [x] Discover в трёх moods визуально различим (фон, карточки, CTA) - [x] Soft card + CTA pattern с борда (не плоский as-is list-only) - [x] IA и фильтры без смены модели; только presentation **Design:** board NEW Discover, §D Discover; as-is refs: `eventhub-discover-{flow,momentum,control}.png` **Done:** EventHubFront#27 — `--disc-*` + `.eh-discover-card` / `.eh-discover-cta`; list → card grid. **Follow-up:** media hierarchy — `.eh-discover-media` with per-mood size/radius/washes (FLOW air / MOMENTUM energy / CONTROL steel), optional cover via `image_url`, display title + soft-card rhythm. ### 4. CONTROL booking surfaces — **P1** ✅ Коммерческие карточки: слот, специалист, CTA — только Control-specific (§E). **Acceptance** - [x] Slot card + specialist card + primary CTA в CONTROL - [x] Specialists остаются внутри Control (не 4-й mood) - [x] FLOW/MOMENTUM не получают Control commercial chrome **Design:** board §E Booking / Commercial **Done:** EventHubFront#28 — `.eh-book-slot` / `.eh-book-specialist` / `.eh-book-request`; steel chrome только под `data-mood=business`. **Follow-up:** §E polish — `--book-*` tokens; CONTROL steel left-rail + sharper radii; slot duration/available label; specialist/request avatars; mood-token CTA/Decline. ### 5. Polish + stage smoke — **P2** ✅ Мелкие расхождения с бордом, wordmark/lockup consistency, stage smoke трёх moods. **Acceptance** - [x] Mock smoke: calendar → discover → bookings в 3 moods (+ IA без sidebar) - [x] Нет регрессий IA (top + bottom nav) — покрыто mock e2e - [x] Известные отклонения от борда зафиксированы в UI-PARITY (Known deviations) - [x] Visual pass ×3 moods (local preview / Playwright shots) — Login / Calendar / Discover / CONTROL booking - [x] Stage smoke на живом стенде — UI `0.2.29` (`79dadfb21f98`) @ `https://stage.calentiq.com` (2026-07-27) **Done:** EventHubFront#29 — `e2e/tests/mood-parity-smoke.spec.ts`, brand meta, CONTROL tagline polish. **Follow-up:** visual pass polish — agenda Предстоящие + counts/time-first; CONTROL steel rail; mock Discover cover neutral. Stage: seed commercial + slots + pending booking для §E Confirm/Decline. ## Non-goals - Left sidebar / смена IA - 4-й Creator mood / AI-assistant home - Rename API keys (`calm|energetic|business` остаются) - Покупка доменов / DNS cutover - Админский UI (`design/admin/`) — не трогать Front-бордами - Полный pixel-perfect каждого пикселя борда (направление + mood atmosphere) --- ## Mapping → issues | # | Workstream | Issue | |---|------------|-------| | — | Story (родитель) | [Spec#11](https://git.sabilin.com/EventHub/EventHubSpec/issues/11) | | 1 | Mood system / atmosphere | [Front#25](https://git.sabilin.com/EventHub/EventHubFront/issues/25) | | 2 | Calendar mood-chrome | [Front#26](https://git.sabilin.com/EventHub/EventHubFront/issues/26) | | 3 | Discover mood variants | [Front#27](https://git.sabilin.com/EventHub/EventHubFront/issues/27) | | 4 | CONTROL booking surfaces | [Front#28](https://git.sabilin.com/EventHub/EventHubFront/issues/28) | | 5 | Polish + stage smoke | [Front#29](https://git.sabilin.com/EventHub/EventHubFront/issues/29) | | — | 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) | | — | Mood chip → AppShell + week scroll | [Front#40](https://git.sabilin.com/EventHub/EventHubFront/issues/40) | Эпик: [Spec#10](https://git.sabilin.com/EventHub/EventHubSpec/issues/10). --- ## Suggested start Кодить с **Task 1 (Mood system / atmosphere)**: без токенов и switcher-as-cards остальные экраны будут «перекраской primary», а не mood product surface.