88 KiB
Архитектура Team Planner
1. C4 Model
1.1 Level 1: System Context
flowchart TB
subgraph external [" "]
User["👤 Пользователь<br/><i>Член команды разработки</i>"]
AI["🤖 AI Proxy Service<br/><i>LLM для оценки задач</i>"]
KC["🔐 Keycloak<br/><i>auth.vigdorov.ru<br/>Identity Provider</i>"]
end
subgraph system ["Team Planner"]
TP["🎯 Team Planner<br/><i>Приложение для управления<br/>бэклогом идей команды</i>"]
end
User -->|"Управляет идеями,<br/>командой, комментариями<br/>[HTTPS]"| TP
User -->|"Авторизация<br/>[OIDC/PKCE]"| KC
KC -->|"JWT токены"| TP
TP -->|"Запросы на оценку<br/>трудозатрат<br/>[HTTPS/REST]"| AI
style TP fill:#1168bd,color:#fff
style User fill:#08427b,color:#fff
style AI fill:#999,color:#fff
style KC fill:#c92a2a,color:#fff
1.2 Level 2: Container Diagram
flowchart TB
User["👤 Пользователь<br/><i>Член команды разработки</i>"]
Admin["👤 Администратор<br/><i>email из K8s Secret</i>"]
KC["🔐 Keycloak<br/><i>auth.vigdorov.ru</i>"]
subgraph TeamPlanner ["Team Planner"]
SPA["📱 Frontend SPA<br/><i>React, TypeScript, MUI</i><br/><br/>Веб-интерфейс для<br/>работы с идеями"]
API["⚙️ Backend API<br/><i>NestJS, TypeScript</i><br/><br/>REST API"]
WS["🔌 WebSocket Gateway<br/><i>Socket.io</i><br/><br/>Real-time обновления"]
DB[("🗄️ Database<br/><i>PostgreSQL</i><br/><br/>Идеи, команда,<br/>права, аудит")]
end
AI["🤖 AI Proxy Service<br/><i>LLM для оценки задач</i>"]
User -->|"Использует<br/>[HTTPS]"| SPA
Admin -->|"Управляет правами<br/>[HTTPS]"| SPA
User <-->|"OIDC Login<br/>[Redirect]"| KC
SPA -->|"API запросы<br/>[REST + Bearer JWT]"| API
SPA <-->|"Real-time<br/>[WebSocket]"| WS
API -.->|"Валидация JWT<br/>[JWKS]"| KC
API -->|"Читает/пишет<br/>[TypeORM]"| DB
WS -->|"Читает<br/>[TypeORM]"| DB
API -->|"Оценка трудозатрат<br/>[HTTPS/REST]"| AI
API -->|"Уведомления<br/>[Events]"| WS
style SPA fill:#438dd5,color:#fff
style API fill:#438dd5,color:#fff
style WS fill:#438dd5,color:#fff
style DB fill:#438dd5,color:#fff
style User fill:#08427b,color:#fff
style Admin fill:#08427b,color:#fff
style AI fill:#999,color:#fff
style KC fill:#c92a2a,color:#fff
1.3 Level 3: Component Diagram — Backend API
flowchart TB
subgraph API ["Backend API (NestJS)"]
subgraph Controllers ["Controllers"]
IdeasCtrl["IdeasController<br/><i>CRUD идей, reorder</i>"]
CommentsCtrl["CommentsController<br/><i>Комментарии к идеям</i>"]
TeamCtrl["TeamController<br/><i>Члены команды, роли</i>"]
AiCtrl["AiController<br/><i>AI-оценка, генерация ТЗ</i>"]
PermCtrl["PermissionsController<br/><i>Управление правами</i>"]
AuditCtrl["AuditController<br/><i>История действий</i>"]
ExportCtrl["ExportController<br/><i>Экспорт в DOCX</i>"]
end
subgraph Services ["Services"]
IdeasSvc["IdeasService"]
CommentsSvc["CommentsService"]
TeamSvc["TeamService"]
AiSvc["AiService<br/><i>Интеграция с AI Proxy</i>"]
PermSvc["PermissionsService<br/><i>Проверка/управление правами</i>"]
AuditSvc["AuditService<br/><i>Логирование действий</i>"]
ExportSvc["ExportService<br/><i>Генерация DOCX</i>"]
end
subgraph Gateway ["WebSocket Gateway"]
WSGateway["EventsGateway<br/><i>Socket.io</i><br/><i>Real-time события</i>"]
end
subgraph Auth ["Auth Module"]
JwtStrategy["JwtStrategy<br/><i>JWKS валидация</i>"]
JwtGuard["JwtAuthGuard<br/><i>Глобальная защита</i>"]
PermGuard["PermissionsGuard<br/><i>Проверка прав</i>"]
end
subgraph Entities ["Entities (TypeORM)"]
IdeaEntity[("Idea")]
CommentEntity[("Comment")]
TeamMemberEntity[("TeamMember")]
RoleEntity[("Role")]
UserEntity[("User")]
UserPermEntity[("UserPermissions")]
AuditEntity[("AuditLog")]
end
end
KC["🔐 Keycloak"]
AI["🤖 AI Proxy"]
DB[("PostgreSQL")]
IdeasCtrl --> IdeasSvc
CommentsCtrl --> CommentsSvc
TeamCtrl --> TeamSvc
AiCtrl --> AiSvc
PermCtrl --> PermSvc
AuditCtrl --> AuditSvc
ExportCtrl --> ExportSvc
IdeasSvc --> IdeaEntity
IdeasSvc --> AuditSvc
IdeasSvc --> WSGateway
CommentsSvc --> CommentEntity
CommentsSvc --> AuditSvc
TeamSvc --> TeamMemberEntity
TeamSvc --> RoleEntity
AiSvc --> IdeaEntity
AiSvc --> TeamMemberEntity
AiSvc -->|"HTTP"| AI
AiSvc --> AuditSvc
PermSvc --> UserPermEntity
PermSvc --> UserEntity
AuditSvc --> AuditEntity
ExportSvc --> IdeaEntity
ExportSvc --> CommentEntity
IdeaEntity --> DB
CommentEntity --> DB
TeamMemberEntity --> DB
RoleEntity --> DB
UserEntity --> DB
UserPermEntity --> DB
AuditEntity --> DB
JwtStrategy -.->|"JWKS"| KC
JwtGuard --> JwtStrategy
PermGuard --> PermSvc
style IdeasCtrl fill:#85c1e9,color:#000
style CommentsCtrl fill:#85c1e9,color:#000
style TeamCtrl fill:#85c1e9,color:#000
style AiCtrl fill:#85c1e9,color:#000
style PermCtrl fill:#85c1e9,color:#000
style AuditCtrl fill:#85c1e9,color:#000
style ExportCtrl fill:#85c1e9,color:#000
style IdeasSvc fill:#82e0aa,color:#000
style CommentsSvc fill:#82e0aa,color:#000
style TeamSvc fill:#82e0aa,color:#000
style AiSvc fill:#82e0aa,color:#000
style PermSvc fill:#82e0aa,color:#000
style AuditSvc fill:#82e0aa,color:#000
style ExportSvc fill:#82e0aa,color:#000
style WSGateway fill:#f5b7b1,color:#000
style JwtStrategy fill:#f9e79f,color:#000
style JwtGuard fill:#f9e79f,color:#000
style PermGuard fill:#f9e79f,color:#000
style AI fill:#999,color:#fff
style KC fill:#c92a2a,color:#fff
1.4 Level 3: Component Diagram — Frontend SPA
flowchart TB
subgraph SPA ["Frontend SPA (React)"]
subgraph Pages ["Pages / Views"]
IdeasPage["IdeasPage<br/><i>Главная страница</i>"]
TeamPage["TeamPage<br/><i>Управление командой</i>"]
AdminPage["AdminPage<br/><i>Панель администратора</i>"]
AuditPage["AuditPage<br/><i>История действий</i>"]
LoginPage["LoginPage<br/><i>Страница входа</i>"]
end
subgraph Components ["UI Components"]
IdeasTable["IdeasTable<br/><i>Таблица идей + D&D</i>"]
IdeasFilters["IdeasFilters<br/><i>Фильтры и поиск</i>"]
CreateIdeaModal["CreateIdeaModal"]
AiEstimateModal["AiEstimateModal<br/><i>Результат AI-оценки</i>"]
CommentsPanel["CommentsPanel<br/><i>Комментарии к идее</i>"]
SpecModal["SpecificationModal<br/><i>Просмотр/редакт. ТЗ</i>"]
PermissionsTable["PermissionsTable<br/><i>Таблица прав доступа</i>"]
AuditLogTable["AuditLogTable<br/><i>Таблица истории</i>"]
ThemeToggle["ThemeToggle<br/><i>Переключатель темы</i>"]
OnlineUsers["OnlineUsers<br/><i>Онлайн пользователи</i>"]
end
subgraph State ["State Management"]
IdeasStore["IdeasStore<br/><i>Zustand</i>"]
ThemeStore["ThemeStore<br/><i>Zustand</i>"]
ReactQuery["React Query<br/><i>Кэш, мутации</i>"]
end
subgraph ServicesLayer ["Services"]
IdeasAPI["ideasApi"]
TeamAPI["teamApi"]
AiAPI["aiApi"]
CommentsAPI["commentsApi"]
PermissionsAPI["permissionsApi"]
AuditAPI["auditApi"]
ExportAPI["exportApi"]
end
subgraph AuthLayer ["Auth"]
AuthProvider["AuthProvider<br/><i>Keycloak context</i>"]
ApiClient["api.ts<br/><i>Axios + interceptors</i>"]
end
subgraph WebSocketLayer ["WebSocket"]
WSProvider["WebSocketProvider<br/><i>Socket.io client</i>"]
WSHooks["useWebSocket<br/><i>Real-time хуки</i>"]
end
subgraph ThemeLayer ["Theme"]
ThemeProvider["ThemeProvider<br/><i>MUI dark/light</i>"]
end
end
KC["🔐 Keycloak"]
API["⚙️ Backend API"]
WS["🔌 WebSocket Gateway"]
IdeasPage --> IdeasTable
IdeasPage --> IdeasFilters
IdeasPage --> CreateIdeaModal
IdeasPage --> OnlineUsers
IdeasTable --> AiEstimateModal
IdeasTable --> CommentsPanel
IdeasTable --> SpecModal
TeamPage --> TeamAPI
AdminPage --> PermissionsTable
AuditPage --> AuditLogTable
IdeasTable --> ReactQuery
ReactQuery --> IdeasAPI
ReactQuery --> AiAPI
ReactQuery --> CommentsAPI
ReactQuery --> PermissionsAPI
ReactQuery --> AuditAPI
ReactQuery --> ExportAPI
IdeasFilters --> IdeasStore
ThemeToggle --> ThemeStore
IdeasAPI --> ApiClient
TeamAPI --> ApiClient
AiAPI --> ApiClient
CommentsAPI --> ApiClient
PermissionsAPI --> ApiClient
AuditAPI --> ApiClient
ExportAPI --> ApiClient
ApiClient -->|"REST + JWT"| API
AuthProvider -->|"OIDC"| KC
WSProvider <-->|"WebSocket"| WS
WSHooks --> WSProvider
ThemeProvider --> ThemeStore
style IdeasPage fill:#d4e6f1,color:#000
style TeamPage fill:#d4e6f1,color:#000
style AdminPage fill:#d4e6f1,color:#000
style AuditPage fill:#d4e6f1,color:#000
style LoginPage fill:#d4e6f1,color:#000
style IdeasTable fill:#aed6f1,color:#000
style IdeasFilters fill:#aed6f1,color:#000
style CreateIdeaModal fill:#aed6f1,color:#000
style AiEstimateModal fill:#aed6f1,color:#000
style CommentsPanel fill:#aed6f1,color:#000
style SpecModal fill:#aed6f1,color:#000
style PermissionsTable fill:#aed6f1,color:#000
style AuditLogTable fill:#aed6f1,color:#000
style ThemeToggle fill:#aed6f1,color:#000
style OnlineUsers fill:#aed6f1,color:#000
style IdeasStore fill:#a9dfbf,color:#000
style ThemeStore fill:#a9dfbf,color:#000
style ReactQuery fill:#a9dfbf,color:#000
style AuthProvider fill:#f9e79f,color:#000
style ApiClient fill:#f9e79f,color:#000
style WSProvider fill:#f5b7b1,color:#000
style WSHooks fill:#f5b7b1,color:#000
style ThemeProvider fill:#d7bde2,color:#000
style API fill:#438dd5,color:#fff
style WS fill:#438dd5,color:#fff
style KC fill:#c92a2a,color:#fff
2. Sequence Diagrams
2.0 Авторизация (Keycloak OIDC)
sequenceDiagram
autonumber
actor User as Пользователь
participant FE as Frontend<br/>(React)
participant KC as Keycloak<br/>(auth.vigdorov.ru)
participant BE as Backend<br/>(NestJS)
User->>FE: Открывает приложение
FE->>FE: keycloak.init({ onLoad: 'login-required' })
FE->>KC: Redirect на /auth (PKCE)
KC-->>User: Форма входа
User->>KC: Вводит логин/пароль
KC->>KC: Проверяет credentials
KC-->>FE: Redirect с authorization code
FE->>KC: POST /token (code + code_verifier)
KC-->>FE: { access_token, refresh_token }
FE->>FE: Сохраняет токены в памяти
FE-->>User: Показывает приложение
Note over FE,BE: Все последующие API запросы
FE->>BE: GET /api/ideas<br/>Authorization: Bearer {token}
BE->>KC: GET /certs (JWKS, кэшируется)
KC-->>BE: Public keys
BE->>BE: Валидация JWT подписи
BE-->>FE: 200 OK { data }
Note over FE,KC: Автообновление токена (каждые 10 сек)
FE->>KC: POST /token (refresh_token)
KC-->>FE: { new_access_token }
2.1 Создание идеи
sequenceDiagram
autonumber
actor User as Пользователь
participant FE as Frontend<br/>(React)
participant BE as Backend<br/>(NestJS)
participant DB as PostgreSQL
User->>FE: Заполняет форму идеи
FE->>FE: Валидация на клиенте
FE->>BE: POST /api/ideas
BE->>BE: Валидация DTO
BE->>DB: INSERT INTO ideas
DB-->>BE: idea record
BE-->>FE: 201 Created { idea }
FE->>FE: Добавляет в store
FE-->>User: Показывает новую идею в списке
2.2 Inline-редактирование идеи
sequenceDiagram
autonumber
actor User as Пользователь
participant FE as Frontend<br/>(React)
participant BE as Backend<br/>(NestJS)
participant DB as PostgreSQL
User->>FE: Double-click на ячейку
FE->>FE: Переключает в режим редактирования
User->>FE: Изменяет значение, blur/Enter
FE->>FE: Оптимистичное обновление store
FE->>BE: PATCH /api/ideas/:id
BE->>BE: Валидация DTO
BE->>DB: UPDATE ideas SET ...
DB-->>BE: updated idea
BE-->>FE: 200 OK { idea }
FE->>FE: Подтверждает изменение в store
alt Ошибка
BE-->>FE: 4xx/5xx error
FE->>FE: Откат оптимистичного обновления
FE-->>User: Показывает ошибку
end
2.3 Drag & Drop (изменение порядка)
sequenceDiagram
autonumber
actor User as Пользователь
participant FE as Frontend<br/>(React)
participant BE as Backend<br/>(NestJS)
participant DB as PostgreSQL
User->>FE: Перетаскивает идею
FE->>FE: dnd-kit обновляет UI
FE->>FE: Оптимистичное обновление порядка
FE->>BE: PATCH /api/ideas/reorder
Note right of FE: { ids: [id1, id2, ...] }
BE->>DB: UPDATE ideas SET position = ...
DB-->>BE: OK
BE-->>FE: 200 OK
FE-->>User: Порядок сохранён
2.4 AI-оценка трудозатрат
sequenceDiagram
autonumber
actor User as Пользователь
participant FE as Frontend<br/>(React)
participant BE as Backend<br/>(NestJS)
participant AI as AI Proxy
participant DB as PostgreSQL
User->>FE: Нажимает "Оценить"
FE->>FE: Показывает loader
FE->>BE: POST /api/ai/estimate
Note right of FE: { ideaId }
BE->>DB: SELECT idea, team_members
DB-->>BE: idea + team data
BE->>BE: Формирует промпт
BE->>AI: POST /chat/completions
Note right of BE: prompt с описанием идеи<br/>и составом команды
AI-->>BE: LLM response
BE->>BE: Парсит ответ
BE->>DB: UPDATE ideas SET estimate = ...
DB-->>BE: OK
BE-->>FE: 200 OK { estimate }
FE->>FE: Обновляет store
FE-->>User: Показывает оценку
2.5 Генерация мини-ТЗ
sequenceDiagram
autonumber
actor User as Пользователь
participant FE as Frontend<br/>(React)
participant BE as Backend<br/>(NestJS)
participant AI as AI Proxy
participant DB as PostgreSQL
User->>FE: Нажимает "ТЗ" (генерация)
FE->>FE: Открывает модалку, показывает loader
FE->>BE: POST /api/ai/generate-specification
Note right of FE: { ideaId }
BE->>DB: SELECT idea
DB-->>BE: idea data
BE->>BE: Формирует промпт для ТЗ
BE->>AI: POST /chat/completions
Note right of BE: prompt с описанием идеи
AI-->>BE: LLM response (ТЗ в markdown)
BE->>BE: Парсит ответ
BE->>DB: UPDATE ideas SET specification = ...
DB-->>BE: OK
BE-->>FE: 200 OK { specification }
FE->>FE: Показывает ТЗ в модалке
FE-->>User: Может просмотреть/редактировать
2.6 Редактирование ТЗ
sequenceDiagram
autonumber
actor User as Пользователь
participant FE as Frontend<br/>(React)
participant BE as Backend<br/>(NestJS)
participant DB as PostgreSQL
User->>FE: Открывает модалку ТЗ
FE-->>User: Показывает сохранённое ТЗ
User->>FE: Нажимает "Редактировать"
FE->>FE: Переключает в режим редактирования
User->>FE: Изменяет текст ТЗ
User->>FE: Нажимает "Сохранить"
FE->>BE: PATCH /api/ideas/:id
Note right of FE: { specification: "..." }
BE->>DB: UPDATE ideas SET specification = ...
DB-->>BE: updated idea
BE-->>FE: 200 OK { idea }
FE->>FE: Обновляет store
FE-->>User: Показывает обновлённое ТЗ
2.7 Добавление комментария
sequenceDiagram
autonumber
actor User as Пользователь
participant FE as Frontend<br/>(React)
participant BE as Backend<br/>(NestJS)
participant DB as PostgreSQL
User->>FE: Пишет комментарий
FE->>BE: POST /api/ideas/:id/comments
Note right of FE: { text, parentId? }
BE->>BE: Валидация
BE->>DB: INSERT INTO comments
DB-->>BE: comment record
BE-->>FE: 201 Created { comment }
FE->>FE: Добавляет в store
FE-->>User: Показывает комментарий
2.6 Загрузка списка идей с фильтрами
sequenceDiagram
autonumber
actor User as Пользователь
participant FE as Frontend<br/>(React)
participant BE as Backend<br/>(NestJS)
participant DB as PostgreSQL
User->>FE: Открывает страницу / меняет фильтры
FE->>FE: Показывает skeleton loader
FE->>BE: GET /api/ideas?status=...&priority=...
BE->>DB: SELECT * FROM ideas WHERE ... ORDER BY ...
DB-->>BE: ideas[]
BE-->>FE: 200 OK { data, total, page }
FE->>FE: Сохраняет в store
FE-->>User: Отображает таблицу
2.9 Real-time обновления (WebSocket)
sequenceDiagram
autonumber
actor User1 as Пользователь 1
actor User2 as Пользователь 2
participant FE1 as Frontend 1
participant FE2 as Frontend 2
participant WS as WebSocket Gateway
participant BE as Backend
participant DB as PostgreSQL
Note over FE1,FE2: Оба пользователя подключены к WebSocket
FE1->>WS: connect(token)
WS->>WS: Валидация JWT
WS-->>FE1: connected
FE2->>WS: connect(token)
WS-->>FE2: connected
User1->>FE1: Редактирует идею
FE1->>BE: PATCH /api/ideas/:id
BE->>DB: UPDATE ideas
DB-->>BE: OK
BE->>WS: emit('idea:updated', idea)
WS-->>FE1: idea:updated
WS-->>FE2: idea:updated
FE1->>FE1: Обновляет store
FE2->>FE2: Обновляет store
FE2-->>User2: Видит изменения в реальном времени
2.10 Проверка прав доступа
sequenceDiagram
autonumber
actor User as Пользователь
participant FE as Frontend
participant BE as Backend
participant DB as PostgreSQL
User->>FE: Пытается создать идею
FE->>BE: POST /api/ideas
Note right of FE: Authorization: Bearer token
BE->>BE: JwtAuthGuard: валидация JWT
BE->>DB: SELECT * FROM user_permissions WHERE userId = ?
DB-->>BE: permissions
BE->>BE: PermissionsGuard: проверка 'create_ideas'
alt Право есть
BE->>DB: INSERT INTO ideas
BE->>DB: INSERT INTO audit_log
DB-->>BE: OK
BE-->>FE: 201 Created { idea }
else Права нет
BE-->>FE: 403 Forbidden { message: "Недостаточно прав" }
FE-->>User: Показывает ошибку
end
2.11 Изменение прав пользователя (Админ)
sequenceDiagram
autonumber
actor Admin as Администратор
participant FE as Frontend
participant BE as Backend
participant DB as PostgreSQL
Admin->>FE: Открывает панель администратора
FE->>BE: GET /api/permissions/users
BE->>BE: Проверка: isAdmin(user.email)
BE->>DB: SELECT * FROM users + permissions
DB-->>BE: users with permissions
BE-->>FE: 200 OK { users }
FE-->>Admin: Показывает таблицу прав
Admin->>FE: Изменяет право пользователя
FE->>BE: PATCH /api/permissions/:userId
Note right of FE: { create_ideas: true }
BE->>BE: Проверка: isAdmin
BE->>DB: UPDATE user_permissions
BE->>DB: INSERT INTO audit_log
DB-->>BE: OK
BE-->>FE: 200 OK { permissions }
FE-->>Admin: Обновляет UI
2.12 Просмотр и восстановление из аудита
sequenceDiagram
autonumber
actor User as Пользователь
participant FE as Frontend
participant BE as Backend
participant DB as PostgreSQL
User->>FE: Открывает историю действий
FE->>BE: GET /api/audit?entityType=idea&page=1
BE->>BE: Проверка права 'view_audit_log'
BE->>DB: SELECT * FROM audit_log WHERE ...
DB-->>BE: audit entries
BE-->>FE: 200 OK { data, meta }
FE-->>User: Показывает таблицу истории
User->>FE: Нажимает "Восстановить" на удалённой идее
FE->>BE: POST /api/audit/:id/restore
BE->>DB: SELECT oldValue FROM audit_log
DB-->>BE: { oldValue: {...} }
BE->>DB: INSERT INTO ideas (oldValue data)
BE->>DB: INSERT INTO audit_log (restore action)
DB-->>BE: OK
BE-->>FE: 201 Created { idea }
FE->>FE: Обновляет список идей
FE-->>User: Идея восстановлена
2.13 Экспорт идеи в DOCX
sequenceDiagram
autonumber
actor User as Пользователь
participant FE as Frontend
participant BE as Backend
participant DB as PostgreSQL
User->>FE: Нажимает "Экспорт" на идее
FE->>BE: GET /api/export/idea/:id
BE->>BE: Проверка права 'export_ideas'
BE->>DB: SELECT idea + comments + estimate
DB-->>BE: full idea data
BE->>BE: Генерирует DOCX (docx library)
BE-->>FE: 200 OK (binary, Content-Type: application/vnd.openxmlformats...)
FE->>FE: Скачивает файл
FE-->>User: Файл скачан
3. API Contracts
3.1 Ideas
GET /api/ideas
Получение списка идей с пагинацией и фильтрами.
Query Parameters:
{
page?: number; // default: 1
limit?: number; // default: 50
status?: IdeaStatus; // фильтр по статусу
priority?: Priority; // фильтр по приоритету
module?: Module; // фильтр по модулю
color?: string; // фильтр по цвету
search?: string; // поиск по тексту
sortBy?: string; // поле для сортировки
sortOrder?: 'asc' | 'desc';
}
Response 200:
{
data: Idea[];
meta: {
total: number;
page: number;
limit: number;
totalPages: number;
}
}
POST /api/ideas
Создание новой идеи.
Request Body:
{
title: string; // обязательное
description?: string;
status: IdeaStatus; // default: 'new'
priority: Priority; // default: 'medium'
module: Module[];
targetAudience?: string;
painPoint?: string;
aiRole?: string;
validationMethod?: string;
color?: string;
position?: number;
}
Response 201:
{
id: string;
title: string;
description: string | null;
status: IdeaStatus;
priority: Priority;
module: Module[];
targetAudience: string | null;
painPoint: string | null;
aiRole: string | null;
validationMethod: string | null;
color: string | null;
position: number;
estimate: Estimate | null;
createdAt: string;
updatedAt: string;
}
PATCH /api/ideas/:id
Обновление идеи (partial update).
Request Body:
Partial<{
title: string;
description: string;
status: IdeaStatus;
priority: Priority;
module: Module[];
targetAudience: string;
painPoint: string;
aiRole: string;
validationMethod: string;
color: string;
}>
Response 200: Idea
DELETE /api/ideas/:id
Удаление идеи.
Response 204: No Content
PATCH /api/ideas/reorder
Изменение порядка идей.
Request Body:
{
ids: string[]; // ID идей в новом порядке
}
Response 200:
{
success: true;
}
3.2 Comments
GET /api/ideas/:ideaId/comments
Получение комментариев к идее.
Response 200:
{
data: Comment[];
}
// Comment
{
id: string;
text: string;
ideaId: string;
parentId: string | null; // для тредов
createdAt: string;
updatedAt: string;
replies?: Comment[]; // вложенные ответы
}
POST /api/ideas/:ideaId/comments
Добавление комментария.
Request Body:
{
text: string;
parentId?: string; // для ответа на комментарий
}
Response 201: Comment
DELETE /api/comments/:id
Удаление комментария.
Response 204: No Content
3.3 Team
GET /api/team/members
Получение списка членов команды.
Response 200:
{
data: TeamMember[];
}
// TeamMember
{
id: string;
name: string;
role: TeamRole;
productivity: {
trivial: number; // часы
easy: number;
medium: number;
hard: number;
epic: number;
};
createdAt: string;
updatedAt: string;
}
POST /api/team/members
Добавление члена команды.
Request Body:
{
name: string;
role: TeamRole;
productivity?: {
trivial?: number;
easy?: number;
medium?: number;
hard?: number;
epic?: number;
};
}
Response 201: TeamMember
PATCH /api/team/members/:id
Обновление члена команды.
Request Body: Partial<TeamMember>
Response 200: TeamMember
DELETE /api/team/members/:id
Удаление члена команды.
Response 204: No Content
GET /api/team/summary
Сводка по команде.
Response 200:
{
totalMembers: number;
byRole: Record<TeamRole, number>;
}
3.4 AI
POST /api/ai/estimate
AI-оценка трудозатрат для идеи.
Request Body:
{
ideaId: string;
}
Response 200:
{
ideaId: string;
estimate: {
totalHours: number;
totalDays: number;
complexity: 'trivial' | 'easy' | 'medium' | 'hard' | 'epic';
breakdown: {
role: TeamRole;
hours: number;
complexity: Complexity;
description: string;
}[];
recommendations?: string[];
};
}
POST /api/ai/generate-specification
AI-генерация мини-ТЗ для идеи.
Request Body:
{
ideaId: string;
}
Response 200:
{
ideaId: string;
specification: string; // markdown текст ТЗ
generatedAt: string; // ISO timestamp
}
Структура генерируемого ТЗ:
## Цель
[что должно быть достигнуто]
## Функциональные требования
- [требование 1]
- [требование 2]
...
## Технические требования
[архитектура, интеграции, ограничения]
## Критерии приёмки
- [критерий 1]
- [критерий 2]
...
## Зависимости и риски
[что может повлиять на реализацию]
3.5 Enums
enum IdeaStatus {
NEW = 'new',
DISCUSSING = 'discussing',
APPROVED = 'approved',
IN_PROGRESS = 'in_progress',
DONE = 'done',
REJECTED = 'rejected'
}
enum Priority {
CRITICAL = 'critical',
HIGH = 'high',
MEDIUM = 'medium',
LOW = 'low'
}
enum Module {
FRONTEND = 'frontend',
BACKEND = 'backend',
AI = 'ai',
MOBILE = 'mobile',
INFRASTRUCTURE = 'infrastructure',
OTHER = 'other'
}
enum TeamRole {
BACKEND = 'backend',
FRONTEND = 'frontend',
AI_ML = 'ai_ml',
ANALYST = 'analyst',
QA = 'qa',
DEVOPS = 'devops',
DESIGNER = 'designer',
PM = 'pm'
}
enum Complexity {
TRIVIAL = 'trivial',
EASY = 'easy',
MEDIUM = 'medium',
HARD = 'hard',
EPIC = 'epic'
}
enum Permission {
VIEW_IDEAS = 'view_ideas',
CREATE_IDEAS = 'create_ideas',
EDIT_OWN_IDEAS = 'edit_own_ideas',
EDIT_ANY_IDEAS = 'edit_any_ideas',
DELETE_OWN_IDEAS = 'delete_own_ideas',
DELETE_ANY_IDEAS = 'delete_any_ideas',
REORDER_IDEAS = 'reorder_ideas',
ADD_COMMENTS = 'add_comments',
DELETE_OWN_COMMENTS = 'delete_own_comments',
DELETE_ANY_COMMENTS = 'delete_any_comments',
REQUEST_AI_ESTIMATE = 'request_ai_estimate',
REQUEST_AI_SPECIFICATION = 'request_ai_specification',
EDIT_SPECIFICATION = 'edit_specification',
DELETE_AI_GENERATIONS = 'delete_ai_generations',
MANAGE_TEAM = 'manage_team',
MANAGE_ROLES = 'manage_roles',
EXPORT_IDEAS = 'export_ideas',
VIEW_AUDIT_LOG = 'view_audit_log'
}
enum AuditAction {
CREATE = 'create',
UPDATE = 'update',
DELETE = 'delete',
GENERATE = 'generate',
RESTORE = 'restore'
}
enum EntityType {
IDEA = 'idea',
COMMENT = 'comment',
SPECIFICATION = 'specification',
ESTIMATE = 'estimate',
TEAM_MEMBER = 'team_member',
USER_PERMISSIONS = 'user_permissions'
}
3.6 Permissions
GET /api/permissions/me
Получение прав текущего пользователя.
Response 200:
{
userId: string;
email: string;
isAdmin: boolean;
permissions: Record<Permission, boolean>;
}
GET /api/permissions/users
Получение списка пользователей с правами (только для админа).
Response 200:
{
data: {
userId: string;
email: string;
name: string;
isAdmin: boolean;
permissions: Record<Permission, boolean>;
lastLogin: string;
}[];
}
PATCH /api/permissions/:userId
Изменение прав пользователя (только для админа).
Request Body:
Partial<Record<Permission, boolean>>
Response 200:
{
userId: string;
permissions: Record<Permission, boolean>;
}
3.7 Audit
GET /api/audit
Получение истории действий.
Query Parameters:
{
page?: number; // default: 1
limit?: number; // default: 50
userId?: string; // фильтр по пользователю
action?: AuditAction; // фильтр по действию
entityType?: EntityType; // фильтр по типу сущности
entityId?: string; // фильтр по ID сущности
from?: string; // ISO date - начало периода
to?: string; // ISO date - конец периода
}
Response 200:
{
data: {
id: string;
userId: string;
userName: string;
action: AuditAction;
entityType: EntityType;
entityId: string;
oldValue: object | null;
newValue: object | null;
timestamp: string;
}[];
meta: {
total: number;
page: number;
limit: number;
totalPages: number;
}
}
POST /api/audit/:id/restore
Восстановление сущности из аудита.
Response 201:
{
restored: true;
entity: Idea | Comment | TeamMember; // восстановленная сущность
}
GET /api/audit/settings
Получение настроек аудита (только для админа).
Response 200:
{
retentionDays: number; // срок хранения в днях
}
PATCH /api/audit/settings
Изменение настроек аудита (только для админа).
Request Body:
{
retentionDays: number; // 1-365
}
Response 200:
{
retentionDays: number;
}
3.8 Export
GET /api/export/idea/:id
Экспорт идеи в DOCX.
Response 200:
Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document
Content-Disposition: attachment; filename="idea-{title}.docx"
Binary DOCX content
3.9 WebSocket Events
События от сервера (server → client)
// Идеи
'idea:created' → { idea: Idea }
'idea:updated' → { idea: Idea }
'idea:deleted' → { ideaId: string }
'ideas:reordered' → { ids: string[] }
// Комментарии
'comment:created' → { comment: Comment, ideaId: string }
'comment:deleted' → { commentId: string, ideaId: string }
// AI
'specification:generated' → { ideaId: string, specification: string }
'estimate:generated' → { ideaId: string, estimate: Estimate }
// Присутствие
'users:online' → { users: { id: string, name: string, avatar?: string }[] }
'user:joined' → { user: { id: string, name: string } }
'user:left' → { userId: string }
// Редактирование
'idea:editing' → { ideaId: string, userId: string, userName: string }
'idea:stopEditing' → { ideaId: string, userId: string }
События от клиента (client → server)
// Присоединение
'join' → { token: string }
// Редактирование
'startEditing' → { ideaId: string }
'stopEditing' → { ideaId: string }
4. UI Prototypes
4.1 Главная страница - Список идей
┌─────────────────────────────────────────────────────────────────────────────┐
│ 🎯 Team Planner [Команда] [+ Идея] │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─ Фильтры ─────────────────────────────────────────────────────────────┐ │
│ │ [Статус ▼] [Приоритет ▼] [Модуль ▼] [Цвет ▼] [🔍 Поиск... ] │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─ Таблица идей ────────────────────────────────────────────────────────┐ │
│ │ ⋮⋮ │ Статус │ ⚡ │ Модуль │ Идея │ Для кого │ Оценка │ │
│ ├────┼───────────┼────┼──────────┼────────────────┼──────────┼─────────┤ │
│ │ ⋮⋮ │ 🟢 Новая │ 🔴 │ Frontend │ Добавить темну │ Все поль │ 3д │ │
│ │ ⋮⋮ │ 🟡 В обсу │ 🟡 │ Backend │ API кэширован │ Разработ │ 5д │ │
│ │ ⋮⋮ │ 🔵 Одобре │ 🟢 │ AI │ Автоматическа │ Менеджер │ 10д │ │
│ │ ⋮⋮ │ 🟣 В рабо │ 🔴 │ Backend │ Оптимизация з │ Все │ 7д │ │
│ │ │ │ │ │ │ │ │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ │
│ Показано 4 из 24 [< Пред] 1 2 3 [След >] │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Легенда:
⋮⋮ - drag handle для перетаскивания
⚡ - приоритет (иконка молнии)
🔴🟡🟢 - цветовые индикаторы приоритета
4.2 Расширенная строка с комментариями
┌─────────────────────────────────────────────────────────────────────────────┐
│ ⋮⋮ │ 🟢 Новая │ 🔴 │ Frontend │ Добавить тёмную тему │ Все │ 3д [▼]│
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─ Детали ──────────────────────────────────────────────────────────────┐ │
│ │ Боль: Пользователи жалуются на яркий интерфейс вечером │ │
│ │ AI роль: — │ │
│ │ Проверка: A/B тест с 10% пользователей │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─ Комментарии (3) ─────────────────────────────────────────────────────┐ │
│ │ 💬 Иван: Нужно согласовать палитру с дизайнером 2ч назад │ │
│ │ └─ 💬 Мария: Уже в процессе, будет готово завтра 1ч назад │ │
│ │ 💬 Петр: Предлагаю использовать CSS variables 30м назад │ │
│ │ │ │
│ │ [Написать комментарий... ] [Отправить] │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ │
│ [🤖 Оценить AI] [✏️ Редактировать] [🗑️ Удалить] │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
4.3 Модальное окно создания/редактирования идеи
┌─────────────────────────────────────────────────────────────────────────────┐
│ Новая идея [✕] │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Суть идеи * │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────┐ ┌─────────────────────┐ ┌──────────────────┐ │
│ │ Статус │ │ Приоритет │ │ Модуль │ │
│ │ [Новая ▼] │ │ [Средний ▼] │ │ [Frontend ▼] │ │
│ └─────────────────────┘ └─────────────────────┘ └──────────────────┘ │
│ │
│ Для кого эта идея │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ Какую боль решает │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ Роль AI (если применимо) │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ Быстрый способ проверить │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ Цвет строки │
│ [⬜][🟥][🟧][🟨][🟩][🟦][🟪] │
│ │
│ [Отмена] [💾 Сохранить] │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
4.4 Страница управления командой
┌─────────────────────────────────────────────────────────────────────────────┐
│ 🎯 Team Planner [Команда] [+ Идея] │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ◀ Назад к идеям │
│ │
│ ┌─ Состав команды ──────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 👨💻 Backend: 3 👩💻 Frontend: 2 🤖 AI/ML: 1 📊 Аналитик: 1 │ │
│ │ 🧪 QA: 2 🔧 DevOps: 1 🎨 Дизайн: 1 📋 PM: 1 │ │
│ │ │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─ Участники ───────────────────────────────────────────────────────────┐ │
│ │ Имя │ Роль │ Trivial │ Easy │ Med │ Hard │ Epic │ │
│ ├──────────────────┼────────────┼─────────┼──────┼─────┼──────┼────────┤ │
│ │ Иван Петров │ Backend │ 1ч │ 4ч │ 16ч │ 40ч │ 80ч │ │
│ │ Мария Сидорова │ Frontend │ 1ч │ 4ч │ 16ч │ 40ч │ 80ч │ │
│ │ Алексей Козлов │ AI/ML │ 2ч │ 8ч │ 24ч │ 60ч │ 120ч │ │
│ │ ... │ │ │ │ │ │ │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ │
│ [+ Добавить участника] │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
4.5 Модальное окно AI-оценки
┌─────────────────────────────────────────────────────────────────────────────┐
│ 🤖 AI-оценка трудозатрат [✕] │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Идея: Добавить тёмную тему │
│ │
│ ┌─ Общая оценка ────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ ⏱️ 24 часа (~3 рабочих дня) │ │
│ │ 📊 Сложность: Средняя │ │
│ │ │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─ Разбивка по ролям ───────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 👩💻 Frontend │ 16ч │ ████████████████░░░░ │ Средняя │ │
│ │ 🎨 Дизайнер │ 4ч │ ████░░░░░░░░░░░░░░░░ │ Лёгкая │ │
│ │ 🧪 QA │ 4ч │ ████░░░░░░░░░░░░░░░░ │ Лёгкая │ │
│ │ │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─ Рекомендации ────────────────────────────────────────────────────────┐ │
│ │ • Использовать CSS custom properties для цветовой схемы │ │
│ │ • Добавить переключатель в настройки пользователя │ │
│ │ • Учесть системные настройки (prefers-color-scheme) │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ │
│ [Сохранить оценку] │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
4.6 Модальное окно мини-ТЗ
┌─────────────────────────────────────────────────────────────────────────────┐
│ 📋 Техническое задание [✕] │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Идея: Добавить тёмную тему │
│ Сгенерировано: 15.01.2026, 12:30 │
│ │
│ ┌─ Содержание ТЗ ─────────────────────────────────────────────────────────┐│
│ │ ││
│ │ ## Цель ││
│ │ Реализовать тёмную тему оформления для снижения нагрузки на глаза ││
│ │ пользователей при работе в условиях низкой освещённости. ││
│ │ ││
│ │ ## Функциональные требования ││
│ │ - Переключатель темы в настройках пользователя ││
│ │ - Автоматическое определение системной темы ││
│ │ - Сохранение выбора пользователя ││
│ │ ││
│ │ ## Технические требования ││
│ │ - CSS custom properties для цветовой схемы ││
│ │ - Поддержка prefers-color-scheme ││
│ │ - localStorage для сохранения выбора ││
│ │ ││
│ │ ## Критерии приёмки ││
│ │ - [ ] Все элементы UI корректно отображаются в тёмной теме ││
│ │ - [ ] Переключение работает мгновенно без перезагрузки ││
│ │ - [ ] Выбор сохраняется между сессиями ││
│ │ ││
│ │ ## Зависимости и риски ││
│ │ - Необходимо согласование цветовой палитры с дизайнером ││
│ │ - Возможны проблемы с контрастностью на некоторых элементах ││
│ │ ││
│ └─────────────────────────────────────────────────────────────────────────┘│
│ │
│ [✏️ Редактировать] [Закрыть] │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Режим редактирования:
┌─────────────────────────────────────────────────────────────────────────────┐
│ 📋 Редактирование ТЗ [✕] │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Идея: Добавить тёмную тему │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────┐│
│ │ ## Цель ││
│ │ Реализовать тёмную тему оформления для снижения нагрузки... ││
│ │ ││
│ │ ## Функциональные требования ││
│ │ - Переключатель темы в настройках пользователя ││
│ │ ... ││
│ │ [textarea, multiline]││
│ └─────────────────────────────────────────────────────────────────────────┘│
│ │
│ [Отмена] [💾 Сохранить] │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Режим генерации (loading):
┌─────────────────────────────────────────────────────────────────────────────┐
│ 📋 Техническое задание [✕] │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Идея: Добавить тёмную тему │
│ │
│ │
│ Генерируем техническое задание... │
│ │
│ ═══════════════════ │
│ [progress bar] │
│ │
│ │
│ [Отмена] │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
4.7 Панель администратора — Управление правами
┌─────────────────────────────────────────────────────────────────────────────┐
│ 🎯 Team Planner [☀️/🌙] [👥 2 онлайн] [Админ] [Выход] │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ [Идеи] [Команда] [Администрирование] [История] │
│ ───────────────────── │
│ │
│ ┌─ Управление правами пользователей ──────────────────────────────────────┐│
│ │ ││
│ │ ┌─────────────────────────────────────────────────────────────────────┐││
│ │ │ Пользователь │ Послед. вход │ Идеи │ Свои │ Чужие│ AI │ ... │││
│ │ ├───────────────────┼──────────────┼──────┼──────┼──────┼─────┼─────┤││
│ │ │ 👤 Иван Петров │ 10 мин назад │ ✅ │ ✅ │ ❌ │ ✅ │ │││
│ │ │ ivan@mail.ru │ │ │ │ │ │ │││
│ │ │ 👤 Мария Сидорова │ 2 дня назад │ ✅ │ ✅ │ ✅ │ ✅ │ │││
│ │ │ maria@mail.ru │ │ │ │ │ │ │││
│ │ │ 👤 Новый юзер │ Никогда │ ✅ │ ❌ │ ❌ │ ❌ │ │││
│ │ │ new@mail.ru │ (только view)│ │ │ │ │ │││
│ │ └─────────────────────────────────────────────────────────────────────┘││
│ │ ││
│ │ Легенда колонок: ││
│ │ Идеи = create_ideas, Свои = edit_own_ideas, Чужие = edit_any_ideas ││
│ │ AI = request_ai_estimate + request_ai_specification ││
│ │ ││
│ └─────────────────────────────────────────────────────────────────────────┘│
│ │
│ ┌─ Настройки аудита ──────────────────────────────────────────────────────┐│
│ │ ││
│ │ Срок хранения истории: [30] дней [Сохранить] ││
│ │ ││
│ └─────────────────────────────────────────────────────────────────────────┘│
│ │
└─────────────────────────────────────────────────────────────────────────────┘
4.8 История действий (Аудит)
┌─────────────────────────────────────────────────────────────────────────────┐
│ 🎯 Team Planner [☀️/🌙] [👥 2 онлайн] [Иван] [Выход] │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ [Идеи] [Команда] [Администрирование] [История] │
│ ──────── │
│ │
│ ┌─ Фильтры ───────────────────────────────────────────────────────────────┐│
│ │ [Пользователь ▼] [Действие ▼] [Сущность ▼] [С: 📅] [По: 📅] [Поиск...] ││
│ └─────────────────────────────────────────────────────────────────────────┘│
│ │
│ ┌─ История действий ──────────────────────────────────────────────────────┐│
│ │ ││
│ │ ┌─────────────────────────────────────────────────────────────────────┐││
│ │ │ Дата/Время │ Пользователь │ Действие │ Сущность │ Детали │││
│ │ ├──────────────────┼──────────────┼──────────┼──────────┼───────────┤││
│ │ │ 15.01 14:30 │ Иван │ ✏️ update│ Идея │ [👁️ Diff]│││
│ │ │ 15.01 14:25 │ Мария │ ➕ create│ Коммент. │ [👁️] │││
│ │ │ 15.01 14:20 │ Иван │ 🗑️ delete│ Идея │ [🔄 Восст]││
│ │ │ 15.01 14:15 │ AI │ 🤖 gen. │ ТЗ │ [👁️] │││
│ │ │ 15.01 14:10 │ Мария │ ➕ create│ Идея │ [👁️] │││
│ │ └─────────────────────────────────────────────────────────────────────┘││
│ │ ││
│ └─────────────────────────────────────────────────────────────────────────┘│
│ │
│ Показано 5 из 128 [< Пред] 1 2 3 [След >] │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Просмотр изменений (diff):
┌─────────────────────────────────────────────────────────────────────────────┐
│ 📋 Детали изменения [✕] │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Пользователь: Иван Петров │
│ Дата: 15.01.2026, 14:30 │
│ Действие: Обновление идеи │
│ │
│ ┌─ Изменения ─────────────────────────────────────────────────────────────┐│
│ │ ││
│ │ Статус: 🟢 Новая → 🟡 В обсуждении ││
│ │ Приоритет: Средний → Высокий ││
│ │ ││
│ └─────────────────────────────────────────────────────────────────────────┘│
│ │
│ [Закрыть] │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
4.9 Главная страница с онлайн-пользователями и темой
┌─────────────────────────────────────────────────────────────────────────────┐
│ 🎯 Team Planner [☀️ Светлая / 🌙 Тёмная] [👥 3] [Иван] [⎋ Выход] │
├─────────────────────────────────────────────────────────────────────────────┤
│ ┌───────────────────┐ │
│ [Идеи] [Команда] [История] Онлайн сейчас:│ 🟢 Иван │ │
│ ─────── │ 🟢 Мария │ │
│ │ 🟢 Алексей │ │
│ ┌─ Фильтры ──────────────────────┐ └───────────────────┘ │
│ │ [Статус ▼] [Приоритет ▼] ... │ │
│ └────────────────────────────────┘ │
│ │
│ ┌─ Таблица идей ───────────────────────────────────────────────────────┐ │
│ │ ⋮⋮│Статус │⚡│Модуль │Идея │Автор │Оценка│ │ │ │
│ ├───┼─────────┼──┼───────┼──────────────┼──────┼──────┼──────┼─────────┤ │
│ │⋮⋮ │🟢 Новая │🔴│Front │Тёмная тема │Иван │3д │[📋][⬇️]│[🗑️] │ │
│ │ │ │ │ │✏️ Мария ред. │ │ │ │ │ │
│ │⋮⋮ │🟡 Обсужд│🟡│Back │API кэш │Мария │5д │[📋][⬇️]│[🗑️] │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ │
│ [+ Новая идея] │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Легенда:
📋 - Кнопка ТЗ
⬇️ - Кнопка экспорта в DOCX
✏️ Мария ред. - индикатор что Мария сейчас редактирует эту идею
5. UI Specification
5.1 Цветовая палитра
Основные цвета (Светлая тема)
Primary: #1976D2 (MUI Blue 700)
Primary Light: #42A5F5 (MUI Blue 400)
Primary Dark: #1565C0 (MUI Blue 800)
Secondary: #9C27B0 (MUI Purple 500)
Background: #FFFFFF
Surface: #F5F5F5 (Grey 100)
Text Primary: #212121 (Grey 900)
Text Secondary: #757575 (Grey 600)
Основные цвета (Тёмная тема)
Primary: #90CAF9 (MUI Blue 200)
Primary Light: #E3F2FD (MUI Blue 50)
Primary Dark: #42A5F5 (MUI Blue 400)
Secondary: #CE93D8 (MUI Purple 200)
Background: #121212
Surface: #1E1E1E
Paper: #2D2D2D
Text Primary: #FFFFFF
Text Secondary: #B0B0B0
Статусы идей
New: #4CAF50 (Green 500) 🟢
Discussing: #FF9800 (Orange 500) 🟡
Approved: #2196F3 (Blue 500) 🔵
In Progress: #9C27B0 (Purple 500) 🟣
Done: #607D8B (Blue Grey 500) ⚫
Rejected: #F44336 (Red 500) 🔴
Приоритеты
Critical: #D32F2F (Red 700) фон: #FFEBEE
High: #F57C00 (Orange 700) фон: #FFF3E0
Medium: #FBC02D (Yellow 700) фон: #FFFDE7
Low: #388E3C (Green 700) фон: #E8F5E9
Цвета для маркировки строк
Без цвета: transparent
Красный: #FFCDD2 (Red 100)
Оранжевый: #FFE0B2 (Orange 100)
Жёлтый: #FFF9C4 (Yellow 100)
Зелёный: #C8E6C9 (Green 100)
Голубой: #BBDEFB (Blue 100)
Фиолетовый: #E1BEE7 (Purple 100)
5.2 Типографика
Font Family: 'Roboto', 'Helvetica', 'Arial', sans-serif
H1: 24px, weight 500, line-height 1.2
H2: 20px, weight 500, line-height 1.3
H3: 16px, weight 500, line-height 1.4
Body1: 14px, weight 400, line-height 1.5
Body2: 12px, weight 400, line-height 1.43
Caption: 12px, weight 400, line-height 1.66, color: #757575
5.3 Компоненты
Кнопки
| Тип | Использование | Стиль |
|---|---|---|
| Primary | Главное действие (Сохранить, Создать) | Filled, Primary color |
| Secondary | Вторичные действия (Отмена) | Outlined, Grey |
| Text | Третичные действия (Назад) | Text only |
| Icon | Действия в строке таблицы | IconButton, 40x40px |
| Danger | Удаление | Filled, Red |
Border Radius: 4px
Height: 36px (medium), 40px (large)
Padding: 8px 16px
Состояния кнопок
Default: opacity 1
Hover: brightness 0.95, shadow elevation 2
Active: brightness 0.9
Disabled: opacity 0.38, cursor not-allowed
Loading: показать CircularProgress (20px), disabled
Инпуты
Height: 40px
Border: 1px solid #E0E0E0
Border Radius: 4px
Padding: 8px 12px
Focus: border-color: Primary, box-shadow: 0 0 0 2px Primary/20%
Error: border-color: #D32F2F, helper text red
Disabled: background: #F5F5F5, opacity 0.6
Таблица
Header:
Background: #FAFAFA
Font: 14px, weight 500
Height: 48px
Border: 1px solid #E0E0E0 (bottom)
Row:
Height: 52px
Border: 1px solid #E0E0E0 (bottom)
Hover: background #F5F5F5
Row (colored):
Background: соответствующий цвет из палитры маркировки
Hover: darken 5%
Cell:
Padding: 16px
Vertical align: middle
Dropdown/Select
Trigger: как Input
Menu:
Background: #FFFFFF
Shadow: 0 2px 8px rgba(0,0,0,0.15)
Border Radius: 4px
Max Height: 300px (scroll)
Item:
Height: 40px
Padding: 8px 16px
Hover: background #F5F5F5
Selected: background Primary/10%, color Primary
Модальные окна
Overlay: rgba(0, 0, 0, 0.5)
Background: #FFFFFF
Border Radius: 8px
Shadow: 0 8px 32px rgba(0,0,0,0.2)
Width: 480px (small), 640px (medium), 800px (large)
Max Height: 90vh
Padding: 24px
Header:
Font: H2
Border: 1px solid #E0E0E0 (bottom)
Padding: 24px 24px 16px
Footer:
Border: 1px solid #E0E0E0 (top)
Padding: 16px 24px
Justify: flex-end
Gap: 12px
5.4 Состояния загрузки
Skeleton Loader
Для таблицы:
- Показать 5 строк skeleton
- Анимация: shimmer effect (gradient slide)
- Background: #E0E0E0
- Highlight: #F5F5F5
Для карточек/полей:
- Прямоугольники с закруглением 4px
- Высота соответствует контенту
Spinner (CircularProgress)
Size: 24px (в кнопках), 40px (в контенте)
Color: Primary
Thickness: 3.6
Inline Loading (в ячейках таблицы)
Показать маленький spinner (16px) справа от текста
Текст затемнить (opacity 0.5)
5.5 Анимации
Duration:
Fast: 150ms (hover effects)
Normal: 250ms (transitions)
Slow: 350ms (modals, large elements)
Easing:
Standard: cubic-bezier(0.4, 0, 0.2, 1)
Enter: cubic-bezier(0.0, 0, 0.2, 1)
Exit: cubic-bezier(0.4, 0, 1, 1)
Drag & Drop:
Item lift: scale 1.02, shadow elevation 8
Drop zone: border 2px dashed Primary, background Primary/5%
5.6 Иконки
Использовать Material Icons (MUI Icons).
Размеры:
Small: 18px
Medium: 24px (default)
Large: 36px
Основные иконки:
Добавить: Add (+)
Редактировать: Edit (карандаш)
Удалить: Delete (корзина)
Drag: DragIndicator (⋮⋮)
Фильтр: FilterList
Поиск: Search (🔍)
Комментарий: ChatBubbleOutline
AI: AutoAwesome (✨) или SmartToy (🤖)
Развернуть: ExpandMore
Свернуть: ExpandLess
5.7 Отступы и сетка
Spacing unit: 8px
Margins/Paddings:
xs: 4px
sm: 8px
md: 16px
lg: 24px
xl: 32px
Container:
Max width: 1440px
Padding: 24px (desktop), 16px (tablet), 12px (mobile)
Table:
Min width: 1024px
Horizontal scroll на меньших экранах
5.8 Breakpoints
xs: 0px
sm: 600px
md: 900px
lg: 1200px
xl: 1536px
Desktop-first approach:
Основной дизайн для lg+ (1200px+)
Адаптация для md (900-1199px)
Горизонтальный скролл для sm и ниже
6. Error States
6.1 Validation Errors
Inline под полем:
Color: #D32F2F
Font: 12px
Icon: ErrorOutline (слева от текста)
Margin top: 4px
6.2 API Errors
Toast notification (Snackbar):
Position: bottom-left
Duration: 5000ms (auto-hide)
Background: #D32F2F
Color: #FFFFFF
Action: "Повторить" (если применимо)
6.3 Empty States
Центрированный блок:
Icon: 64px, Grey 400
Title: H2, Grey 700
Description: Body1, Grey 500
Action: Primary Button
Примеры:
Нет идей: "Список пуст. Создайте первую идею!"
Нет команды: "Добавьте членов команды для AI-оценки"
Нет результатов: "По вашему запросу ничего не найдено"
7. Авторизация (Keycloak)
7.1 Конфигурация Keycloak
| Параметр | Значение |
|---|---|
| URL | https://auth.vigdorov.ru |
| Realm | team-planner |
| Client ID | team-planner-frontend |
| Client Type | Public (no secret) |
| Authentication Flow | Authorization Code + PKCE |
7.2 Настройка Client в Keycloak
Client authentication: OFF (public client)
Standard flow: ON
Direct access grants: OFF
Valid redirect URIs: http://localhost:4000/*
Web origins: http://localhost:4000
7.3 Environment Variables
Backend (.env):
KEYCLOAK_REALM_URL=https://auth.vigdorov.ru/realms/team-planner
ADMIN_EMAIL=admin@vigdorov.ru # email администратора из K8s Secret
AI_PROXY_BASE_URL=http://ai-proxy:3000 # URL AI Proxy сервиса
AI_PROXY_API_KEY=... # API ключ для AI Proxy
AUDIT_RETENTION_DAYS=30 # срок хранения аудита (по умолчанию)
Frontend (.env):
VITE_KEYCLOAK_URL=https://auth.vigdorov.ru
VITE_KEYCLOAK_REALM=team-planner
VITE_KEYCLOAK_CLIENT_ID=team-planner-frontend
VITE_WS_URL=wss://team-planner.vigdorov.ru # WebSocket URL
7.4 JWT Validation (Backend)
// Валидация через JWKS (публичные ключи)
secretOrKeyProvider: passportJwtSecret({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 5,
jwksUri: `${realmUrl}/protocol/openid-connect/certs`,
})
// Проверки
issuer: KEYCLOAK_REALM_URL
algorithms: ['RS256']
7.5 Защищённые и публичные endpoints
| Endpoint | Доступ | Право / Декоратор |
|---|---|---|
GET / |
Public | @Public() |
GET /health |
Public | @Public() |
GET /api/ideas |
Protected | view_ideas |
POST /api/ideas |
Protected | create_ideas |
PATCH /api/ideas/:id |
Protected | edit_own_ideas / edit_any_ideas |
DELETE /api/ideas/:id |
Protected | delete_own_ideas / delete_any_ideas |
PATCH /api/ideas/reorder |
Protected | reorder_ideas |
POST /api/ai/estimate |
Protected | request_ai_estimate |
POST /api/ai/generate-specification |
Protected | request_ai_specification |
GET /api/permissions/me |
Protected | — (все пользователи) |
GET /api/permissions/users |
Protected | Admin only |
PATCH /api/permissions/:userId |
Protected | Admin only |
GET /api/audit |
Protected | view_audit_log |
POST /api/audit/:id/restore |
Protected | Admin only |
GET /api/export/idea/:id |
Protected | export_ideas |
| WebSocket | Protected | JWT в handshake |