From c5e68efb5ddd945ca3078bf9a0d34816c09e60a9 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 7 Jul 2026 11:46:06 +0400 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB=20?= =?UTF-8?q?=D1=84=D0=B8=D0=BB=D1=82=D1=80=D1=8B=20=D0=BD=D0=B0=20=D1=81?= =?UTF-8?q?=D1=82=D1=80=D0=B0=D0=BD=D0=B8=D1=86=D1=83=20=D1=81=20LTD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/api.ts | 27 ++- frontend/src/components/MultiSelectFilter.vue | 215 ++++++++++++++++++ frontend/src/types.ts | 12 + frontend/src/utils/teamLtdFilters.ts | 64 ++++++ frontend/src/views/TeamLtdView.vue | 187 ++++++++++++++- frontend/tsconfig.tsbuildinfo | 2 +- public/index.php | 42 +++- src/Features/Reports/ReportsApi.php | 179 ++++++++++++--- 8 files changed, 681 insertions(+), 47 deletions(-) create mode 100644 frontend/src/components/MultiSelectFilter.vue create mode 100644 frontend/src/utils/teamLtdFilters.ts diff --git a/frontend/src/api.ts b/frontend/src/api.ts index 40882c1..68b5e4c 100644 --- a/frontend/src/api.ts +++ b/frontend/src/api.ts @@ -1,4 +1,12 @@ -import type { AppConfig, PaginatedTeamTasks, Sprint, SprintTask, Team } from './types' +import type { + AppConfig, + PaginatedTeamTasks, + Sprint, + SprintTask, + Team, + TeamTaskFilterOptions, + TeamTaskFilterParams, +} from './types' async function request(url: string): Promise { const response = await fetch(url) @@ -36,11 +44,28 @@ export function fetchTeamTasks( teamId: number, page = 1, perPage = 50, + filters: TeamTaskFilterParams = {}, ): Promise { const params = new URLSearchParams({ page: String(page), per_page: String(perPage), }) + for (const taskType of filters.taskTypes ?? []) { + params.append('task_type', taskType) + } + + for (const assignee of filters.assignees ?? []) { + params.append('assignee', assignee) + } + + for (const sprintId of filters.sprintIds ?? []) { + params.append('sprint_id', String(sprintId)) + } + return request(`/api/teams/${teamId}/tasks?${params}`) } + +export function fetchTeamTaskFilters(teamId: number): Promise { + return request(`/api/teams/${teamId}/tasks/filters`) +} diff --git a/frontend/src/components/MultiSelectFilter.vue b/frontend/src/components/MultiSelectFilter.vue new file mode 100644 index 0000000..4f40c0f --- /dev/null +++ b/frontend/src/components/MultiSelectFilter.vue @@ -0,0 +1,215 @@ + + + + + diff --git a/frontend/src/types.ts b/frontend/src/types.ts index ff8fb31..578e7b5 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -68,3 +68,15 @@ export interface PaginatedTeamTasks { total: number total_pages: number } + +export interface TeamTaskFilterOptions { + task_types: string[] + assignees: Array<{ key: string; label: string }> + sprints: Array<{ id: number; name: string }> +} + +export interface TeamTaskFilterParams { + taskTypes?: string[] + assignees?: string[] + sprintIds?: number[] +} diff --git a/frontend/src/utils/teamLtdFilters.ts b/frontend/src/utils/teamLtdFilters.ts new file mode 100644 index 0000000..c801d59 --- /dev/null +++ b/frontend/src/utils/teamLtdFilters.ts @@ -0,0 +1,64 @@ +export interface SelectOption { + value: string + label: string +} + +export interface TeamTaskFilters { + taskTypes: string[] + assignees: string[] + sprintIds: string[] +} + +export function emptyTeamTaskFilters(): TeamTaskFilters { + return { + taskTypes: [], + assignees: [], + sprintIds: [], + } +} + +export function hasActiveTeamTaskFilters(filters: TeamTaskFilters): boolean { + return filters.taskTypes.length > 0 || filters.assignees.length > 0 || filters.sprintIds.length > 0 +} + +export function parseTeamTaskFiltersFromQuery(query: Record): TeamTaskFilters { + return { + taskTypes: parseStringList(query.task_type), + assignees: parseStringList(query.assignee), + sprintIds: parseStringList(query.sprint_id), + } +} + +export function buildTeamTaskQuery(page: number, filters: TeamTaskFilters): Record { + const query: Record = {} + + if (page > 1) { + query.page = String(page) + } + + if (filters.taskTypes.length > 0) { + query.task_type = filters.taskTypes + } + + if (filters.assignees.length > 0) { + query.assignee = filters.assignees + } + + if (filters.sprintIds.length > 0) { + query.sprint_id = filters.sprintIds + } + + return query +} + +function parseStringList(value: unknown): string[] { + if (value === null || value === undefined || value === '') { + return [] + } + + const items = Array.isArray(value) ? value : [value] + + return items + .map((item) => String(item)) + .filter((item) => item !== '') +} diff --git a/frontend/src/views/TeamLtdView.vue b/frontend/src/views/TeamLtdView.vue index ef973ce..9178d62 100644 --- a/frontend/src/views/TeamLtdView.vue +++ b/frontend/src/views/TeamLtdView.vue @@ -1,10 +1,19 @@ @@ -78,9 +184,44 @@ watch(

Все задачи команды

+
+ + + + +
+

{{ filterOptionsError }}

+
Загрузка...
{{ error }}
-
Задач пока нет
+
+ {{ hasFilters ? 'Нет задач по выбранным фильтрам' : 'Задач пока нет' }} +