# Frontend — React SPA React-приложение VidConf: аутентификация, лобби-хаб, календарь и «мои конференции», вход по номеру/ссылке (в т.ч. гостем), комната конференции на LiveKit с чатом в реальном времени, профиль пользователя, админ-панель. Поддерживает светлую и тёмную темы оформления. ## Стек технологий - **Фреймворк:** React 18 + TypeScript - **Сборка:** Vite (dev сервер с HMR) - **Стили:** Tailwind CSS 4 + @tailwindcss/vite - **Компоненты:** shadcn/ui, `@base-ui/react` - **Видео:** `@livekit/components-react` + `livekit-client` - **Календарь:** `@fullcalendar/react` (day-grid + interaction) - **HTTP:** fetch API (клиент в `src/api/client.ts`) ## Структура проекта ``` src/ ├── pages/ Компоненты страниц верхнего уровня │ ├── LoginPage.tsx Вход │ ├── RegisterPage.tsx Регистрация (выбор команды) │ ├── VerifyEmailPage.tsx Подтверждение email по токену из письма │ ├── LobbyPage.tsx Лобби-хаб (2×2 плитки: создать/календарь/подключиться/мои) │ ├── CalendarPage.tsx Календарь (FullCalendar), создание/редактирование/отмена конференций │ ├── MyConferencesPage.tsx "Мои конференции" с фильтрами (Все/Закреплённые/Предстоящие) │ ├── ProfilePage.tsx Профиль пользователя (ФИО, команда, аватар, смена пароля) │ ├── JoinPage.tsx Вход в конференцию по номеру/ссылке (/j/{slug}), гостевой экран │ ├── RoomPage.tsx Комната конференции (LiveKit): видео, чат, тулбар │ └── AdminPage.tsx Админ-панель (конференции, пользователи, команды, настройки) ├── components/ │ ├── ui/ shadcn/ui + расширения │ │ ├── button.tsx │ │ ├── Avatar.tsx Аватар с заглушкой (инициалы) │ │ ├── Select.tsx Кастомный Select по дизайн-системе │ │ ├── ConferenceHoverCard.tsx Ховер-карточка конференции │ │ ├── CopyPill.tsx Копирование номера/ссылки │ │ ├── LogoMark.tsx Логотип │ │ ├── ThemeToggle.tsx Переключатель светлая/тёмная │ │ └── ToastProvider.tsx Уведомления │ ├── calendar/ │ │ ├── ConferenceCalendar.tsx FullCalendar-компонент │ │ ├── ConferenceFormCard.tsx Форма создания/редактирования конференции │ │ ├── ConferenceOccurrenceDialog.tsx Диалог вхождения повторяющейся конференции │ │ └── ParticipantsPicker.tsx Кастомный Select для выбора участников с поиском │ ├── admin/ │ │ ├── AdminConferencesTab.tsx │ │ ├── AdminUsersTab.tsx / AdminUserCreateDialog.tsx / AdminUserProfileDialog.tsx │ │ ├── AdminTeamsTab.tsx │ │ └── AdminSettingsTab.tsx │ ├── auth/ │ │ └── AuthLayout.tsx Общий layout экранов входа/регистрации │ ├── layout/ │ │ └── ShellTopbar.tsx Верхняя панель оболочки (лобби/календарь/админка) │ └── room/ │ ├── RoomStage.tsx / RoomParticipantTile.tsx Видеосетка участников │ ├── RoomTopbar.tsx / RoomToolbar.tsx Панели управления комнатой │ ├── ChatPanel.tsx Панель текстового чата │ └── DeviceSettingsDialog.tsx Выбор камеры/микрофона ├── auth/ │ ├── AuthProvider.tsx / authContext.ts / authStore.ts Хранение сессии, refresh │ ├── useAuth.ts Hook доступа к текущему пользователю │ ├── RequireAuth.tsx Route-guard: требует вход │ └── RequireAdmin.tsx Route-guard: требует роль admin ├── api/ │ ├── client.ts Fetch-клиент (перехват токенов, обработка 401 → refresh) │ ├── health.ts getHealth() │ ├── auth.ts register(), login(), verifyEmail(), refreshToken(), logout() │ ├── conferences.ts createConference(), listMyConferences(), getCalendar(), │ │ resolveConference(), joinConference(), guestJoinConference(), │ │ updateConference(), deleteConference() │ ├── users.ts Профиль, аватары, смена пароля, поиск пользователей │ └── admin.ts Админ-функции (конференции, пользователи, команды, настройки) ├── hooks/ │ ├── useChat.ts WebSocket-чат конференции (подключение, история, отправка) │ ├── useTheme.ts Светлая/тёмная тема (localStorage + системный автодетект) │ ├── useFullscreen.ts Полноэкранный режим комнаты │ └── useRoomPiP.ts Document Picture-in-Picture для комнаты ├── lib/ │ ├── utils.ts │ ├── localTime.ts Форматирование времени в локальном часовом поясе │ ├── recurrenceFormat.ts Человекочитаемое описание правила повторения │ ├── parseJoinQuery.ts Разбор номера/ссылки конференции из строки │ └── pluralize.ts ├── styles/ Tailwind + токены дизайн-системы (per-page css + tokens.css) ├── App.tsx Корневой компонент с маршрутизацией (React Router) ├── main.tsx Точка входа └── index.css Глобальные стили Tailwind ``` ### Маршруты (App.tsx) | Путь | Страница | |---|---| | `/login`, `/register`, `/verify-email` | Аутентификация | | `/lobby` | Лобби-хаб (требует вход) | | `/join`, `/j/:slug` | Вход в конференцию по ссылке/номеру | | `/room/:slug` | Комната конференции | | `/calendar` | Календарь (требует вход) | | `/my-conferences` | Мои конференции (требует вход) | | `/profile` | Профиль (требует вход) | | `/admin` | Админ-панель (требует роль admin) | ## Быстрый старт ### Требования - Node 20+ - npm ### 1. Установка зависимостей ```bash cd frontend npm install ``` ### 2. Переменные окружения Отдельный `.env` для frontend не требуется: Vite proxy перенаправляет `/api/*` (включая WebSocket-чат) на backend по адресу `http://localhost:8000` (см. `vite.config.ts`). ### 3. Запуск dev сервера ```bash npm run dev ``` Запущен на `http://localhost:5173` с HMR. ### 4. Сборка для продакшена ```bash npm run build npm run preview ``` ## Скрипты ```bash npm run dev # Запустить Vite dev сервер npm run lint # ESLint npm run build # tsc -b (проверка типов) + сборка для продакшена npm run preview # Локальный просмотр production-сборки ``` Отдельного `type-check`-скрипта нет — проверка типов встроена в `npm run build` (`tsc -b`); для быстрой проверки без сборки: `npx tsc -b --noEmit`. ## Аутентификация `src/auth/` хранит сессию (access-токен в памяти, refresh — в httpOnly cookie), предоставляет `useAuth()` и route-guard'ы `RequireAuth`/`RequireAdmin`. `src/api/client.ts` автоматически подставляет access-токен и обновляет его при 401 через `refreshToken()`. ## Комната конференции `RoomPage.tsx` подключается к LiveKit через `@livekit/components-react` (`LiveKitRoom`), рендерит видеосетку (`RoomStage`/`RoomParticipantTile`), тулбар управления (`RoomToolbar`), диалог настройки устройств (`DeviceSettingsDialog`), полноэкранный режим (`useFullscreen`) и Picture-in-Picture (`useRoomPiP`). Чат конференции — `ChatPanel.tsx` поверх хука `useChat` (WebSocket, история последних сообщений, broadcast, отметка гостей, счётчик непрочитанных); показывается только если бэкенд вернул `chat_enabled: true` в `JoinOut`. ## Темы оболочки Оболочка (auth, лобби, календарь, join, «мои конференции», админка) поддерживает светлую и тёмную тему; комната конференции всегда тёмная, независимо от выбора. ### Механизм переключения - **Светлая тема** — дефолт, если пользователь не делал явного выбора и системная тема светлая. - **Тёмная тема** — см. `design/mockups/dark/README.md`. - **Системная тема** — автодетект `@media (prefers-color-scheme: dark)`, если явного выбора нет. - **Явный выбор** — переключатель (☀️/🌙) сохраняет выбор в `localStorage` с ключом `vidconf-theme` (`'light'` или `'dark'`). ### Реализация **Токены:** `design/tokens.css` (синхронная копия в `frontend/src/styles/tokens.css`): ```css /* Светлая — :root */ :root { --color-bg: #F1F1F1; /* … */ } /* Тёмная — явный выбор */ :root[data-theme="dark"] { --color-bg: #1E1E1E; /* … */ } /* Тёмная — автодетект ОС (если data-theme не проставлен) */ @media (prefers-color-scheme: dark) { :root:not([data-theme]) { --color-bg: #1E1E1E; /* … */ } } /* Комната конференции — всегда тёмная, независимо от выбора */ [data-theme="room"] { --color-room-bg: #1E1E1E; /* … */ } ``` **Hook:** `src/hooks/useTheme.ts` — управление явным выбором: - `readStoredTheme()` — читает из localStorage - `applyThemeAttribute(theme)` — проставляет атрибут `data-theme` на `` - `useTheme().resolved` — текущая активная тема (явный выбор ИЛИ системная) - `useTheme().setTheme(theme)` — задаёт явный выбор и сохраняет в localStorage **Компонент:** `src/components/ui/ThemeToggle.tsx` — сегментированный переключатель (☀️ светлая, 🌙 тёмная): ```tsx // Монтируется в ShellTopbar (лобби/календарь/админка) // AuthLayout (login/register/verify-email) // JoinPage (вход по номеру/ссылке) ``` **Анти-FOUC:** инлайн-скрипт в `frontend/index.html` (строки 8–26) выполняется до бандла React и проставляет `data-theme` на `` ДО первого рендера, если в localStorage есть явный выбор. Это исключает вспышку светлой темы при загрузке тёмного интерфейса. ### Инвариант: комната всегда отдельная Экран конференции (`RoomPage.tsx`, `[data-theme="room"]`) — единственное место, использующее свой тёмный набор токенов `--color-room-*` (§1.2a `DESIGN_SYSTEM.md`). Переключатель оболочки на неё **не влияет**. ### Палитра тёмной оболочки Получена переиспользованием токенов комнаты (не новых цветов, а уже утверждённых). Детали о смешивании, контрасте и исключениях (например, `--color-danger-solid` для кнопок) — см. `design/DESIGN_SYSTEM.md` §0.1 и §1.2b, `design/mockups/dark/README.md`. ## Стили **Tailwind CSS 4:** - Утилита-первый подход к стилям - Тёмная тема через атрибут `data-theme` - Дизайн-токены из `design/DESIGN_SYSTEM.md` (`design/tokens.css`) **Компоненты shadcn/ui:** - Предварительно построенные, без стилей (настраиваются с Tailwind) **Дизайн-система:** Дизайн-система и спецификации см. в `design/DESIGN_SYSTEM.md`: - Цветовая палитра, типография, спецификации компонентов - Макеты в `design/mockups/` (светлые) и `design/mockups/dark/` (тёмные) ## TypeScript Весь код должен быть type-safe: ```typescript // ✓ Правильно interface PageProps { userId: string } function MyComponent({ userId }: PageProps) { return
{userId}
} // ✗ Неправильно (implicit any) function BadComponent(props) { return
{props.userId}
} ``` Запустите `npm run build` (или `npx tsc -b --noEmit`) для проверки. ## Тестирование Автоматических frontend-тестов (unit/E2E) в проекте пока нет — проверка качества ограничивается ESLint и TypeScript. Backend покрыт pytest, см. [backend/README.md](../backend/README.md). ## Процесс разработки 1. `npm run dev` — запустить dev сервер 2. Создать фичевую ветку 3. Добавить/обновить страницу или компонент в `src/pages/` или `src/components/` 4. `npm run lint && npm run build` 5. Создать коммит с описанием Пример: ``` feat: добавить страницу профиля - Реализовать ProfilePage с редактированием ФИО и команды - Добавить функции в @/api/users.ts ESLint: 0 ошибок ``` ## Решение проблем **Порт Vite dev сервера 5173 занят:** ```bash npm run dev -- --port 3000 ``` **API proxy не работает:** Проверьте `vite.config.ts`: ```typescript server: { proxy: { '/api': { target: 'http://localhost:8000', changeOrigin: true, ws: true, }, }, } ``` **Ошибки TypeScript:** ```bash npx tsc -b --noEmit npm run lint -- --fix ``` **Сборка не удаётся:** ```bash npm install --force npm run build ``` ## Ссылки - [Корневой README](../README.md) — обзор проекта - [Справка API](../docs/api/README.md) — endpoint'ы - [Дизайн-система](../design/DESIGN_SYSTEM.md) — рекомендации UI - [Backend README](../backend/README.md) — детали FastAPI - [Документация Vite](https://vitejs.dev/) - [Документация React](https://react.dev/) - [Документация Tailwind](https://tailwindcss.com/)