issue-25 #27
@ -5,10 +5,10 @@
 | 
				
			|||||||
  </component>
 | 
					  </component>
 | 
				
			||||||
  <component name="ChangeListManager">
 | 
					  <component name="ChangeListManager">
 | 
				
			||||||
    <list default="true" id="3a869f59-290a-4ab2-b036-a878ce801bc4" name="Changes" comment="Remove update spamming in console">
 | 
					    <list default="true" id="3a869f59-290a-4ab2-b036-a878ce801bc4" name="Changes" comment="Remove update spamming in console">
 | 
				
			||||||
 | 
					      <change afterPath="$PROJECT_DIR$/src/main/java/core/api/models/timemanager/taskgroup/RecursiveTaskgroupInfo.java" afterDir="false" />
 | 
				
			||||||
 | 
					      <change afterPath="$PROJECT_DIR$/src/main/java/core/api/models/timemanager/tasks/TaskOverviewInfo.java" afterDir="false" />
 | 
				
			||||||
      <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
 | 
					      <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
 | 
				
			||||||
      <change beforePath="$PROJECT_DIR$/../frontend/src/app/app.module.ts" beforeDir="false" afterPath="$PROJECT_DIR$/../frontend/src/app/app.module.ts" afterDir="false" />
 | 
					      <change beforePath="$PROJECT_DIR$/src/main/java/core/api/controller/TaskgroupController.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/core/api/controller/TaskgroupController.java" afterDir="false" />
 | 
				
			||||||
      <change beforePath="$PROJECT_DIR$/../frontend/src/app/dashboard/dashboard.component.css" beforeDir="false" afterPath="$PROJECT_DIR$/../frontend/src/app/dashboard/dashboard.component.css" afterDir="false" />
 | 
					 | 
				
			||||||
      <change beforePath="$PROJECT_DIR$/../frontend/src/app/dashboard/dashboard.component.html" beforeDir="false" afterPath="$PROJECT_DIR$/../frontend/src/app/dashboard/dashboard.component.html" afterDir="false" />
 | 
					 | 
				
			||||||
    </list>
 | 
					    </list>
 | 
				
			||||||
    <option name="SHOW_DIALOG" value="false" />
 | 
					    <option name="SHOW_DIALOG" value="false" />
 | 
				
			||||||
    <option name="HIGHLIGHT_CONFLICTS" value="true" />
 | 
					    <option name="HIGHLIGHT_CONFLICTS" value="true" />
 | 
				
			||||||
@ -104,7 +104,7 @@
 | 
				
			|||||||
      <workItem from="1698246651541" duration="5106000" />
 | 
					      <workItem from="1698246651541" duration="5106000" />
 | 
				
			||||||
      <workItem from="1698298897364" duration="4242000" />
 | 
					      <workItem from="1698298897364" duration="4242000" />
 | 
				
			||||||
      <workItem from="1698426430946" duration="665000" />
 | 
					      <workItem from="1698426430946" duration="665000" />
 | 
				
			||||||
      <workItem from="1698474363766" duration="612000" />
 | 
					      <workItem from="1698474363766" duration="1934000" />
 | 
				
			||||||
    </task>
 | 
					    </task>
 | 
				
			||||||
    <task id="LOCAL-00001" summary="Structure Taskgroups in Hierarchies">
 | 
					    <task id="LOCAL-00001" summary="Structure Taskgroups in Hierarchies">
 | 
				
			||||||
      <option name="closed" value="true" />
 | 
					      <option name="closed" value="true" />
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,7 @@
 | 
				
			|||||||
package core.api.controller;
 | 
					package core.api.controller;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import core.api.models.auth.SimpleStatusResponse;
 | 
					import core.api.models.auth.SimpleStatusResponse;
 | 
				
			||||||
 | 
					import core.api.models.timemanager.taskgroup.RecursiveTaskgroupInfo;
 | 
				
			||||||
import core.api.models.timemanager.taskgroup.TaskgroupDetailInfo;
 | 
					import core.api.models.timemanager.taskgroup.TaskgroupDetailInfo;
 | 
				
			||||||
