From 6047f4097c5ef56413968327621d9976afc3a3d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20B=C3=B6ckelmann?= Date: Sat, 16 Mar 2024 11:29:55 +0100 Subject: [PATCH] Display Subtasks in TaskDetail Component --- .../core/api/controller/TaskController.java | 14 +- frontend/src/api/api/task.service.ts | 197 +++++++++++------- .../task-dashboard.component.ts | 8 +- .../task-detail-overview.component.html | 7 + .../task-detail-overview.component.ts | 8 + .../task-editor/task-editor.component.ts | 2 +- openapi.yaml | 38 +++- 7 files changed, 200 insertions(+), 74 deletions(-) diff --git a/backend/src/main/java/core/api/controller/TaskController.java b/backend/src/main/java/core/api/controller/TaskController.java index f982187..c40c660 100644 --- a/backend/src/main/java/core/api/controller/TaskController.java +++ b/backend/src/main/java/core/api/controller/TaskController.java @@ -13,6 +13,7 @@ import org.springframework.web.bind.annotation.*; import javax.validation.Valid; import java.time.LocalDate; import java.util.ArrayList; +import java.util.Collection; import java.util.List; @CrossOrigin(origins = "*", maxAge = 3600) @@ -169,7 +170,7 @@ public class TaskController { return ResponseEntity.ok(new SimpleStatusResponse("success")); } - @PutMapping("/tasks/{taskID}/createSubtask") + @PutMapping("/tasks/{taskID}/subtasks") public ResponseEntity onCreateSubTask(@PathVariable long taskID, @Valid @RequestBody TaskFieldInfo taskFieldInfo) { var taskPermissionResult = taskService.getTaskPermissions(taskID, SecurityContextHolder.getContext().getAuthentication().getName()); if(taskPermissionResult.hasIssue()) { @@ -184,4 +185,15 @@ public class TaskController { } } + + @GetMapping("/tasks/{taskID}/subtasks") + public ResponseEntity onListSubtasks(@PathVariable long taskID) { + var taskPermissionResult = taskService.getTaskPermissions(taskID, SecurityContextHolder.getContext().getAuthentication().getName()); + if(taskPermissionResult.hasIssue()) { + return taskPermissionResult.mapToResponseEntity(); + } + + Collection subtasks = taskPermissionResult.getResult().getSubtasks(); + return ResponseEntity.ok(subtasks.stream().map(TaskEntityInfo::new).toList()); + } } diff --git a/frontend/src/api/api/task.service.ts b/frontend/src/api/api/task.service.ts index 2f417ef..c15e918 100644 --- a/frontend/src/api/api/task.service.ts +++ b/frontend/src/api/api/task.service.ts @@ -153,76 +153,6 @@ export class TaskService { ); } - /** - * Creates Subtask - * Create Subtask - * @param taskID internal id of task - * @param taskFieldInfo - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public tasksTaskIDCreateSubtaskPut(taskID: number, taskFieldInfo?: TaskFieldInfo, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable; - public tasksTaskIDCreateSubtaskPut(taskID: number, taskFieldInfo?: TaskFieldInfo, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable>; - public tasksTaskIDCreateSubtaskPut(taskID: number, taskFieldInfo?: TaskFieldInfo, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable>; - public tasksTaskIDCreateSubtaskPut(taskID: number, taskFieldInfo?: TaskFieldInfo, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable { - if (taskID === null || taskID === undefined) { - throw new Error('Required parameter taskID was null or undefined when calling tasksTaskIDCreateSubtaskPut.'); - } - - let localVarHeaders = this.defaultHeaders; - - let localVarCredential: string | undefined; - // authentication (API_TOKEN) required - localVarCredential = this.configuration.lookupCredential('API_TOKEN'); - if (localVarCredential) { - localVarHeaders = localVarHeaders.set('Authorization', 'Bearer ' + localVarCredential); - } - - let localVarHttpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (localVarHttpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (localVarHttpHeaderAcceptSelected !== undefined) { - localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected); - } - - let localVarHttpContext: HttpContext | undefined = options && options.context; - if (localVarHttpContext === undefined) { - localVarHttpContext = new HttpContext(); - } - - - // to determine the Content-Type header - const consumes: string[] = [ - 'application/json' - ]; - const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes); - if (httpContentTypeSelected !== undefined) { - localVarHeaders = localVarHeaders.set('Content-Type', httpContentTypeSelected); - } - - let responseType_: 'text' | 'json' = 'json'; - if(localVarHttpHeaderAcceptSelected && localVarHttpHeaderAcceptSelected.startsWith('text')) { - responseType_ = 'text'; - } - - return this.httpClient.put(`${this.configuration.basePath}/tasks/${encodeURIComponent(String(taskID))}/createSubtask`, - taskFieldInfo, - { - context: localVarHttpContext, - responseType: responseType_, - withCredentials: this.configuration.withCredentials, - headers: localVarHeaders, - observe: observe, - reportProgress: reportProgress - } - ); - } - /** * edits an existing task * edits an existing task @@ -471,6 +401,133 @@ export class TaskService { ); } + /** + * @param taskID internal id of task + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public tasksTaskIDSubtasksGet(taskID: number, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable>; + public tasksTaskIDSubtasksGet(taskID: number, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable>>; + public tasksTaskIDSubtasksGet(taskID: number, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable>>; + public tasksTaskIDSubtasksGet(taskID: number, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable { + if (taskID === null || taskID === undefined) { + throw new Error('Required parameter taskID was null or undefined when calling tasksTaskIDSubtasksGet.'); + } + + let localVarHeaders = this.defaultHeaders; + + let localVarCredential: string | undefined; + // authentication (API_TOKEN) required + localVarCredential = this.configuration.lookupCredential('API_TOKEN'); + if (localVarCredential) { + localVarHeaders = localVarHeaders.set('Authorization', 'Bearer ' + localVarCredential); + } + + let localVarHttpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (localVarHttpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } + if (localVarHttpHeaderAcceptSelected !== undefined) { + localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected); + } + + let localVarHttpContext: HttpContext | undefined = options && options.context; + if (localVarHttpContext === undefined) { + localVarHttpContext = new HttpContext(); + } + + + let responseType_: 'text' | 'json' = 'json'; + if(localVarHttpHeaderAcceptSelected && localVarHttpHeaderAcceptSelected.startsWith('text')) { + responseType_ = 'text'; + } + + return this.httpClient.get>(`${this.configuration.basePath}/tasks/${encodeURIComponent(String(taskID))}/subtasks`, + { + context: localVarHttpContext, + responseType: responseType_, + withCredentials: this.configuration.withCredentials, + headers: localVarHeaders, + observe: observe, + reportProgress: reportProgress + } + ); + } + + /** + * Creates Subtask + * Create Subtask + * @param taskID internal id of task + * @param taskFieldInfo + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public tasksTaskIDSubtasksPut(taskID: number, taskFieldInfo?: TaskFieldInfo, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable; + public tasksTaskIDSubtasksPut(taskID: number, taskFieldInfo?: TaskFieldInfo, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable>; + public tasksTaskIDSubtasksPut(taskID: number, taskFieldInfo?: TaskFieldInfo, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable>; + public tasksTaskIDSubtasksPut(taskID: number, taskFieldInfo?: TaskFieldInfo, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable { + if (taskID === null || taskID === undefined) { + throw new Error('Required parameter taskID was null or undefined when calling tasksTaskIDSubtasksPut.'); + } + + let localVarHeaders = this.defaultHeaders; + + let localVarCredential: string | undefined; + // authentication (API_TOKEN) required + localVarCredential = this.configuration.lookupCredential('API_TOKEN'); + if (localVarCredential) { + localVarHeaders = localVarHeaders.set('Authorization', 'Bearer ' + localVarCredential); + } + + let localVarHttpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (localVarHttpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } + if (localVarHttpHeaderAcceptSelected !== undefined) { + localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected); + } + + let localVarHttpContext: HttpContext | undefined = options && options.context; + if (localVarHttpContext === undefined) { + localVarHttpContext = new HttpContext(); + } + + + // to determine the Content-Type header + const consumes: string[] = [ + 'application/json' + ]; + const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes); + if (httpContentTypeSelected !== undefined) { + localVarHeaders = localVarHeaders.set('Content-Type', httpContentTypeSelected); + } + + let responseType_: 'text' | 'json' = 'json'; + if(localVarHttpHeaderAcceptSelected && localVarHttpHeaderAcceptSelected.startsWith('text')) { + responseType_ = 'text'; + } + + return this.httpClient.put(`${this.configuration.basePath}/tasks/${encodeURIComponent(String(taskID))}/subtasks`, + taskFieldInfo, + { + context: localVarHttpContext, + responseType: responseType_, + withCredentials: this.configuration.withCredentials, + headers: localVarHeaders, + observe: observe, + reportProgress: reportProgress + } + ); + } + /** * creates a new task * creates tasks diff --git a/frontend/src/app/tasks/task-dashboard/task-dashboard.component.ts b/frontend/src/app/tasks/task-dashboard/task-dashboard.component.ts index 21e8949..8737120 100644 --- a/frontend/src/app/tasks/task-dashboard/task-dashboard.component.ts +++ b/frontend/src/app/tasks/task-dashboard/task-dashboard.component.ts @@ -1,4 +1,4 @@ -import {Component, Input, OnChanges, ViewChild} from '@angular/core'; +import {Component, Input, OnChanges, OnInit, ViewChild} from '@angular/core'; import {TaskEntityInfo, TaskService} from "../../../api"; import {MatPaginator} from "@angular/material/paginator"; import {MatSort} from "@angular/material/sort"; @@ -22,10 +22,15 @@ export class TaskDashboardComponent implements OnChanges{ ngOnChanges(): void { if(this.taskgroupID != undefined) { this.fetchTasks() + } else if(this.subTasks.length > 0) { + this.datasource.data = this.subTasks; + this.datasource.paginator = this.paginator!; + this.datasource.sort = this.sort!; } } @Input("taskgroupID") taskgroupID: number | undefined + @Input("subTasks") subTasks: TaskEntityInfo[] = [] @ViewChild(MatPaginator) paginator: MatPaginator | undefined @ViewChild(MatSort) sort: MatSort | undefined @@ -147,5 +152,6 @@ export class TaskDashboardComponent implements OnChanges{ resp.forEach(task => console.log(task)) } }) + } } diff --git a/frontend/src/app/tasks/task-detail-overview/task-detail-overview.component.html b/frontend/src/app/tasks/task-detail-overview/task-detail-overview.component.html index 90577ea..92b3840 100644 --- a/frontend/src/app/tasks/task-detail-overview/task-detail-overview.component.html +++ b/frontend/src/app/tasks/task-detail-overview/task-detail-overview.component.html @@ -42,6 +42,13 @@ + + Subtasks +
+ +
+
+ Schedules diff --git a/frontend/src/app/tasks/task-detail-overview/task-detail-overview.component.ts b/frontend/src/app/tasks/task-detail-overview/task-detail-overview.component.ts index 66259bd..369634e 100644 --- a/frontend/src/app/tasks/task-detail-overview/task-detail-overview.component.ts +++ b/frontend/src/app/tasks/task-detail-overview/task-detail-overview.component.ts @@ -50,6 +50,8 @@ export class TaskDetailOverviewComponent implements OnInit { currentProgress: string = "0"; futureProgress: string = "0"; + subTasks: TaskEntityInfo[] = []; + constructor(private activatedRoute: ActivatedRoute, private taskgroupService: TaskgroupService, private taskService: TaskService, @@ -100,6 +102,12 @@ export class TaskDetailOverviewComponent implements OnInit { this.calcProgress(); } }) + + this.taskService.tasksTaskIDSubtasksGet(Number(params.get('taskID'))).subscribe({ + next: resp => { + this.subTasks = resp; + } + }) } }); } diff --git a/frontend/src/app/tasks/task-editor/task-editor.component.ts b/frontend/src/app/tasks/task-editor/task-editor.component.ts index 5eacda2..ff12d58 100644 --- a/frontend/src/app/tasks/task-editor/task-editor.component.ts +++ b/frontend/src/app/tasks/task-editor/task-editor.component.ts @@ -90,7 +90,7 @@ export class TaskEditorComponent implements OnInit { } createSubTask(startDate_formatted: string|undefined, endDate_formatted: string|undefined) { - this.taskService.tasksTaskIDCreateSubtaskPut(this.editorData.parentTask!.taskID, { + this.taskService.tasksTaskIDSubtasksPut(this.editorData.parentTask!.taskID, { taskName: this.nameCtrl.value, eta: this.etaCtrl.value, startDate: startDate_formatted, diff --git a/openapi.yaml b/openapi.yaml index c3eb30e..23db524 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -1269,7 +1269,7 @@ paths: schema: type: object $ref: "#/components/schemas/SimpleStatusResponse" - /tasks/{taskID}/createSubtask: + /tasks/{taskID}/subtasks: put: security: - API_TOKEN: [] @@ -1315,6 +1315,42 @@ paths: application/json: schema: $ref: '#/components/schemas/SimpleStatusResponse' + get: + security: + - API_TOKEN: [] + tags: + - task + parameters: + - name: taskID + in: path + description: internal id of task + required: true + schema: + type: number + example: 1 + responses: + 200: + description: Operation successfull + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/TaskEntityInfo' + 403: + description: No permission + content: + application/json: + schema: + $ref: '#/components/schemas/SimpleStatusResponse' + 404: + description: Task not found + content: + application/json: + schema: + $ref: '#/components/schemas/SimpleStatusResponse' + + /schedules: get: