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 ? 'Нет задач по выбранным фильтрам' : 'Задач пока нет' }} +