452 lines
15 KiB
Markdown
452 lines
15 KiB
Markdown
# Team Board — Frontend спецификация
|
||
|
||
## Общие сведения
|
||
|
||
**Технологии:**
|
||
- React 19.2.0
|
||
- TypeScript (~5.9.3)
|
||
- Vite 7.3.1 (bundler + dev server)
|
||
- React Router DOM 7.13.1 (роутинг)
|
||
- Tailwind CSS 4.2.1 (стили)
|
||
|
||
**Файл конфигурации:** `package.json`
|
||
**Архитектура:** SPA (Single Page Application)
|
||
|
||
---
|
||
|
||
## Структура проекта
|
||
|
||
```
|
||
src/
|
||
├── App.tsx # Главный компонент с роутингом
|
||
├── pages/ # Страницы приложения
|
||
│ ├── HomePage.tsx # Главная страница (список проектов)
|
||
│ ├── LoginPage.tsx # Страница входа
|
||
│ ├── ProjectPage.tsx # Страница проекта (канбан + чат)
|
||
│ ├── CreateProjectPage.tsx # Создание проекта
|
||
│ └── settings/ # Настройки
|
||
│ ├── SettingsLayout.tsx # Layout для настроек
|
||
│ ├── SettingsPage.tsx # Общие настройки
|
||
│ ├── LabelsPage.tsx # Управление лейблами
|
||
│ └── AgentsPage.tsx # Управление агентами
|
||
├── components/ # Переиспользуемые компоненты
|
||
│ ├── AuthGuard.tsx # Защита роутов авторизацией
|
||
│ ├── Sidebar.tsx # Боковая панель навигации
|
||
│ ├── KanbanBoard.tsx # Канбан доска задач
|
||
│ ├── TaskModal.tsx # Модальное окно задачи
|
||
│ ├── CreateTaskModal.tsx # Создание задачи
|
||
│ ├── CreateProjectModal.tsx # Создание проекта
|
||
│ ├── CreateAgentModal.tsx # Создание агента
|
||
│ ├── AgentModal.tsx # Просмотр/редактирование агента
|
||
│ ├── ProjectSettings.tsx # Настройки проекта
|
||
│ ├── ProjectFiles.tsx # Файлы проекта
|
||
│ └── ChatPanel.tsx # Панель чата
|
||
├── lib/ # Библиотеки и утилиты
|
||
│ ├── api.ts # REST API клиент
|
||
│ ├── ws.ts # WebSocket клиент
|
||
│ └── auth-client.ts # Авторизация
|
||
└── assets/ # Статические ресурсы
|
||
```
|
||
|
||
---
|
||
|
||
## Роуты и страницы
|
||
|
||
### Публичные роуты:
|
||
- `/login` — **LoginPage** — вход в систему
|
||
|
||
### Защищённые роуты (AuthGuard):
|
||
- `/` — **HomePage** — список проектов
|
||
- `/projects/:id` — **ProjectPage** — страница проекта с канбаном и чатом
|
||
- `/projects/new` — **CreateProjectPage** — создание нового проекта
|
||
- `/settings` — **SettingsLayout** — настройки (nested routes)
|
||
- `/settings` — **SettingsPage** — общие настройки
|
||
- `/settings/labels` — **LabelsPage** — управление лейблами
|
||
- `/settings/agents` — **AgentsPage** — управление агентами
|
||
|
||
### Роутинг:
|
||
```tsx
|
||
<BrowserRouter>
|
||
<Routes>
|
||
<Route path="/login" element={<LoginPage />} />
|
||
<Route path="/" element={<AuthGuard><HomePage /></AuthGuard>} />
|
||
<Route path="/projects/:id" element={<AuthGuard><ProjectPage /></AuthGuard>} />
|
||
<Route path="/projects/new" element={<AuthGuard><CreateProjectPage /></AuthGuard>} />
|
||
<Route path="/settings" element={<AuthGuard><SettingsLayout /></AuthGuard>}>
|
||
<Route index element={<SettingsPage />} />
|
||
<Route path="labels" element={<LabelsPage />} />
|
||
<Route path="agents" element={<AgentsPage />} />
|
||
</Route>
|
||
</Routes>
|
||
</BrowserRouter>
|
||
```
|
||
|
||
---
|
||
|
||
## Основные компоненты
|
||
|
||
### AuthGuard
|
||
**Файл:** `components/AuthGuard.tsx`
|
||
**Назначение:** проверяет авторизацию, перенаправляет на /login если токена нет
|
||
|
||
### KanbanBoard
|
||
**Файл:** `components/KanbanBoard.tsx`
|
||
**Назначение:** канбан доска с drag & drop задач между колонками
|
||
|
||
**Колонки:**
|
||
- Backlog (#737373)
|
||
- TODO (#3b82f6)
|
||
- In Progress (#f59e0b)
|
||
- Review (#a855f7)
|
||
- Done (#22c55e)
|
||
|
||
**Функции:**
|
||
- Drag & drop задач между статусами
|
||
- Фильтрация по исполнителю/лейблам
|
||
- Real-time обновления через WebSocket
|
||
- Создание задач в определённой колонке
|
||
|
||
### TaskModal
|
||
**Файл:** `components/TaskModal.tsx`
|
||
**Назначение:** полное отображение и редактирование задачи
|
||
|
||
**Секции:**
|
||
- Заголовок и описание
|
||
- Метаданные (статус, приоритет, лейблы, исполнитель)
|
||
- Steps (чеклист этапов)
|
||
- Комментарии и обсуждения
|
||
- Файловые вложения
|
||
|
||
### ChatPanel
|
||
**Файл:** `components/ChatPanel.tsx`
|
||
**Назначение:** панель чата проекта с real-time сообщениями
|
||
|
||
**Функции:**
|
||
- Отправка сообщений
|
||
- Упоминания участников (@username)
|
||
- Загрузка файлов
|
||
- Real-time обновления через WebSocket
|
||
|
||
### Sidebar
|
||
**Файл:** `components/Sidebar.tsx`
|
||
**Назначение:** боковая панель навигации
|
||
|
||
**Элементы:**
|
||
- Список проектов
|
||
- Общий чат (Lobby)
|
||
- Настройки
|
||
- Онлайн участники
|
||
|
||
---
|
||
|
||
## State Management
|
||
|
||
**Подход:** Локальный state с React hooks
|
||
**Глобального стора нет** — каждый компонент управляет своим состоянием
|
||
|
||
### Паттерны управления состоянием:
|
||
|
||
#### Загрузка данных:
|
||
```tsx
|
||
const [data, setData] = useState<T[]>([]);
|
||
const [loading, setLoading] = useState(true);
|
||
|
||
useEffect(() => {
|
||
async function load() {
|
||
try {
|
||
const result = await apiCall();
|
||
setData(result);
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
}
|
||
load();
|
||
}, [dependency]);
|
||
```
|
||
|
||
#### Real-time обновления:
|
||
```tsx
|
||
useEffect(() => {
|
||
const unsubscribe = wsClient.on("event.type", (data) => {
|
||
setItems(prev => prev.map(item =>
|
||
item.id === data.id ? data : item
|
||
));
|
||
});
|
||
return unsubscribe;
|
||
}, []);
|
||
```
|
||
|
||
#### Оптимистичные обновления:
|
||
```tsx
|
||
const handleUpdate = async (id: string, changes: Partial<T>) => {
|
||
// Оптимистично обновляем UI
|
||
setItems(prev => prev.map(item =>
|
||
item.id === id ? {...item, ...changes} : item
|
||
));
|
||
|
||
try {
|
||
await updateItem(id, changes);
|
||
} catch {
|
||
// Откат в случае ошибки
|
||
loadItems();
|
||
}
|
||
};
|
||
```
|
||
|
||
---
|
||
|
||
## API интеграция
|
||
|
||
**Файл:** `lib/api.ts`
|
||
**Базовый URL:** определяется через `VITE_API_URL` environment variable
|
||
|
||
### HTTP клиент:
|
||
```typescript
|
||
async function request<T>(path: string, options: RequestInit = {}): Promise<T> {
|
||
const token = getToken();
|
||
const headers = {
|
||
"Content-Type": "application/json",
|
||
...(token && { "Authorization": `Bearer ${token}` })
|
||
};
|
||
|
||
const res = await fetch(`${API_BASE}${path}`, { ...options, headers });
|
||
if (!res.ok) {
|
||
if (res.status === 401) {
|
||
localStorage.removeItem("tb_token");
|
||
window.location.href = "/login";
|
||
}
|
||
throw new Error(await res.json().error);
|
||
}
|
||
return res.json();
|
||
}
|
||
```
|
||
|
||
### API методы по категориям:
|
||
|
||
#### Auth:
|
||
- `login(login, password)` — вход в систему
|
||
|
||
#### Projects:
|
||
- `getProjects()` — список проектов
|
||
- `getProject(id)` — получить проект
|
||
- `createProject(data)` — создать проект
|
||
- `updateProject(id, data)` — обновить проект
|
||
- `deleteProject(id)` — удалить проект
|
||
|
||
#### Tasks:
|
||
- `getTasks(projectId)` — задачи проекта
|
||
- `getTask(id)` — получить задачу
|
||
- `createTask(projectId, data)` — создать задачу
|
||
- `updateTask(id, data)` — обновить задачу
|
||
- `deleteTask(id)` — удалить задачу
|
||
- `takeTask(id)` — взять задачу (для агентов)
|
||
- `rejectTask(id, reason)` — отклонить задачу
|
||
- `assignTask(id, assigneeId)` — назначить задачу
|
||
- `watchTask(id)` / `unwatchTask(id)` — подписки на задачу
|
||
|
||
#### Steps:
|
||
- `getTaskSteps(taskId)` — этапы задачи
|
||
- `createTaskStep(taskId, data)` — создать этап
|
||
- `updateTaskStep(taskId, stepId, data)` — обновить этап
|
||
- `deleteTaskStep(taskId, stepId)` — удалить этап
|
||
|
||
#### Messages:
|
||
- `getMessages(params)` — сообщения чата/задачи
|
||
- `createMessage(data)` — отправить сообщение
|
||
|
||
#### Members:
|
||
- `getMembers()` — список участников
|
||
- `getMember(id)` — получить участника
|
||
- `createMember(data)` — создать участника/агента
|
||
- `updateMember(id, data)` — обновить участника
|
||
- `regenerateMemberToken(id)` — новый токен агента
|
||
- `revokeMemberToken(id)` — отозвать токен
|
||
|
||
#### Labels:
|
||
- `getLabels()` — глобальные лейблы
|
||
- `createLabel(data)` — создать лейбл
|
||
- `updateLabel(id, data)` — обновить лейбл
|
||
- `deleteLabel(id)` — удалить лейбл
|
||
|
||
#### Files:
|
||
- `uploadFile(file)` — загрузить временный файл
|
||
- `downloadAttachment(id)` — скачать вложение
|
||
- `getProjectFiles(projectId)` — файлы проекта
|
||
- `uploadProjectFile(projectId, file, description)` — загрузить файл проекта
|
||
- `deleteProjectFile(projectId, fileId)` — удалить файл проекта
|
||
|
||
---
|
||
|
||
## WebSocket интеграция
|
||
|
||
**Файл:** `lib/ws.ts`
|
||
**URL:** определяется через `VITE_WS_URL` environment variable
|
||
|
||
### WSClient класс:
|
||
```typescript
|
||
class WSClient {
|
||
connect() // подключение с токеном из localStorage
|
||
disconnect() // отключение
|
||
on(type, handler) // подписка на события
|
||
send(data) // отправка сообщения
|
||
|
||
// Convenience методы:
|
||
subscribeProject(projectId)
|
||
unsubscribeProject(projectId)
|
||
sendChat(chatId, content, mentions)
|
||
sendTaskComment(taskId, content, mentions)
|
||
heartbeat(status)
|
||
}
|
||
```
|
||
|
||
### Обработка событий:
|
||
```typescript
|
||
useEffect(() => {
|
||
const unsubscribe = wsClient.on("message.new", (message) => {
|
||
setMessages(prev => [...prev, message]);
|
||
});
|
||
return unsubscribe;
|
||
}, []);
|
||
```
|
||
|
||
### Поддерживаемые события:
|
||
- `auth.ok` — успешная авторизация
|
||
- `auth.error` — ошибка авторизации
|
||
- `message.new` — новое сообщение
|
||
- `task.created` — задача создана
|
||
- `task.updated` — задача обновлена
|
||
- `task.assigned` — задача назначена
|
||
- `task.deleted` — задача удалена
|
||
- `agent.status` — изменение статуса участника
|
||
- `agent.stream.*` — streaming от агентов
|
||
|
||
### Auto-reconnect:
|
||
- При разрыве соединения автоматический переподключение через 3 секунды
|
||
- Heartbeat каждые 30 секунд для поддержания соединения
|
||
- Очередь сообщений до подключения и авторизации
|
||
|
||
---
|
||
|
||
## Авторизация
|
||
|
||
**Файл:** `lib/auth-client.ts`
|
||
|
||
### Токены:
|
||
- Хранение: `localStorage.getItem("tb_token")`
|
||
- Формат: JWT токен
|
||
- Автоматическое добавление в заголовки API запросов
|
||
- Автоматический redirect на /login при 401 ошибке
|
||
|
||
### AuthGuard компонент:
|
||
```tsx
|
||
function AuthGuard({ children }: { children: React.ReactNode }) {
|
||
const token = localStorage.getItem("tb_token");
|
||
if (!token) return <Navigate to="/login" />;
|
||
return <>{children}</>;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Стили и UI
|
||
|
||
### Tailwind CSS 4.2.1:
|
||
- Utility-first подход
|
||
- CSS переменные для темизации
|
||
- Responsive дизайн
|
||
|
||
### Цветовая схема:
|
||
```css
|
||
:root {
|
||
--background: #ffffff;
|
||
--foreground: #0f0f0f;
|
||
--muted: #6b7280;
|
||
--border: #e5e7eb;
|
||
--primary: #3b82f6;
|
||
--destructive: #ef4444;
|
||
}
|
||
```
|
||
|
||
### Компоненты UI:
|
||
- Модальные окна
|
||
- Drag & drop интерфейсы
|
||
- Kanban колонки
|
||
- Формы с валидацией
|
||
- Файл загрузчики
|
||
- Chat интерфейс
|
||
|
||
---
|
||
|
||
## Environment переменные
|
||
|
||
**Файл:** `.env`
|
||
|
||
```bash
|
||
VITE_API_URL=http://localhost:8100 # Базовый URL REST API
|
||
VITE_WS_URL=ws://localhost:8100 # Базовый URL WebSocket
|
||
```
|
||
|
||
**Использование:**
|
||
```typescript
|
||
const API_BASE = import.meta.env.VITE_API_URL!;
|
||
const WS_BASE = import.meta.env.VITE_WS_URL!;
|
||
```
|
||
|
||
---
|
||
|
||
## Типизация
|
||
|
||
### TypeScript конфигурация:
|
||
- Строгий режим
|
||
- Path mapping (`@/` → `src/`)
|
||
- DOM типы
|
||
|
||
### API типы:
|
||
Все типы синхронизированы с backend Pydantic схемами:
|
||
- `Member`, `AgentConfig`, `MemberBrief`
|
||
- `Project`, `ProjectMember`
|
||
- `Task`, `Step`, `SubtaskBrief`
|
||
- `Message`, `Attachment`
|
||
- `Label`
|
||
|
||
---
|
||
|
||
## Производительность
|
||
|
||
### Оптимизации:
|
||
- Lazy loading компонентов
|
||
- Мемоизация через React.memo для тяжёлых компонентов
|
||
- Дебаунсинг поисковых запросов
|
||
- Пагинация сообщений чата
|
||
- Оптимистичные обновления UI
|
||
|
||
### Bundle размер:
|
||
- Tree shaking неиспользуемого кода
|
||
- Code splitting по роутам
|
||
- Минификация в production
|
||
|
||
---
|
||
|
||
## Особенности архитектуры
|
||
|
||
### Real-time синхронизация:
|
||
- WebSocket для мгновенных обновлений
|
||
- Оптимистичные обновления с откатом при ошибках
|
||
- Автоматическая подписка на события проекта
|
||
|
||
### Drag & Drop:
|
||
- Нативный HTML5 drag & drop API
|
||
- Визуальная обратная связь при перетаскивании
|
||
- Оптимистичное обновление с откатом
|
||
|
||
### Файловая система:
|
||
- Поддержка загрузки файлов через drag & drop
|
||
- Preview для изображений
|
||
- Скачивание файлов через API
|
||
|
||
### Offline-first:
|
||
- Кэширование в localStorage (токен авторизации)
|
||
- Graceful degradation при отсутствии сети
|
||
- Очередь WebSocket сообщений
|
||
|
||
Этот документ описывает фронтенд на основе исходного кода в `/root/projects/team-board/web-client-vite/src/` на дату создания спецификации. |