Files
team-planner/ARCHITECTURE.md
2025-12-29 16:58:56 +03:00

39 KiB
Raw Blame History

Архитектура 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>"]
    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

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 Создание идеи

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 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: Отображает таблицу

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[];
  };
}

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'
}

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-оценки"
  Нет результатов: "По вашему запросу ничего не найдено"