import core.api.models.timemanager.taskgroup.TaskgroupEntityInfo;
 | 
					import core.api.models.timemanager.taskgroup.TaskgroupEntityInfo;
 | 
				
			||||||
import core.api.models.timemanager.taskgroup.TaskgroupFieldInfo;
 | 
					import core.api.models.timemanager.taskgroup.TaskgroupFieldInfo;
 | 
				
			||||||
@ -80,6 +81,13 @@ public class TaskgroupController {
 | 
				
			|||||||
        return ResponseEntity.ok(taskgroupEntityInfos);
 | 
					        return ResponseEntity.ok(taskgroupEntityInfos);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @GetMapping("/taskgroups/tree")
 | 
				
			||||||
 | 
					    public ResponseEntity<List<RecursiveTaskgroupInfo>> listTaskgroupsOfUserAsTree() {
 | 
				
			||||||
 | 
					        List<Taskgroup> taskgroups = taskgroupService.getTopTaskgroupsByUser(SecurityContextHolder.getContext().getAuthentication().getName());
 | 
				
			||||||
 | 
					        List<RecursiveTaskgroupInfo> taskgroupEntityInfos = taskgroups.stream().map(RecursiveTaskgroupInfo::new).toList();
 | 
				
			||||||
 | 
					        return ResponseEntity.ok(taskgroupEntityInfos);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @GetMapping("/taskgroups/{taskgroupID}")
 | 
					    @GetMapping("/taskgroups/{taskgroupID}")
 | 
				
			||||||
    public ResponseEntity<?> getDetails(@PathVariable long taskgroupID) {
 | 
					    public ResponseEntity<?> getDetails(@PathVariable long taskgroupID) {
 | 
				
			||||||
        PermissionResult<Taskgroup> taskgroupPermissionResult = taskgroupService.getTaskgroupByIDAndUsername(taskgroupID, SecurityContextHolder.getContext().getAuthentication().getName());
 | 
					        PermissionResult<Taskgroup> taskgroupPermissionResult = taskgroupService.getTaskgroupByIDAndUsername(taskgroupID, SecurityContextHolder.getContext().getAuthentication().getName());
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,63 @@
 | 
				
			|||||||
 | 
					package core.api.models.timemanager.taskgroup;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import core.api.models.timemanager.tasks.TaskOverviewInfo;
 | 
				
			||||||
 | 
					import core.entities.timemanager.Task;
 | 
				
			||||||
 | 
					import core.entities.timemanager.Taskgroup;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.ArrayList;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class RecursiveTaskgroupInfo {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private long taskgroupID;
 | 
				
			||||||
 | 
					    private String taskgroupName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private List<RecursiveTaskgroupInfo> childTaskgroups = new ArrayList<>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private List<TaskOverviewInfo> activeTasks = new ArrayList<>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public RecursiveTaskgroupInfo(Taskgroup taskgroup) {
 | 
				
			||||||
 | 
					        this.taskgroupID = taskgroup.getTaskgroupID();
 | 
				
			||||||
 | 
					        this.taskgroupName = taskgroup.getTaskgroupName();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for(Taskgroup child : taskgroup.getChildren()) {
 | 
				
			||||||
 | 
					            this.childTaskgroups.add(new RecursiveTaskgroupInfo(child));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for(Task task : taskgroup.getTasks()) {
 | 
				
			||||||
 | 
					            this.activeTasks.add(new TaskOverviewInfo(task));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public long getTaskgroupID() {
 | 
				
			||||||
 | 
					        return taskgroupID;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setTaskgroupID(long taskgroupID) {
 | 
				
			||||||
 | 
					        this.taskgroupID = taskgroupID;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getTaskgroupName() {
 | 
				
			||||||
 | 
					        return taskgroupName;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setTaskgroupName(String taskgroupName) {
 | 
				
			||||||
 | 
					        this.taskgroupName = taskgroupName;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public List<RecursiveTaskgroupInfo> getChildTaskgroups() {
 | 
				
			||||||
 | 
					        return childTaskgroups;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setChildTaskgroups(List<RecursiveTaskgroupInfo> childTaskgroups) {
 | 
				
			||||||
 | 
					        this.childTaskgroups = childTaskgroups;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public List<TaskOverviewInfo> getActiveTasks() {
 | 
				
			||||||
 | 
					        return activeTasks;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setActiveTasks(List<TaskOverviewInfo> activeTasks) {
 | 
				
			||||||
 | 
					        this.activeTasks = activeTasks;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,72 @@
 | 
				
			|||||||
 | 
					package core.api.models.timemanager.tasks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import core.entities.timemanager.Task;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.time.LocalDate;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class TaskOverviewInfo {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private long taskID;
 | 
				
			||||||
 | 
					    private String taskName;
 | 
				
			||||||
 | 
					    private int activeMinutes;
 | 
				
			||||||
 | 
					    private int eta;
 | 
				
			||||||
 | 
					    private LocalDate limit;
 | 
				
			||||||
 | 
					    private boolean overdue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public TaskOverviewInfo(Task task) {
 | 
				
			||||||
 | 
					        this.taskID = task.getTaskID();
 | 
				
			||||||
 | 
					        this.taskName = task.getTaskName();
 | 
				
			||||||
 | 
					        this.activeMinutes = task.getWorkTime();
 | 
				
			||||||
 | 
					        this.eta = task.getEta();
 | 
				
			||||||
 | 
					        this.limit = task.getDeadline();
 | 
				
			||||||
 | 
					        this.overdue = LocalDate.now().isAfter(task.getDeadline());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public long getTaskID() {
 | 
				
			||||||
 | 
					        return taskID;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setTaskID(long taskID) {
 | 
				
			||||||
 | 
					        this.taskID = taskID;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getTaskName() {
 | 
				
			||||||
 | 
					        return taskName;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setTaskName(String taskName) {
 | 
				
			||||||
 | 
					        this.taskName = taskName;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int getActiveMinutes() {
 | 
				
			||||||
 | 
					        return activeMinutes;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setActiveMinutes(int activeMinutes) {
 | 
				
			||||||
 | 
					        this.activeMinutes = activeMinutes;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int getEta() {
 | 
				
			||||||
 | 
					        return eta;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setEta(int eta) {
 | 
				
			||||||
 | 
					        this.eta = eta;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public LocalDate getLimit() {
 | 
				
			||||||
 | 
					        return limit;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setLimit(LocalDate limit) {
 | 
				
			||||||
 | 
					        this.limit = limit;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean isOverdue() {
 | 
				
			||||||
 | 
					        return overdue;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setOverdue(boolean overdue) {
 | 
				
			||||||
 | 
					        this.overdue = overdue;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -28,12 +28,14 @@ model/passwordChangeRequest.ts
 | 
				
			|||||||
model/propertiesInfo.ts
 | 
					model/propertiesInfo.ts
 | 
				
			||||||
model/propertyInfo.ts
 | 
					model/propertyInfo.ts
 | 
				
			||||||
model/propertyUpdateRequest.ts
 | 
					model/propertyUpdateRequest.ts
 | 
				
			||||||
 | 
					model/recursiveTaskgroupInfo.ts
 | 
				
			||||||
model/scheduleActivateInfo.ts
 | 
					model/scheduleActivateInfo.ts
 | 
				
			||||||
model/scheduleInfo.ts
 | 
					model/scheduleInfo.ts
 | 
				
			||||||
model/signUpRequest.ts
 | 
					model/signUpRequest.ts
 | 
				
			||||||
model/simpleStatusResponse.ts
 | 
					model/simpleStatusResponse.ts
 | 
				
			||||||
model/taskEntityInfo.ts
 | 
					model/taskEntityInfo.ts
 | 
				
			||||||
model/taskFieldInfo.ts
 | 
					model/taskFieldInfo.ts
 | 
				
			||||||
 | 
					model/taskOverviewInfo.ts
 | 
				
			||||||
model/taskScheduleStopResponse.ts
 | 
					model/taskScheduleStopResponse.ts
 | 
				
			||||||
model/taskShortInfo.ts
 | 
					model/taskShortInfo.ts
 | 
				
			||||||
model/taskgroupDetailInfo.ts
 | 
					model/taskgroupDetailInfo.ts
 | 
				
			||||||
 | 
				
			|||||||
@ -20,6 +20,7 @@ import { Observable }                                        from 'rxjs';
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import { InlineResponse200 } from '../model/models';
 | 
					import { InlineResponse200 } from '../model/models';
 | 
				
			||||||
import { InlineResponse403 } from '../model/models';
 | 
					import { InlineResponse403 } from '../model/models';
 | 
				
			||||||
 | 
					import { RecursiveTaskgroupInfo } from '../model/models';
 | 
				
			||||||
import { SimpleStatusResponse } from '../model/models';
 | 
					import { SimpleStatusResponse } from '../model/models';
 | 
				
			||||||
import { TaskgroupDetailInfo } from '../model/models';
 | 
					import { TaskgroupDetailInfo } from '../model/models';
 | 
				
			||||||
import { TaskgroupEntityInfo } from '../model/models';
 | 
					import { TaskgroupEntityInfo } from '../model/models';
 | 
				
			||||||
@ -513,4 +514,59 @@ export class TaskgroupService {
 | 
				
			|||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * list all top level taskgroups of authorized user
 | 
				
			||||||
 | 
					     * list all taskgroups of authorized user
 | 
				
			||||||
 | 
					     * @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 taskgroupsTreeGet(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<Array<RecursiveTaskgroupInfo>>;
 | 
				
			||||||
 | 
					    public taskgroupsTreeGet(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<HttpResponse<Array<RecursiveTaskgroupInfo>>>;
 | 
				
			||||||
 | 
					    public taskgroupsTreeGet(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<HttpEvent<Array<RecursiveTaskgroupInfo>>>;
 | 
				
			||||||
 | 
					    public taskgroupsTreeGet(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<any> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        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<Array<RecursiveTaskgroupInfo>>(`${this.configuration.basePath}/taskgroups/tree`,
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                context: localVarHttpContext,
 | 
				
			||||||
 | 
					                responseType: <any>responseType_,
 | 
				
			||||||
 | 
					                withCredentials: this.configuration.withCredentials,
 | 
				
			||||||
 | 
					                headers: localVarHeaders,
 | 
				
			||||||
 | 
					                observe: observe,
 | 
				
			||||||
 | 
					                reportProgress: reportProgress
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -12,12 +12,14 @@ export * from './passwordChangeRequest';
 | 
				
			|||||||
export * from './propertiesInfo';
 | 
					export * from './propertiesInfo';
 | 
				
			||||||
export * from './propertyInfo';
 | 
					export * from './propertyInfo';
 | 
				
			||||||
export * from './propertyUpdateRequest';
 | 
					export * from './propertyUpdateRequest';
 | 
				
			||||||
 | 
					export * from './recursiveTaskgroupInfo';
 | 
				
			||||||
export * from './scheduleActivateInfo';
 | 
					export * from './scheduleActivateInfo';
 | 
				
			||||||
export * from './scheduleInfo';
 | 
					export * from './scheduleInfo';
 | 
				
			||||||
export * from './signUpRequest';
 | 
					export * from './signUpRequest';
 | 
				
			||||||
export * from './simpleStatusResponse';
 | 
					export * from './simpleStatusResponse';
 | 
				
			||||||
export * from './taskEntityInfo';
 | 
					export * from './taskEntityInfo';
 | 
				
			||||||
export * from './taskFieldInfo';
 | 
					export * from './taskFieldInfo';
 | 
				
			||||||
 | 
					export * from './taskOverviewInfo';
 | 
				
			||||||
export * from './taskScheduleStopResponse';
 | 
					export * from './taskScheduleStopResponse';
 | 
				
			||||||
export * from './taskShortInfo';
 | 
					export * from './taskShortInfo';
 | 
				
			||||||
export * from './taskgroupDetailInfo';
 | 
					export * from './taskgroupDetailInfo';
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										27
									
								
								frontend/src/api/model/recursiveTaskgroupInfo.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								frontend/src/api/model/recursiveTaskgroupInfo.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * API Title
 | 
				
			||||||
 | 
					 * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The version of the OpenAPI document: 1.0
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
 | 
				
			||||||
 | 
					 * https://openapi-generator.tech
 | 
				
			||||||
 | 
					 * Do not edit the class manually.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					import { TaskOverviewInfo } from './taskOverviewInfo';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface RecursiveTaskgroupInfo { 
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * internal id of taskgroup
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    taskgroupID: number;
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * name of taskgroup
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    taskgroupName: string;
 | 
				
			||||||
 | 
					    childTaskgroups: Array<RecursiveTaskgroupInfo>;
 | 
				
			||||||
 | 
					    activeTasks: Array<TaskOverviewInfo>;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										40
									
								
								frontend/src/api/model/taskOverviewInfo.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								frontend/src/api/model/taskOverviewInfo.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,40 @@
 | 
				
			|||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * API Title
 | 
				
			||||||
 | 
					 * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The version of the OpenAPI document: 1.0
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
 | 
				
			||||||
 | 
					 * https://openapi-generator.tech
 | 
				
			||||||
 | 
					 * Do not edit the class manually.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface TaskOverviewInfo { 
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * internal id of task
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    taskID: number;
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * name of task
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    taskName: string;
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * expected time to finish task
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    eta: number;
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * date until the task has to be finished
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    limit: string;
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * determines whether the task is overdue
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    overdue: boolean;
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * number in minutes that was already worked on this task
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    activeTime?: number;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1,11 +1,10 @@
 | 
				
			|||||||
.container {
 | 
					.container {
 | 
				
			||||||
  margin: 20px auto;
 | 
					  margin: 20px auto;
 | 
				
			||||||
  width: 70%;
 | 
					  width: 70%;
 | 
				
			||||||
  display: flex;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.spacer {
 | 
					.spacer {
 | 
				
			||||||
  margin-bottom: 2.5%;
 | 
					  flex: 1 1 auto;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -146,3 +145,19 @@
 | 
				
			|||||||
  font-weight: normal;
 | 
					  font-weight: normal;
 | 
				
			||||||
  text-align: left;
 | 
					  text-align: left;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.task-number{
 | 
				
			||||||
 | 
					  color: white;
 | 
				
			||||||
 | 
					  background-color: deepskyblue;
 | 
				
			||||||
 | 
					  padding: 1px 10px;
 | 
				
			||||||
 | 
					  border-radius: 5px;
 | 
				
			||||||
 | 
					  margin-right: 5px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.treenode-content-container {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  flex-direction: row;
 | 
				
			||||||
 | 
					  justify-content: space-between;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,11 @@
 | 
				
			|||||||
  <mat-tree-node *matTreeNodeDef="let node" matTreeNodePadding class="taskgroup-btn" matTreeNodePaddingIndent="10">
 | 
					  <mat-tree-node *matTreeNodeDef="let node" matTreeNodePadding class="taskgroup-btn" matTreeNodePaddingIndent="10">
 | 
				
			||||||
    <!-- use a disabled button to provide padding for tree leaf -->
 | 
					    <!-- use a disabled button to provide padding for tree leaf -->
 | 
				
			||||||
    <button mat-icon-button disabled></button>
 | 
					    <button mat-icon-button disabled></button>
 | 
				
			||||||
 | 
					    <div class="treenode-content-container">
 | 
				
			||||||
      <button mat-button class="node-name">{{node.name}}</button>
 | 
					      <button mat-button class="node-name">{{node.name}}</button>
 | 
				
			||||||
 | 
					      <span class="spacer"></span>
 | 
				
			||||||
 | 
					      <div class="task-number">{{node.activeTasks}}</div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
  </mat-tree-node>
 | 
					  </mat-tree-node>
 | 
				
			||||||
  <!-- This is the tree node template for expandable nodes -->
 | 
					  <!-- This is the tree node template for expandable nodes -->
 | 
				
			||||||
  <mat-tree-node *matTreeNodeDef="let node;when: hasChild" matTreeNodePadding class="taskgroup-btn" matTreeNodePaddingIndent="10">
 | 
					  <mat-tree-node *matTreeNodeDef="let node;when: hasChild" matTreeNodePadding class="taskgroup-btn" matTreeNodePaddingIndent="10">
 | 
				
			||||||
@ -16,6 +20,10 @@
 | 
				
			|||||||
        {{treeControl.isExpanded(node) ? 'expand_more' : 'chevron_right'}}
 | 
					        {{treeControl.isExpanded(node) ? 'expand_more' : 'chevron_right'}}
 | 
				
			||||||
      </mat-icon>
 | 
					      </mat-icon>
 | 
				
			||||||
    </button>
 | 
					    </button>
 | 
				
			||||||
 | 
					    <div class="treenode-content-container">
 | 
				
			||||||
      <button mat-button class="node-name">{{node.name}}</button>
 | 
					      <button mat-button class="node-name">{{node.name}}</button>
 | 
				
			||||||
 | 
					      <span class="spacer"></span>
 | 
				
			||||||
 | 
					      <div class="task-number">{{node.activeTasks}}</div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
  </mat-tree-node>
 | 
					  </mat-tree-node>
 | 
				
			||||||
</mat-tree>
 | 
					</mat-tree>
 | 
				
			||||||
 | 
				
			|||||||
@ -3,52 +3,17 @@ import {MatIconModule} from "@angular/material/icon";
 | 
				
			|||||||
import {MatButtonModule} from "@angular/material/button";
 | 
					import {MatButtonModule} from "@angular/material/button";
 | 
				
			||||||
import {MatTreeFlatDataSource, MatTreeFlattener, MatTreeModule} from "@angular/material/tree";
 | 
					import {MatTreeFlatDataSource, MatTreeFlattener, MatTreeModule} from "@angular/material/tree";
 | 
				
			||||||
import {FlatTreeControl} from "@angular/cdk/tree";
 | 
					import {FlatTreeControl} from "@angular/cdk/tree";
 | 
				
			||||||
 | 
					import {RecursiveTaskgroupInfo, TaskgroupService} from "../../../api";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface TreeNode {
 | 
					 | 
				
			||||||
  name: string
 | 
					 | 
				
			||||||
  children?: TreeNode[]
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const TREE_DATA: TreeNode[] = [
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    name: "KIT",
 | 
					 | 
				
			||||||
    children: [
 | 
					 | 
				
			||||||
      {
 | 
					 | 
				
			||||||
        name: "WS23",
 | 
					 | 
				
			||||||
        children: [
 | 
					 | 
				
			||||||
          {
 | 
					 | 
				
			||||||
            name: "Computergrafik",
 | 
					 | 
				
			||||||
            children: [{name: "Vorlesungen"}, {name: "Globalübungen"}]
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          {
 | 
					 | 
				
			||||||
            name: "Softwaretechnik II",
 | 
					 | 
				
			||||||
            children: [{name: "Vorlesungen"}, {name: "Globalübungen"}]
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          {
 | 
					 | 
				
			||||||
            name: "Algorithmen II",
 | 
					 | 
				
			||||||
            children: [{name: "Vorlesungen"}, {name: "Globalübungen"}]
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        ]
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      {
 | 
					 | 
				
			||||||
        name: "SS24",
 | 
					 | 
				
			||||||
        children: [
 | 
					 | 
				
			||||||
          {
 | 
					 | 
				
			||||||
            name: "Interaktive Computergrafik",
 | 
					 | 
				
			||||||
            children: [{name: "Vorlesungen"}, {name: "Globalübungen"}]
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        ]
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    ]
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Flat node with expandable and level information */
 | 
					/** Flat node with expandable and level information */
 | 
				
			||||||
interface ExampleFlatNode {
 | 
					interface ExampleFlatNode {
 | 
				
			||||||
  expandable: boolean;
 | 
					  expandable: boolean;
 | 
				
			||||||
  name: string;
 | 
					  name: string;
 | 
				
			||||||
  level: number;
 | 
					  level: number;
 | 
				
			||||||
 | 
					  activeTasks: number;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-taskgroup-overview',
 | 
					  selector: 'app-taskgroup-overview',
 | 
				
			||||||
@ -56,12 +21,12 @@ interface ExampleFlatNode {
 | 
				
			|||||||
  styleUrls: ['./taskgroup-overview.component.css']
 | 
					  styleUrls: ['./taskgroup-overview.component.css']
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class TaskgroupOverviewComponent {
 | 
					export class TaskgroupOverviewComponent {
 | 
				
			||||||
 | 
					  private _transformer = (node: RecursiveTaskgroupInfo, level: number) => {
 | 
				
			||||||
  private _transformer = (node: TreeNode, level: number) => {
 | 
					 | 
				
			||||||
    return {
 | 
					    return {
 | 
				
			||||||
      expandable: !!node.children && node.children.length > 0,
 | 
					      expandable: !!node.childTaskgroups && node.childTaskgroups.length > 0,
 | 
				
			||||||
      name: node.name,
 | 
					      name: node.taskgroupName,
 | 
				
			||||||
      level: level,
 | 
					      level: level,
 | 
				
			||||||
 | 
					      activeTasks: node.activeTasks.length
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -74,13 +39,21 @@ export class TaskgroupOverviewComponent {
 | 
				
			|||||||
    this._transformer,
 | 
					    this._transformer,
 | 
				
			||||||
    node => node.level,
 | 
					    node => node.level,
 | 
				
			||||||
    node => node.expandable,
 | 
					    node => node.expandable,
 | 
				
			||||||
    node => node.children,
 | 
					    node => node.childTaskgroups,
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  dataSource = new MatTreeFlatDataSource(this.treeControl, this.treeFlattener);
 | 
					  dataSource = new MatTreeFlatDataSource(this.treeControl, this.treeFlattener);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor() {
 | 
					  constructor(private taskgroupService: TaskgroupService) {
 | 
				
			||||||
    this.dataSource.data = TREE_DATA;
 | 
					
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ngOnInit() {
 | 
				
			||||||
 | 
					    this.taskgroupService.taskgroupsTreeGet().subscribe({
 | 
				
			||||||
 | 
					      next: resp => {
 | 
				
			||||||
 | 
					        this.dataSource.data = resp;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  hasChild = (_: number, node: ExampleFlatNode) => node.expandable;
 | 
					  hasChild = (_: number, node: ExampleFlatNode) => node.expandable;
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										78
									
								
								openapi.yaml
									
									
									
									
									
								
							
							
						
						
									
										78
									
								
								openapi.yaml
									
									
									
									
									
								
							@ -581,6 +581,23 @@ paths:
 | 
				
			|||||||
                type: array
 | 
					                type: array
 | 
				
			||||||
                items:
 | 
					                items:
 | 
				
			||||||
                  $ref: '#/components/schemas/TaskgroupEntityInfo'
 | 
					                  $ref: '#/components/schemas/TaskgroupEntityInfo'
 | 
				
			||||||
 | 
					  /taskgroups/tree:
 | 
				
			||||||
 | 
					    get:
 | 
				
			||||||
 | 
					      security:
 | 
				
			||||||
 | 
					        - API_TOKEN: []
 | 
				
			||||||
 | 
					      tags:
 | 
				
			||||||
 | 
					        - taskgroup
 | 
				
			||||||
 | 
					      summary: list all top level taskgroups of authorized user
 | 
				
			||||||
 | 
					      description: list all taskgroups of authorized user
 | 
				
			||||||
 | 
					      responses:
 | 
				
			||||||
 | 
					        200: 
 | 
				
			||||||
 | 
					          description: Anfrage erfolgreich
 | 
				
			||||||
 | 
					          content:
 | 
				
			||||||
 | 
					            'application/json':
 | 
				
			||||||
 | 
					              schema:
 | 
				
			||||||
 | 
					                type: array
 | 
				
			||||||
 | 
					                items:
 | 
				
			||||||
 | 
					                  $ref: '#/components/schemas/RecursiveTaskgroupInfo'
 | 
				
			||||||
  /taskgroups:
 | 
					  /taskgroups:
 | 
				
			||||||
    get:
 | 
					    get:
 | 
				
			||||||
      security:
 | 
					      security:
 | 
				
			||||||
@ -1979,3 +1996,64 @@ components:
 | 
				
			|||||||
          type: number
 | 
					          type: number
 | 
				
			||||||
          description: time where the schedule was active
 | 
					          description: time where the schedule was active
 | 
				
			||||||
          example: 10
 | 
					          example: 10
 | 
				
			||||||
 | 
					    RecursiveTaskgroupInfo:
 | 
				
			||||||
 | 
					      required:
 | 
				
			||||||
 | 
					        - taskgroupID
 | 
				
			||||||
 | 
					        - taskgroupName
 | 
				
			||||||
 | 
					        - childTaskgroups
 | 
				
			||||||
 | 
					        - activeTasks
 | 
				
			||||||
 | 
					      additionalProperties: false
 | 
				
			||||||
 | 
					      properties:
 | 
				
			||||||
 | 
					        taskgroupID:
 | 
				
			||||||
 | 
					          type: number
 | 
				
			||||||
 | 
					          description: internal id of taskgroup
 | 
				
			||||||
 | 
					          example: 1
 | 
				
			||||||
 | 
					        taskgroupName:
 | 
				
			||||||
 | 
					          type: string
 | 
				
			||||||
 | 
					          description: name of taskgroup
 | 
				
			||||||
 | 
					          example: Taskgroup 1
 | 
				
			||||||
 | 
					          maxLength: 255
 | 
				
			||||||
 | 
					          minLength: 1
 | 
				
			||||||
 | 
					        childTaskgroups:
 | 
				
			||||||
 | 
					          type: array
 | 
				
			||||||
 | 
					          items: 
 | 
				
			||||||
 | 
					            $ref: '#/components/schemas/RecursiveTaskgroupInfo'
 | 
				
			||||||
 | 
					        activeTasks:
 | 
				
			||||||
 | 
					          type: array
 | 
				
			||||||
 | 
					          items:
 | 
				
			||||||
 | 
					            $ref: '#/components/schemas/TaskOverviewInfo'
 | 
				
			||||||
 | 
					    TaskOverviewInfo:
 | 
				
			||||||
 | 
					      required:
 | 
				
			||||||
 | 
					        - taskID
 | 
				
			||||||
 | 
					        - taskName
 | 
				
			||||||
 | 
					        - activeMinutes
 | 
				
			||||||
 | 
					        - eta
 | 
				
			||||||
 | 
					        - limit
 | 
				
			||||||
 | 
					        - overdue
 | 
				
			||||||
 | 
					      additionalProperties: false
 | 
				
			||||||
 | 
					      properties:
 | 
				
			||||||
 | 
					        taskID:
 | 
				
			||||||
 | 
					          type: number
 | 
				
			||||||
 | 
					          description: internal id of task
 | 
				
			||||||
 | 
					          example: 1
 | 
				
			||||||
 | 
					        taskName:
 | 
				
			||||||
 | 
					          type: string
 | 
				
			||||||
 | 
					          description: name of task
 | 
				
			||||||
 | 
					          example: Vorlesung schauen
 | 
				
			||||||
 | 
					        eta:
 | 
				
			||||||
 | 
					          type: number
 | 
				
			||||||
 | 
					          description: expected time to finish task
 | 
				
			||||||
 | 
					          example: 10
 | 
				
			||||||
 | 
					          minimum: 0
 | 
				
			||||||
 | 
					        limit:
 | 
				
			||||||
 | 
					          type: string
 | 
				
			||||||
 | 
					          format: date
 | 
				
			||||||
 | 
					          description: date until the task has to be finished
 | 
				
			||||||
 | 
					        overdue:
 | 
				
			||||||
 | 
					          type: boolean
 | 
				
			||||||
 | 
					          description: determines whether the task is overdue
 | 
				
			||||||
 | 
					          example: True
 | 
				
			||||||
 | 
					        activeTime:
 | 
				
			||||||
 | 
					          type: number
 | 
				
			||||||
 | 
					          description: number in minutes that was already worked on this task
 | 
				
			||||||
 | 
					          example: 10
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user