Leadera — Фильтры и реорганизация списка диаграмм Ганта

📁 ~/Projects/docs-a2v/content/guides/leadera-gantt-filters.md

Фильтры и реорганизация списка диаграмм Ганта

Дата: 07.04.2026 Статус: Требования Проект: Leadera (dev — sand.a2v.space)


1. Постановка задачи

Список диаграмм Ганта должен показывать все доступные пользователю диаграммы из всех пространств (spaces), с возможностью фильтрации и поиска. Сейчас список привязан к одному пространству — нужно убрать эту зависимость и добавить удобный toolbar.


2. Изменения API

2.1. Новый endpoint списка диаграмм

Было:

GET /api/v1/spaces/{spaceId}/gantt-charts?page=1&per_page=10

Стало:

GET /api/v1/gantt-charts?page=1&per_page=10

Пользователь видит все диаграммы из всех пространств, где он является подтверждённым участником (статус accepted).

2.2. Параметры фильтрации и поиска

Параметр Тип Описание
page int Номер страницы (default: 1)
per_page int Записей на странице (default: 10)
space_id UUID (optional) Фильтр по конкретному пространству
created_by UUID (optional) Фильтр по создателю диаграммы
search string (optional) Поиск по назвению и описанию (ILIKE, case-insensitive)
sort string (optional) Сортировка: created_at, updated_at, title (default: updated_at)
order string (optional) asc / desc (default: desc)

Примеры:

GET /api/v1/gantt-charts?search=миграция
GET /api/v1/gantt-charts?space_id=0b334ded-...&created_by=a1b2c3d4-...
GET /api/v1/gantt-charts?page=2&per_page=20&sort=title&order=asc

2.3. Изменение структуры ответа

Добавить поле space в каждую диаграмму:

{
  "data": [
    {
      "id": "uuid",
      "title": "Миграция backend",
      "description": "...",
      "created_by": "uuid",
      "created_by_name": "Антон Агальцов",
      "created_at": "2026-04-01T10:00:00Z",
      "updated_at": "2026-04-07T09:00:00Z",
      "space": {
        "id": "uuid",
        "name": "IT Ландшафт",
        "color": "#c4623a"
      },
      "is_archived": false
    }
  ],
  "pagination": {
    "page": 1,
    "per_page": 10,
    "total": 42,
    "total_pages": 5
  }
}

2.4. Endpoint для списка фильтров

Вспомогательный endpoint для заполнения dropdown-фильтров:

GET /api/v1/gantt-charts/filters

Ответ:

{
  "spaces": [
    { "id": "uuid", "name": "IT Ландшафт", "color": "#c4623a" },
    { "id": "uuid", "name": "DevOps", "color": "#27ae60" }
  ],
  "creators": [
    { "id": "uuid", "display_name": "Антон Агальцов" },
    { "id": "uuid", "display_name": "Иван Петров" }
  ]
}

Логика creators: уникальные создатели всех диаграмм, доступных текущему пользователю. Получается через DISTINCT-запрос по created_by с JOIN на users.

2.5. Backend — изменения в Go

Файлы для изменения:

internal/handlers/gantt_handler.go

  • Добавить новый handler ListAllGanttCharts — без привязки к space
  • Зарегистрировать маршрут GET /gantt-charts (без /spaces/:id/)
  • Парсить query-параметры: space_id, created_by, search, sort, order
  • Старый маршрут GET /spaces/:id/gantt-charts — оставить для обратной совместимости (или удалить, если не используется)

internal/service/gantt_service.go

  • Новый метод ListAll(ctx, userId, filters) — запрашивает все spaces пользователя (через space_member с status=accepted), затем выбирает все gantt_charts из этих spaces с фильтрами

internal/repository/gantt_repository.go

  • Новый метод с динамическим SQL:
    • Базовый JOIN: gantt_chartsspacesspace_members
    • WHERE: space_members.user_id = $1 AND space_members.status = 'accepted'
    • Опциональные фильтры: AND space_id = $N, AND created_by = $N, AND (title ILIKE '%' || $N || '%' OR description ILIKE '%' || $N || '%')
    • Возвращать поле space (id, name, color) через JOIN

internal/dto/gantt/

  • Новые DTO: GanttChartListFilters, GanttChartWithSpaceResponse, GanttFiltersResponse

SQL (пример):

SELECT gc.*, s.id as space_id, s.name as space_name, s.color as space_color,
       u.display_name as created_by_name
FROM gantt_charts gc
JOIN spaces s ON s.id = gc.space_id
JOIN space_members sm ON sm.space_id = s.id
LEFT JOIN users u ON u.id = gc.created_by
WHERE sm.user_id = $1
  AND sm.status = 'accepted'
  AND ($2::uuid IS NULL OR gc.space_id = $2)
  AND ($3::uuid IS NULL OR gc.created_by = $3)
  AND ($4::text IS NULL OR gc.title ILIKE '%' || $4 || '%'
       OR gc.description ILIKE '%' || $4 || '%')
