Some checks reported errors
continuous-integration/drone/push Build encountered an error
2032 lines
88 KiB
Markdown
2032 lines
88 KiB
Markdown
# Архитектура Team Planner
|
||
|
||
---
|
||
|
||
## 1. C4 Model
|
||
|
||
### 1.1 Level 1: System Context
|
||
|
||
```mermaid
|
||
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
|
||
|
||
```mermaid
|
||
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
|
||
|
||
```mermaid
|
||
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
|
||
|
||
```mermaid
|
||
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)
|
||
|
||
```mermaid
|
||
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 Создание идеи
|
||
|
||
```mermaid
|
||
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-редактирование идеи
|
||
|
||
```mermaid
|
||
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 (изменение порядка)
|
||
|
||
```mermaid
|
||
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-оценка трудозатрат
|
||
|
||
```mermaid
|
||
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 Генерация мини-ТЗ
|
||
|
||
```mermaid
|
||
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 Редактирование ТЗ
|
||
|
||
```mermaid
|
||
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 Добавление комментария
|
||
|
||
```mermaid
|
||
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 Загрузка списка идей с фильтрами
|
||
|
||
```mermaid
|
||
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)
|
||
|
||
```mermaid
|
||
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 Проверка прав доступа
|
||
|
||
```mermaid
|
||
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 Изменение прав пользователя (Админ)
|
||
|
||
```mermaid
|
||
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 Просмотр и восстановление из аудита
|
||
|
||
```mermaid
|
||
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
|
||
|
||
```mermaid
|
||
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:**
|
||
```typescript
|
||
{
|
||
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:**
|
||
```typescript
|
||
{
|
||
data: Idea[];
|
||
meta: {
|
||
total: number;
|
||
page: number;
|
||
limit: number;
|
||
totalPages: number;
|
||
}
|
||
}
|
||
```
|
||
|
||
#### POST /api/ideas
|
||
Создание новой идеи.
|
||
|
||
**Request Body:**
|
||
```typescript
|
||
{
|
||
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:**
|
||
```typescript
|
||
{
|
||
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:**
|
||
```typescript
|
||
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:**
|
||
```typescript
|
||
{
|
||
ids: string[]; // ID идей в новом порядке
|
||
}
|
||
```
|
||
|
||
**Response 200:**
|
||
```typescript
|
||
{
|
||
success: true;
|
||
}
|
||
```
|
||
|
||
### 3.2 Comments
|
||
|
||
#### GET /api/ideas/:ideaId/comments
|
||
Получение комментариев к идее.
|
||
|
||
**Response 200:**
|
||
```typescript
|
||
{
|
||
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:**
|
||
```typescript
|
||
{
|
||
text: string;
|
||
parentId?: string; // для ответа на комментарий
|
||
}
|
||
```
|
||
|
||
**Response 201:** `Comment`
|
||
|
||
#### DELETE /api/comments/:id
|
||
Удаление комментария.
|
||
|
||
**Response 204:** No Content
|
||
|
||
### 3.3 Team
|
||
|
||
#### GET /api/team/members
|
||
Получение списка членов команды.
|
||
|
||
**Response 200:**
|
||
```typescript
|
||
{
|
||
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:**
|
||
```typescript
|
||
{
|
||
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:**
|
||
```typescript
|
||
{
|
||
totalMembers: number;
|
||
byRole: Record<TeamRole, number>;
|
||
}
|
||
```
|
||
|
||
### 3.4 AI
|
||
|
||
#### POST /api/ai/estimate
|
||
AI-оценка трудозатрат для идеи.
|
||
|
||
**Request Body:**
|
||
```typescript
|
||
{
|
||
ideaId: string;
|
||
}
|
||
```
|
||
|
||
**Response 200:**
|
||
```typescript
|
||
{
|
||
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:**
|
||
```typescript
|
||
{
|
||
ideaId: string;
|
||
}
|
||
```
|
||
|
||
**Response 200:**
|
||
```typescript
|
||
{
|
||
ideaId: string;
|
||
specification: string; // markdown текст ТЗ
|
||
generatedAt: string; // ISO timestamp
|
||
}
|
||
```
|
||
|
||
**Структура генерируемого ТЗ:**
|
||
```markdown
|
||
## Цель
|
||
[что должно быть достигнуто]
|
||
|
||
## Функциональные требования
|
||
- [требование 1]
|
||
- [требование 2]
|
||
...
|
||
|
||
## Технические требования
|
||
[архитектура, интеграции, ограничения]
|
||
|
||
## Критерии приёмки
|
||
- [критерий 1]
|
||
- [критерий 2]
|
||
...
|
||
|
||
## Зависимости и риски
|
||
[что может повлиять на реализацию]
|
||
```
|
||
|
||
### 3.5 Enums
|
||
|
||
```typescript
|
||
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:**
|
||
```typescript
|
||
{
|
||
userId: string;
|
||
email: string;
|
||
isAdmin: boolean;
|
||
permissions: Record<Permission, boolean>;
|
||
}
|
||
```
|
||
|
||
#### GET /api/permissions/users
|
||
Получение списка пользователей с правами (только для админа).
|
||
|
||
**Response 200:**
|
||
```typescript
|
||
{
|
||
data: {
|
||
userId: string;
|
||
email: string;
|
||
name: string;
|
||
isAdmin: boolean;
|
||
permissions: Record<Permission, boolean>;
|
||
lastLogin: string;
|
||
}[];
|
||
}
|
||
```
|
||
|
||
#### PATCH /api/permissions/:userId
|
||
Изменение прав пользователя (только для админа).
|
||
|
||
**Request Body:**
|
||
```typescript
|
||
Partial<Record<Permission, boolean>>
|
||
```
|
||
|
||
**Response 200:**
|
||
```typescript
|
||
{
|
||
userId: string;
|
||
permissions: Record<Permission, boolean>;
|
||
}
|
||
```
|
||
|
||
### 3.7 Audit
|
||
|
||
#### GET /api/audit
|
||
Получение истории действий.
|
||
|
||
**Query Parameters:**
|
||
```typescript
|
||
{
|
||
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:**
|
||
```typescript
|
||
{
|
||
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:**
|
||
```typescript
|
||
{
|
||
restored: true;
|
||
entity: Idea | Comment | TeamMember; // восстановленная сущность
|
||
}
|
||
```
|
||
|
||
#### GET /api/audit/settings
|
||
Получение настроек аудита (только для админа).
|
||
|
||
**Response 200:**
|
||
```typescript
|
||
{
|
||
retentionDays: number; // срок хранения в днях
|
||
}
|
||
```
|
||
|
||
#### PATCH /api/audit/settings
|
||
Изменение настроек аудита (только для админа).
|
||
|
||
**Request Body:**
|
||
```typescript
|
||
{
|
||
retentionDays: number; // 1-365
|
||
}
|
||
```
|
||
|
||
**Response 200:**
|
||
```typescript
|
||
{
|
||
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)
|
||
|
||
```typescript
|
||
// Идеи
|
||
'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)
|
||
|
||
```typescript
|
||
// Присоединение
|
||
'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)
|
||
|
||
```typescript
|
||
// Валидация через 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 |
|