issue-106 #107
| @ -164,4 +164,20 @@ public class TaskController { | |||||||
|         taskService.finishTask(taskPermissionResult.getResult()); |         taskService.finishTask(taskPermissionResult.getResult()); | ||||||
|         return ResponseEntity.ok(new SimpleStatusResponse("success")); |         return ResponseEntity.ok(new SimpleStatusResponse("success")); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     @PutMapping("/tasks/{taskID}/createSubtask") | ||||||
|  |     public ResponseEntity<?> onCreateSubTask(@PathVariable long taskID, @Valid @RequestBody TaskFieldInfo taskFieldInfo) { | ||||||
|  |         var taskPermissionResult = taskService.getTaskPermissions(taskID, SecurityContextHolder.getContext().getAuthentication().getName()); | ||||||
|  |         if(taskPermissionResult.hasIssue()) { | ||||||
|  |             return taskPermissionResult.mapToResponseEntity(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         var serviceResult = taskService.createSubTask(taskPermissionResult.getResult(), taskFieldInfo); | ||||||
|  |         if(serviceResult.hasIssue()) { | ||||||
|  |             return serviceResult.mapToResponseEntity(); | ||||||
|  |         } else { | ||||||
|  |             return ResponseEntity.ok(new TaskEntityInfo(serviceResult.getResult())); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -33,7 +33,7 @@ public class Task { | |||||||
|     @OneToOne(mappedBy = "task", cascade = CascadeType.ALL, orphanRemoval = true) |     @OneToOne(mappedBy = "task", cascade = CascadeType.ALL, orphanRemoval = true) | ||||||
|     private TaskSerieItem taskSerieItem; |     private TaskSerieItem taskSerieItem; | ||||||
| 
 | 
 | ||||||
|     @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true) |     @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER) | ||||||
|     private Set<Task> subtasks; |     private Set<Task> subtasks; | ||||||
| 
 | 
 | ||||||
|     @ManyToOne |     @ManyToOne | ||||||
|  | |||||||
| @ -153,6 +153,76 @@ 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<TaskEntityInfo>; | ||||||
|  |     public tasksTaskIDCreateSubtaskPut(taskID: number, taskFieldInfo?: TaskFieldInfo, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<HttpResponse<TaskEntityInfo>>; | ||||||
|  |     public tasksTaskIDCreateSubtaskPut(taskID: number, taskFieldInfo?: TaskFieldInfo, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<HttpEvent<TaskEntityInfo>>; | ||||||
|  |     public tasksTaskIDCreateSubtaskPut(taskID: number, taskFieldInfo?: TaskFieldInfo, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<any> { | ||||||
|  |         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<TaskEntityInfo>(`${this.configuration.basePath}/tasks/${encodeURIComponent(String(taskID))}/createSubtask`, | ||||||
|  |             taskFieldInfo, | ||||||
|  |             { | ||||||
|  |                 context: localVarHttpContext, | ||||||
|  |                 responseType: <any>responseType_, | ||||||
|  |                 withCredentials: this.configuration.withCredentials, | ||||||
|  |                 headers: localVarHeaders, | ||||||
|  |                 observe: observe, | ||||||
|  |                 reportProgress: reportProgress | ||||||
|  |             } | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * edits an existing task |      * edits an existing task | ||||||
|      * edits an existing task |      * edits an existing task | ||||||
|  | |||||||
| @ -93,7 +93,8 @@ export class ActiveTaskOverviewComponent implements OnInit{ | |||||||
|   editTask(editedTask: TaskTaskgroupInfo) { |   editTask(editedTask: TaskTaskgroupInfo) { | ||||||
|     const taskEditorInfo: TaskEditorData = { |     const taskEditorInfo: TaskEditorData = { | ||||||
|       task: editedTask, |       task: editedTask, | ||||||
|       taskgroupID: editedTask.taskgroups[editedTask.taskgroups.length-1].taskgroupID |       taskgroupID: editedTask.taskgroups[editedTask.taskgroups.length-1].taskgroupID, | ||||||
|  |       parentTask: undefined | ||||||
|     }; |     }; | ||||||
|     this.dialog.open(TaskEditorComponent, {data: taskEditorInfo, width: "600px"}) |     this.dialog.open(TaskEditorComponent, {data: taskEditorInfo, width: "600px"}) | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -75,7 +75,8 @@ export class TaskOverviewComponent { | |||||||
|   openTaskCreation() { |   openTaskCreation() { | ||||||
|     const editorData: TaskEditorData = { |     const editorData: TaskEditorData = { | ||||||
|       task: undefined, |       task: undefined, | ||||||
|       taskgroupID: this.taskgroupID! |       taskgroupID: this.taskgroupID!, | ||||||
|  |       parentTask: undefined | ||||||
|     } |     } | ||||||
|     const dialogRef = this.dialog.open(TaskEditorComponent, {data: editorData, width: "600px"}) |     const dialogRef = this.dialog.open(TaskEditorComponent, {data: editorData, width: "600px"}) | ||||||
|     dialogRef.afterClosed().subscribe(res => { |     dialogRef.afterClosed().subscribe(res => { | ||||||
|  | |||||||
| @ -104,7 +104,8 @@ export class TaskgroupDashboardComponent implements OnInit { | |||||||
|   openTaskCreation() { |   openTaskCreation() { | ||||||
|     const editorData: TaskEditorData = { |     const editorData: TaskEditorData = { | ||||||
|       task: undefined, |       task: undefined, | ||||||
|       taskgroupID: this.taskgroupID |       taskgroupID: this.taskgroupID, | ||||||
|  |       parentTask: undefined | ||||||
|     } |     } | ||||||
|     const dialogRef = this.dialog.open(TaskEditorComponent, {data: editorData, width: "600px"}) |     const dialogRef = this.dialog.open(TaskEditorComponent, {data: editorData, width: "600px"}) | ||||||
|     dialogRef.afterClosed().subscribe(res => { |     dialogRef.afterClosed().subscribe(res => { | ||||||
|  | |||||||
| @ -97,7 +97,8 @@ export class TaskDashboardComponent implements OnChanges{ | |||||||
|   editTask(task: TaskEntityInfo) { |   editTask(task: TaskEntityInfo) { | ||||||
|     const taskEditorInfo: TaskEditorData = { |     const taskEditorInfo: TaskEditorData = { | ||||||
|       task: task, |       task: task, | ||||||
|       taskgroupID: this.taskgroupID! |       taskgroupID: this.taskgroupID!, | ||||||
|  |       parentTask: undefined | ||||||
|     }; |     }; | ||||||
|     const dialogRef = this.dialog.open(TaskEditorComponent, {data: taskEditorInfo, minWidth: "400px"}) |     const dialogRef = this.dialog.open(TaskEditorComponent, {data: taskEditorInfo, minWidth: "400px"}) | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -7,7 +7,7 @@ | |||||||
|           <div class="left"> |           <div class="left"> | ||||||
|             <div>{{taskStatus + " " + task!.taskName}}</div> |             <div>{{taskStatus + " " + task!.taskName}}</div> | ||||||
|           </div> |           </div> | ||||||
|           <button class="right lightBlueBtn" mat-raised-button style="margin-left: auto">Add Subtask</button> |           <button class="right lightBlueBtn" mat-raised-button style="margin-left: auto" (click)="addSubtask()">Add Subtask</button> | ||||||
|         </div> |         </div> | ||||||
|       </mat-card-title> |       </mat-card-title> | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -135,7 +135,8 @@ export class TaskDetailOverviewComponent implements OnInit { | |||||||
|     if(this.task != undefined) { |     if(this.task != undefined) { | ||||||
|       const taskEditorInfo: TaskEditorData = { |       const taskEditorInfo: TaskEditorData = { | ||||||
|         task: this.task!, |         task: this.task!, | ||||||
|         taskgroupID: this.taskgroupID! |         taskgroupID: this.taskgroupID!, | ||||||
|  |         parentTask: undefined | ||||||
|       }; |       }; | ||||||
|       this.dialog.open(TaskEditorComponent, {data: taskEditorInfo, width: "600px"}) |       this.dialog.open(TaskEditorComponent, {data: taskEditorInfo, width: "600px"}) | ||||||
|     } |     } | ||||||
| @ -166,4 +167,16 @@ export class TaskDetailOverviewComponent implements OnInit { | |||||||
|       minWidth: "400px" |       minWidth: "400px" | ||||||
|     }) |     }) | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   addSubtask() { | ||||||
|  |     const editorData: TaskEditorData = { | ||||||
|  |       task: undefined, | ||||||
|  |       taskgroupID: this.taskgroupID, | ||||||
|  |       parentTask: this.task | ||||||
|  |     } | ||||||
|  |     const dialogRef = this.dialog.open(TaskEditorComponent, { | ||||||
|  |       data: editorData, | ||||||
|  |       minWidth: "400px" | ||||||
|  |     }) | ||||||
|  |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -3,4 +3,5 @@ import {TaskEntityInfo, TaskTaskgroupInfo} from "../../../api"; | |||||||
| export interface TaskEditorData { | export interface TaskEditorData { | ||||||
|   taskgroupID: number; |   taskgroupID: number; | ||||||
|   task: TaskTaskgroupInfo | TaskEntityInfo | undefined |   task: TaskTaskgroupInfo | TaskEntityInfo | undefined | ||||||
|  |   parentTask: TaskEntityInfo | undefined | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| <h1 mat-dialog-title *ngIf="editorData.task != undefined">Edit Task ({{editorData.task!.taskName}})</h1> | <h1 mat-dialog-title *ngIf="editorData.task != undefined">Edit Task ({{editorData.task!.taskName}})</h1> | ||||||
| <h1 mat-dialog-title *ngIf="editorData.task == undefined">Create New Task</h1> | <h1 mat-dialog-title *ngIf="editorData.task == undefined">Create New {{editorData.parentTask != undefined? 'Sub-':''}}Task</h1> | ||||||
| <div mat-dialog-content> | <div mat-dialog-content> | ||||||
|  |   <p *ngIf="editorData.parentTask != undefined">Create a new Subtask for the Task <i>{{editorData.parentTask!.taskName}}</i></p> | ||||||
|   <mat-form-field appearance="outline" class="long-form"> |   <mat-form-field appearance="outline" class="long-form"> | ||||||
|     <mat-label>Name</mat-label> |     <mat-label>Name</mat-label> | ||||||
|     <input matInput [formControl]="nameCtrl"> |     <input matInput [formControl]="nameCtrl"> | ||||||
|  | |||||||
| @ -61,6 +61,9 @@ export class TaskEditorComponent implements OnInit { | |||||||
|       startDate_formatted = moment(this.startDate.value).format('YYYY-MM-DDTHH:mm:ss.SSSZ'); |       startDate_formatted = moment(this.startDate.value).format('YYYY-MM-DDTHH:mm:ss.SSSZ'); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     if(this.editorData.parentTask != undefined) { | ||||||
|  |       this.createSubTask(startDate_formatted, endDate_formatted); | ||||||
|  |     } else { | ||||||
|       this.taskService.tasksTaskgroupIDPut(this.editorData.taskgroupID, { |       this.taskService.tasksTaskgroupIDPut(this.editorData.taskgroupID, { | ||||||
|         taskName: this.nameCtrl.value, |         taskName: this.nameCtrl.value, | ||||||
|         eta: this.etaCtrl.value, |         eta: this.etaCtrl.value, | ||||||
| @ -84,6 +87,34 @@ export class TaskEditorComponent implements OnInit { | |||||||
|         } |         } | ||||||
|       }) |       }) | ||||||
|     } |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   createSubTask(startDate_formatted: string|undefined, endDate_formatted: string|undefined) { | ||||||
|  |     this.taskService.tasksTaskIDCreateSubtaskPut(this.editorData.parentTask!.taskID, { | ||||||
|  |       taskName: this.nameCtrl.value, | ||||||
|  |       eta: this.etaCtrl.value, | ||||||
|  |       startDate: startDate_formatted, | ||||||
|  |       deadline: endDate_formatted, | ||||||
|  |       finishable: this.finishable, | ||||||
|  |     }).subscribe({ | ||||||
|  |       next: resp => { | ||||||
|  |         this.dialog.close(resp); | ||||||
|  |       }, | ||||||
|  |       error: err => { | ||||||
|  |         if(err.status == 403) { | ||||||
|  |           this.snackbar.open("No permission", "", {duration: 2000}); | ||||||
|  |         } else if(err.status == 404) { | ||||||
|  |           this.snackbar.open("Taskgroup not found", "", {duration: 2000}); | ||||||
|  |         } else if(err.status == 409) { | ||||||
|  |           this.snackbar.open("Task already exists", "", {duration: 2000}); | ||||||
|  |         } else if(err.status == 400) { | ||||||
|  |           this.snackbar.open("Invalid Dates", "", {duration: 2000}) | ||||||
|  |         } else { | ||||||
|  |           this.snackbar.open("Unexpected error", "", {duration: 3000}); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }) | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   editTask() { |   editTask() { | ||||||
|     let endDate_formatted: string|undefined = undefined; |     let endDate_formatted: string|undefined = undefined; | ||||||
| @ -123,4 +154,6 @@ export class TaskEditorComponent implements OnInit { | |||||||
|       } |       } | ||||||
|     }) |     }) | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   protected readonly parent = parent; | ||||||
| } | } | ||||||
|  | |||||||
| @ -59,7 +59,8 @@ export class UpcomingTaskOverviewComponent implements OnInit{ | |||||||
|   editTask(editedTask: TaskTaskgroupInfo) { |   editTask(editedTask: TaskTaskgroupInfo) { | ||||||
|     const taskEditorInfo: TaskEditorData = { |     const taskEditorInfo: TaskEditorData = { | ||||||
|       task: editedTask, |       task: editedTask, | ||||||
|       taskgroupID: editedTask.taskgroups[editedTask.taskgroups.length-1].taskgroupID |       taskgroupID: editedTask.taskgroups[editedTask.taskgroups.length-1].taskgroupID, | ||||||
|  |       parentTask: undefined | ||||||
|     }; |     }; | ||||||
|     this.dialog.open(TaskEditorComponent, {data: taskEditorInfo, width: "600px"}) |     this.dialog.open(TaskEditorComponent, {data: taskEditorInfo, width: "600px"}) | ||||||
|   } |   } | ||||||
|  | |||||||
							
								
								
									
										47
									
								
								openapi.yaml
									
									
									
									
									
								
							
							
						
						
									
										47
									
								
								openapi.yaml
									
									
									
									
									
								
							| @ -1269,6 +1269,53 @@ paths: | |||||||
|               schema: |               schema: | ||||||
|                 type: object |                 type: object | ||||||
|                 $ref: "#/components/schemas/SimpleStatusResponse" |                 $ref: "#/components/schemas/SimpleStatusResponse" | ||||||
|  |   /tasks/{taskID}/createSubtask: | ||||||
|  |     put: | ||||||
|  |       security: | ||||||
|  |         - API_TOKEN: [] | ||||||
|  |       tags: | ||||||
|  |         - task | ||||||
|  |       description: Create Subtask | ||||||
|  |       summary: Creates Subtask | ||||||
|  |       parameters: | ||||||
|  |         - name: taskID | ||||||
|  |           in: path | ||||||
|  |           description: internal id of task | ||||||
|  |           required: true | ||||||
|  |           schema: | ||||||
|  |             type: number | ||||||
|  |             example: 1 | ||||||
|  |       requestBody: | ||||||
|  |         content: | ||||||
|  |           application/json: | ||||||
|  |             schema: | ||||||
|  |               $ref: '#/components/schemas/TaskFieldInfo' | ||||||
|  |       responses: | ||||||
|  |         200: | ||||||
|  |           description: Operation successfull | ||||||
|  |           content: | ||||||
|  |             application/json: | ||||||
|  |               schema: | ||||||
|  |                 $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' | ||||||
|  |         400: | ||||||
|  |           description: Invalid start/end date | ||||||
|  |           content: | ||||||
|  |             application/json: | ||||||
|  |               schema: | ||||||
|  |                 $ref: '#/components/schemas/SimpleStatusResponse' | ||||||
|  |        | ||||||
|   /schedules: |   /schedules: | ||||||
|     get: |     get: | ||||||
|       security: |       security: | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user