ORDER BY
  CASE WHEN $5 = 'title' THEN gc.title END,
  CASE WHEN $5 = 'created_at' THEN gc.created_at::text END,
  gc.updated_at DESC
LIMIT $6 OFFSET $7;

3. Изменения Frontend (Angular)

3.1. Toolbar фильтров

Добавить toolbar над списком диаграмм:

┌──────────────────────────────────────────────────────────────────┐
│ [🔍 Поиск...                              ] [▼ Пространство]    │
│ [▼ Создатель]  [▼ Сортировка]  [Сбросить фильтры]               │
└──────────────────────────────────────────────────────────────────┘

Компоненты:

  • Строка поиска — input с debounce (300ms), поиск по назвению и описанию
  • Dropdown «Пространство» — список из /gantt-charts/filters, с цветовой меткой
  • Dropdown «Создатель» — список уникальных создателей
  • Dropdown «Сортировка» — по дате обновления, дате создания, названию
  • Кнопка «Сбросить» — очищает все фильтры

Состояние фильтров в URL (query params):

/gantt?search=миграция&space_id=xxx&sort=updated_at

Это позволяет делиться ссылкой с уже настроенными фильтрами.

3.2. Карточка диаграммы — бейдж пространства

На каждой карточке добавить бейдж с названием пространства:

┌─────────────────────────────────────┐
│  🟠 IT Ландшафт                     │  ← бейдж с цветом пространства
│  Миграция backend                   │
│  Перевод PHP→Go, этап 2             │
│  Создал: Антон А. · 07.04.2026     │
└─────────────────────────────────────┘

3.3. Форма создания диаграммы — выбор пространства

В модальном окне создания:

  • Dropdown со списком пространств, где пользователь имеет роль editor или выше
  • Валидация: если пользователь не editor ни в одном пространстве — показать сообщение «Нет доступных пространств для создания»

Запрос доступных пространств: переиспользовать существующий GET /api/v1/spaces с фильтрацией на клиенте по role ∈ [owner, admin, editor], либо добавить параметр ?min_role=editor на backend.

3.4. Изменение routing

Было:

/gantt → список гантов текущего пространства

Стало:

/gantt → список всех доступных гантов (из всех пространств)

Убрать зависимость от SpaceStorageService.getSelectedSpaceId() в списке гантов.

3.5. Frontend — файлы для изменения

Файл Изменение
pages/gantt/services/gantt.service.ts Новый метод listAll(filters), getFilters()
pages/gantt/gantt.component.ts Подключить toolbar, парсинг URL-параметров
pages/gantt/gantt.component.html Добавить toolbar, бейджи на карточках
pages/gantt/components/gantt-card/ Добавить бейдж пространства
pages/gantt/components/create-chart-modal/ Dropdown выбора пространства
core/models/ Обновить модели: добавить space в GanttChart

4. Обратная совместимость

Endpoint Статус
GET /api/v1/spaces/{id}/gantt-charts Сохранить (для детального view внутри пространства)
GET /api/v1/gantt-charts Новый — глобальный список
GET /api/v1/gantt-charts/filters Новый — данные для dropdown’ов
POST /api/v1/spaces/{id}/gantt-charts Сохранить (создание в контексте пространства)
Все CRUD для sections/tasks Без изменений

5. Критерии приёмки

  • Endpoint GET /api/v1/gantt-charts возвращает все доступные диаграммы
  • Фильтр по space_id — показывает только диаграммы конкретного пространства
  • Фильтр по created_by — показывает только диаграммы выбранного создателя
  • Поиск search — ищет по title и description (case-insensitive)
  • Dropdown фильтров заполняется реальными данными из /filters
  • Карточка диаграммы показывает бейдж пространства с цветом
  • Форма создания предлагает только пространства с ролью ≥ editor
  • Состояние фильтров сохраняется в URL (можно поделиться ссылкой)
  • Старые endpoints (/spaces/{id}/gantt-charts) продолжают работать
  • Swagger документация обновлена

6. Порядок реализации

  1. Backend: DTO для новых запросов/ответов
  2. Backend: Repository — SQL-запрос с фильтрами + JOIN на spaces
  3. Backend: Service — ListAll, GetFilters
  4. Backend: Handler — ListAllGanttCharts, GetGanttFilters, регистрация маршрутов
  5. Backend: Swagger — обновить документацию
  6. Frontend: Модели — обновить GanttChart, добавить Space в ответ
  7. Frontend: Service — listAll(), getFilters()
  8. Frontend: Toolbar компонент — поиск + dropdown’ы
  9. Frontend: Карточка — бейдж пространства
  10. Frontend: Модальное окно создания — dropdown пространства
  11. Frontend: URL-параметры — синхронизация фильтров с router
  12. Тестирование