Первоначальная версия VidConf
This commit is contained in:
328
frontend/README.md
Normal file
328
frontend/README.md
Normal file
@@ -0,0 +1,328 @@
|
||||
# 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` на `<html>`
|
||||
- `useTheme().resolved` — текущая активная тема (явный выбор ИЛИ системная)
|
||||
- `useTheme().setTheme(theme)` — задаёт явный выбор и сохраняет в localStorage
|
||||
|
||||
**Компонент:** `src/components/ui/ThemeToggle.tsx` — сегментированный переключатель (☀️ светлая, 🌙 тёмная):
|
||||
```tsx
|
||||
<ThemeToggle /> // Монтируется в ShellTopbar (лобби/календарь/админка)
|
||||
// AuthLayout (login/register/verify-email)
|
||||
// JoinPage (вход по номеру/ссылке)
|
||||
```
|
||||
|
||||
**Анти-FOUC:** инлайн-скрипт в `frontend/index.html` (строки 8–26) выполняется до бандла React и проставляет `data-theme` на `<html>` ДО первого рендера, если в 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 <div>{userId}</div>
|
||||
}
|
||||
|
||||
// ✗ Неправильно (implicit any)
|
||||
function BadComponent(props) {
|
||||
return <div>{props.userId}</div>
|
||||
}
|
||||
```
|
||||
|
||||
Запустите `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/)
|
||||
Reference in New Issue
Block a user