Files
EventHubSpec/EventHubFrontSpec.md
T

14 KiB
Raw Blame History

EventHub Client UI — Техническое задание

Клиентское Web SPA для end-user: владельцы календарей и участники. Администраторы работают в EventHubFrontAdmin. Контракт API — 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, avatar_url, language; смена пароля — current_password + password. Read-only в UI: id, email, role, status. Mood — отдельно в /more (preferences.mood). Язык при первом визите — из браузера; при первом логине без language — пишется в профиль. Mood по умолчанию calm, при логине не сидится.

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.

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/reportstarget_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 /healthAPI ….

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.

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) → push image → deploy IFT → e2e-ift (live API) → stage → e2e-stage (live API) (зеркало FrontAdmin). Деплой зависит от EventHubDevOps#7 (deploy-service.sh … client).

E2E: e2e/TESTIDS.md. Моки (npm run test:e2e, project=mock) — только локально / до деплоя. После деплоя на IFT/stage — SMOKE_USER_* + npm run test:e2e:ift|stage против реального API (без Playwright route-моков).

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)