Files
EventHubSpec/EventHubFrontSpec.md
T
aleksey 0d74ad33e3 docs: sync FrontSpec with editable profile and UX shell.
Mood/lang only on auth and More; profile editable fields; search badge and collapsed filters. Refs EventHub/EventHubBack#48
2026-07-20 16:08:50 +03:00

198 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# EventHub Client UI — Техническое задание
Клиентское Web SPA для end-user: владельцы календарей и участники. Администраторы работают в [EventHubFrontAdmin](EventHubFrontAdminSpec.md). Контракт API — [EventHubBackSpec.md](EventHubBackSpec.md) и маршруты `EventHubBack` (порт HTTP **8080**, WS **8081**).
## 1. Роли и режимы
Один пользовательский аккаунт; UI разделяется по контексту (не по JWT-роли админки):
| Режим | Описание | Ключевые возможности |
|-------|----------|----------------------|
| Участник | Поиск и запись на события коммерческих календарей | Поиск, просмотр календаря/события, запись, отмена своей записи, отзывы, жалобы, тикеты |
| Владелец | Управление своими календарями | CRUD календарей и событий, список заявок, confirm/decline, подписка |
Deep-link на календарь, владельцем которого является текущий пользователь, показывает owner-действия (редактирование, заявки).
Права на операции проверяет бэкенд; фронт скрывает недоступные действия по `owner_id` / ответу API.
## 2. Аутентификация и авторизация
Эндпоинты:
- `POST /v1/register` — регистрация (`email`, `password`); статус пользователя `pending` до верификации.
- `POST /v1/verify` — подтверждение email `{ token }`; после успеха у пользователя есть дефолтный personal-календарь.
- `POST /v1/login` — вход; ответ `{ token, refresh_token, user }`. Сессия `auth_session` с `client_type=web`.
- `POST /v1/refresh` — ротация пары токенов по `{ refresh_token }`.
- `GET /v1/user/me` — профиль текущего пользователя.
Реализация:
- JWT в `localStorage` под ключами, отличными от Admin UI (избежать коллизии при общем origin).
- Axios: `Authorization: Bearer <access>`; при 401 — single-flight refresh; при неудаче — редирект на `/login`.
- При старте приложения — `GET /v1/user/me` при наличии токена.
- Logout — только клиентский (очистка storage); серверного revoke нет.
- WebSocket: `ws://…:8081/ws?token=<access_jwt>`; subscribe/unsubscribe по `calendar_id`.
## 3. Технологический стек
- Язык: TypeScript
- Сборщик: Vite
- UI: React 19 + Tailwind CSS 4 + shadcn/Radix
- Типографика: Syne (display) + Manrope (UI)
- Состояние: Zustand (auth + mood + calendar context), TanStack React Query v5 (сервер)
- HTTP: Axios с интерсепторами
- Формы: React Hook Form + Zod
- Дата/время: dayjs (ru locale)
- i18n: react-i18next (ru, en)
- Роутинг: React Router 7
- WebSocket: нативный API с переподключением
- Контейнеризация: Docker, Nginx (прокси `/v1` → user API `:8080`, `/ws``:8081`)
## 3.1. UX / IA (calendar workspace, mobile-first)
- Главная сущность — **календарь**. Центр UI — виджет с видами **месяц** (default) / **неделя** / **день**.
- Главные вкладки: **Календарь** (`/`, `/c/:id`), **Найти** (`/search`), **Записи** (`/bookings`), **Ещё** (`/more`).
- Контекст виджета: свой календарь (селектор) или чужой (browse после поиска). `personal` чужой — только просмотр; `commercial` — запись на событие.
- Выбор события открывает карточку действий: mobile — bottom sheet; desktop — боковая панель. Действия зависят от роли (owner / participant).
- Без выбранного события — панель «О календаре» (описание, рейтинг, отзывы; у browse — отзыв/жалоба на календарь).
- Owner: WS subscribe на активный календарь; `PUT` события из карточки; `PUT` календаря на `/calendars`.
- Поиск — список календарей/событий; выбор подставляет календарь в виджет (`/c/:id` или `/c/:id/e/:eventId`).
- CRUD своих календарей — `/calendars` (из «Ещё»). Legacy `/discover`, `/calendars/:id` → redirects.
- На мобиле — bottom tab bar (+ safe-area); на desktop — sticky header + табы.
- Mood themes — см. §3.2.
## 3.2. Настроения (mood themes)
Пользователь переключает визуальное настроение; атрибут `html[data-mood]`. Для гостя и при отсутствии `preferences.mood` — дефолт `calm` (в профиль при логине не пишется). После входа смена mood сохраняется в `preferences.mood` через `PATCH /v1/user/me`.
| Mood | Идентификатор | Характер |
|------|---------------|----------|
| Спокойный | `calm` | sage/mist, мягкий градиент (default) |
| Энергичный | `energetic` | тёмный ink + coral/amber |
| Деловой | `business` | charcoal + steel, более жёсткие радиусы |
Переключатели mood и языка: на экранах auth (`/login`, `/register`, `/verify`) и в `/more`. В шапке workspace — только бренд, email и выход (без mood/lang).
Язык UI (`ru`/`en`): до логина — `navigator.language`; после входа — поле `language` профиля (`PATCH /v1/user/me`). Если в профиле язык пуст — при логине записывается текущий (браузерный/выбранный на экране входа). Язык также можно сменить в форме `/profile`.
## 4. Маршруты
Публичные:
- `/login`, `/register`, `/verify`
Защищённые (`ProtectedRoute`):
- `/` — Calendar Workspace (автовыбор своего календаря или empty-state)
- `/c/:calendarId` — workspace с календарём
- `/c/:calendarId/e/:eventId` — workspace + карточка события
- `/search` — поиск календарей/событий
- `/bookings` — мои бронирования (deep-link в `/c/.../e/...`)
- `/calendars` — управление своими календарями (CRUD)
- `/more` — вторичное меню + mood switcher
- `/reviews`, `/subscription`, `/tickets`, `/profile`
Redirects: `/discover``/search`; `/calendars/:id``/c/:id`; `/calendars/:id/events/:eventId``/c/:id/e/:eventId`.
Состояние вида: Zustand `calendarContextStore` (`view`, `cursorDate`, last own `calendarId` в `localStorage`).
## 5. Модули
### 5.1. Auth
Регистрация, верификация, вход, восстановление сессии.
### 5.2. Профиль
`GET /v1/user/me` + форма на `/profile`. Редактируемые через `PATCH /v1/user/me`: `nickname`, `phone`, `timezone`, `language`. Read-only в UI: `id`, `email`, `role`, `status`. Mood — отдельно в `/more` (`preferences.mood`). Смена пароля и `avatar_url` на бэке есть, в клиентском UI пока нет. Язык при первом визите — из браузера; при первом логине без `language` — пишется в профиль. Mood по умолчанию `calm`, при логине не сидится.
### 5.3. Поиск (участник)
`GET /v1/search` — фильтры (`type`, `q`, даты, sort, order, пагинация; теги/geo в API есть, в UI пока нет). Без `q`/дат/sort — топы календарей и событий; иначе — поиск. В списке бейдж типа — `calendar`|`event` (не `personal`/`commercial`). Даты и сортировка свёрнуты по умолчанию. Переход к `/c/:id` или `/c/:id/e/:eventId`.
### 5.4. Календари
- Список своих: `GET /v1/calendars`
- CRUD: `POST/PUT/DELETE /v1/calendars`, `GET /v1/calendars/:id`
- Просмотр коммерческого чужого календаря по id (доступ по правилам бэка)
- HTML month view владельца: `GET /v1/calendars/:calendar_id/view?month=YYYY-MM` — при переключении виджета на **прошедший месяц** (owner + вид «Месяц») автоматически подменяет клиентский grid серверным HTML (live/архив, задача #15). Текущий и будущие месяцы — React-виджет по `GET …/events`.
- Создание `commercial` требует активной подписки (ответ 402 → экран подписки)
### 5.5. События
- Список: `GET /v1/calendars/:calendar_id/events`
- CRUD владельца: `POST/PUT/DELETE`
- Детали: `GET /v1/events/:id`
- Вхождения: `GET /v1/events/:id/occurrences`
- Отмена вхождения: `DELETE /v1/events/:id/occurrences/:start_time`
### 5.6. Запись (bookings)
- Участник: `POST /v1/events/:id/bookings`, `GET /v1/user/bookings`, `DELETE /v1/bookings/:id`, `GET /v1/bookings/:id`
- Владелец: `GET /v1/events/:id/bookings`, `PUT /v1/bookings/:id` с `{ action: confirm | decline }`
- Статусы: `pending` | `confirmed` | `cancelled` (режим confirmation календаря: `auto` | `manual` | timeout)
### 5.7. Отзывы
`GET/POST /v1/reviews`, `GET/PUT/DELETE /v1/reviews/:id`, `GET /v1/user/reviews`. Цели: `event` | `calendar`.
Голос: `PUT /v1/reviews/:id/vote` `{ "value": "like"|"dislike" }`, `DELETE /v1/reviews/:id/vote`; в ответах — `my_vote`.
### 5.8. Жалобы
`POST /v1/reports``target_type`: `event` | `calendar` | `review`.
### 5.9. Подписка
`GET /v1/subscription`, `POST /v1/subscription` (`start_trial` | `activate`).
### 5.10. Тикеты
- ErrorBoundary / необработанные ошибки → `POST /v1/tickets` с `source=frontend`
- Форма «Сообщить о баге» → `source=manual`
- Список: `GET /v1/tickets`, детали `GET /v1/tickets/:id`
### 5.11. Realtime
Подписка WS на открытые календари; сообщения `calendar_update`, `event_update`, `booking_update` → инвалидация React Query.
### 5.12. Версии сборки
Как в Admin UI / Back (`EventHubSpec/WORKFLOW.md` §6):
- Product `MAJOR.MINOR` из `EventHubSpec/VERSION` (CI: `scripts/resolve-product-version.sh`; локальный `VERSION` — fallback).
- Bake: `VITE_APP_VERSION`, `VITE_APP_BUILD`, `VITE_GIT_SHA`, `VITE_BUILT_AT`.
- UI: экран «Ещё» — `UI {version}.{build} (sha)`; API — `GET /health``API …`.
## 6. Структура проекта
```
src/
├── api/ — Axios client + *Api.ts
├── hooks/ — React Query hooks
├── store/ — Zustand (auth)
├── pages/ — auth, discover, calendars, events, bookings, reviews, subscription, tickets, profile
├── layouts/ — consumer shell (не admin Control Center)
├── components/ui — shadcn primitives
├── i18n/ — locales ru/en
├── schemas/ — Zod
└── types/
```
Репозиторий: [EventHubFront](https://git.sabilin.com/EventHub/EventHubFront).
## 6.1. Стенды и балансировщик
| Stand | URL | Swarm |
|-------|-----|--------|
| IFT | `https://ui.ift.eventhub.local` | alias `client-ui` |
| stage | `https://ui.stage.eventhub.local` | alias `client-ui` |
| dev | `https://ui.dev.eventhub.local` | alias `client-ui` |
Образ registry: `git.sabilin.com/eventhub/eventhub-front`. Traefik host-based (без path prefix). Nginx в контейнере: `/` SPA, `/v1/` → eventhub:8080, `/ws` → eventhub:8081.
CI: lint → build → Playwright mock → push image → deploy IFT → e2e-ift → stage → e2e-stage (зеркало FrontAdmin). Деплой зависит от EventHubDevOps#7 (`deploy-service.sh … client`).
E2E: `e2e/TESTIDS.md`; mock `npm run test:e2e`; стенд `SMOKE_USER_*` + `npm run test:e2e:ift|stage`.
## 7. Ограничения MVP (Future)
Не реализуется в UI, пока нет user HTTP API:
- приглашения / шаринг календаря (`calendar_share`)
- CRUD специалистов (`calendar_specialist`)
- серверный logout / revoke session
- загрузка файлов (вложения)
- явный `client_type=mobile` (фаза 3 бэка)
- push-уведомления (таблица `notification` без полноценной доставки)
Лайки/дизлайки отзывов: `PUT/DELETE /v1/reviews/:id/vote`, поле `my_vote` в ответах отзывов (EventHubBack#47).
## 8. Источники истины
- Маршруты и поведение: `EventHubBack/src/eventhub_app.erl` + handlers
- Продукт и auth: `EventHubBackSpec.md` §12, §6.1
- Swagger `client-swagger.json` — вторичный (возможен drift, например `/v1/auth/refresh` vs `/v1/refresh`)