--- title: "Leadera — Фильтры и реорганизация списка диаграмм Ганта" date: 2026-04-07 lastmod: 2026-04-07 tags: ["leadera", "gantt", "api", "frontend", "requirements"] weight: 120 --- # Фильтры и реорганизация списка диаграмм Ганта **Дата:** 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` в каждую диаграмму: ```json { "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 ``` **Ответ:** ```json { "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_charts` → `spaces` → `space_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 (пример):** ```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. Тестирование