989 lines
39 KiB
Markdown
989 lines
39 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>"]
|
||
end
|
||
|
||
subgraph system ["Team Planner"]
|
||
TP["🎯 Team Planner<br/><i>Приложение для управления<br/>бэклогом идей команды</i>"]
|
||
end
|
||
|
||
User -->|"Управляет идеями,<br/>командой, комментариями<br/>[HTTPS]"| 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
|
||
```
|
||
|
||
### 1.2 Level 2: Container Diagram
|
||
|
||
```mermaid
|
||
flowchart TB
|
||
User["👤 Пользователь<br/><i>Член команды разработки</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 + WebSocket"]
|
||
DB[("🗄️ Database<br/><i>PostgreSQL</i><br/><br/>Хранение идей,<br/>команды, комментариев")]
|
||
end
|
||
|
||
AI["🤖 AI Proxy Service<br/><i>LLM для оценки задач</i>"]
|
||
|
||
User -->|"Использует<br/>[HTTPS]"| SPA
|
||
SPA -->|"API запросы<br/>[REST/WebSocket]"| API
|
||
API -->|"Читает/пишет<br/>[TypeORM]"| DB
|
||
API -->|"Оценка трудозатрат<br/>[HTTPS/REST]"| AI
|
||
|
||
style SPA fill:#438dd5,color:#fff
|
||
style API fill:#438dd5,color:#fff
|
||
style DB fill:#438dd5,color:#fff
|
||
style User fill:#08427b,color:#fff
|
||
style AI fill:#999,color:#fff
|
||
```
|
||
|
||
---
|
||
|
||
## 2. Sequence Diagrams
|
||
|
||
### 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 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: Отображает таблицу
|
||
```
|
||
|
||
---
|
||
|
||
## 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[];
|
||
};
|
||
}
|
||
```
|
||
|
||
### 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'
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 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) │ │
|
||
│ └───────────────────────────────────────────────────────────────────────┘ │
|
||
│ │
|
||
│ [Сохранить оценку] │
|
||
│ │
|
||
└─────────────────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## 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)
|
||
```
|
||
|
||
#### Статусы идей
|
||
```
|
||
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-оценки"
|
||
Нет результатов: "По вашему запросу ничего не найдено"
|
||
```
|