issue-53 #78
@ -1,21 +1,30 @@
|
|||||||
package core.api.controller;
|
package core.api.controller;
|
||||||
|
|
||||||
|
import core.api.models.auth.SimpleStatusResponse;
|
||||||
|
import core.api.models.timemanager.history.TaskgroupActivityInfo;
|
||||||
import core.api.models.timemanager.history.WorkingStatus;
|
import core.api.models.timemanager.history.WorkingStatus;
|
||||||
|
import core.api.models.timemanager.taskgroup.TaskgroupEntityInfo;
|
||||||
import core.entities.timemanager.AbstractSchedule;
|
import core.entities.timemanager.AbstractSchedule;
|
||||||
|
import core.entities.timemanager.Task;
|
||||||
|
import core.entities.timemanager.Taskgroup;
|
||||||
|
import core.services.PermissionResult;
|
||||||
|
import core.services.ServiceExitCode;
|
||||||
import core.services.TaskScheduleService;
|
import core.services.TaskScheduleService;
|
||||||
|
import core.services.TaskgroupService;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.web.bind.annotation.CrossOrigin;
|
import org.springframework.security.core.parameters.P;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.LocalTime;
|
import java.time.LocalTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
@CrossOrigin(origins = "*", maxAge = 3600)
|
@CrossOrigin(origins = "*", maxAge = 3600)
|
||||||
@RestController
|
@RestController
|
||||||
@ -23,6 +32,7 @@ import java.util.List;
|
|||||||
public class StatisticController {
|
public class StatisticController {
|
||||||
|
|
||||||
@Autowired private TaskScheduleService taskScheduleService;
|
@Autowired private TaskScheduleService taskScheduleService;
|
||||||
|
@Autowired private TaskgroupService taskgroupService;
|
||||||
|
|
||||||
@GetMapping("/history/workingStatus")
|
@GetMapping("/history/workingStatus")
|
||||||
public ResponseEntity<?> getWorkingStatus() {
|
public ResponseEntity<?> getWorkingStatus() {
|
||||||
@ -39,4 +49,17 @@ public class StatisticController {
|
|||||||
|
|
||||||
return ResponseEntity.ok(new WorkingStatus(missedSchedules, activeTime));
|
return ResponseEntity.ok(new WorkingStatus(missedSchedules, activeTime));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/statistics/taskgroup-activity/{taskgroupID}/{startingDate}/{endingDate}/{includeSubTaskgroups}")
|
||||||
|
public ResponseEntity<?> getTaskgroupActivity(@PathVariable long taskgroupID, @PathVariable String startingDate, @PathVariable String endingDate, @PathVariable boolean includeSubTaskgroups){
|
||||||
|
PermissionResult<Taskgroup> taskgroupPermissionResult = taskgroupService.getTaskgroupByIDAndUsername(taskgroupID, SecurityContextHolder.getContext().getAuthentication().getName());
|
||||||
|
if(!taskgroupPermissionResult.isHasPermissions()) {
|
||||||
|
return ResponseEntity.status(403).body(new SimpleStatusResponse("failed"));
|
||||||
|
} else if(taskgroupPermissionResult.getExitCode() == ServiceExitCode.MISSING_ENTITY) {
|
||||||
|
return ResponseEntity.status(404).body(new SimpleStatusResponse("failed"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResponseEntity.ok(taskgroupPermissionResult.getResult().calcActivityInfo(includeSubTaskgroups,
|
||||||
|
LocalDate.parse(startingDate, DateTimeFormatter.ofPattern("yyyy-MM-dd")), LocalDate.parse(endingDate, DateTimeFormatter.ofPattern("yyyy-MM-dd"))));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +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.*;
|
||||||
import core.api.models.timemanager.taskgroup.TaskgroupDetailInfo;
|
|
||||||
import core.api.models.timemanager.taskgroup.TaskgroupEntityInfo;
|
|
||||||
import core.api.models.timemanager.taskgroup.TaskgroupFieldInfo;
|
|
||||||
import core.entities.timemanager.Taskgroup;
|
import core.entities.timemanager.Taskgroup;
|
||||||
import core.services.*;
|
import core.services.*;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@ -116,4 +113,10 @@ public class TaskgroupController {
|
|||||||
taskService.clearTasks(taskgroupPermissionResult.getResult());
|
taskService.clearTasks(taskgroupPermissionResult.getResult());
|
||||||
return ResponseEntity.ok(new SimpleStatusResponse("success"));
|
return ResponseEntity.ok(new SimpleStatusResponse("success"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/taskgroups/path")
|
||||||
|
public ResponseEntity<?> getTaskgroupPaths() {
|
||||||
|
List<Taskgroup> taskgroups = taskgroupService.getTaskgroupsByUser(SecurityContextHolder.getContext().getAuthentication().getName());
|
||||||
|
return ResponseEntity.ok(taskgroups.stream().map(TaskgroupPathInfo::new));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
package core.api.models.timemanager.history;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import core.api.models.timemanager.taskgroup.TaskgroupEntityInfo;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
public class TaskgroupActivityInfo {
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private LocalDate date;
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private int activeMinutes;
|
||||||
|
|
||||||
|
public TaskgroupActivityInfo(int activeMinutes, LocalDate localDate) {
|
||||||
|
this.date = localDate;
|
||||||
|
this.activeMinutes = activeMinutes;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package core.api.models.timemanager.taskgroup;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import core.entities.timemanager.Taskgroup;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class TaskgroupPathInfo {
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private String taskgroupPath;
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private List<TaskgroupEntityInfo> directChildren;
|
||||||
|
public TaskgroupPathInfo(Taskgroup taskgroup) {
|
||||||
|
List<Taskgroup> taskgroupPath = Taskgroup.getAncestorList(taskgroup);
|
||||||
|
StringBuilder stringBuilder = new StringBuilder();
|
||||||
|
for(Taskgroup taskgroupPathEntity : taskgroupPath) {
|
||||||
|
stringBuilder.append(taskgroupPathEntity.getTaskgroupName());
|
||||||
|
stringBuilder.append("/");
|
||||||
|
}
|
||||||
|
this.taskgroupPath = stringBuilder.substring(0, stringBuilder.length()-1);
|
||||||
|
directChildren = taskgroup.getChildren().stream().map(TaskgroupEntityInfo::new).toList();
|
||||||
|
}
|
||||||
|
}
|
@ -93,6 +93,10 @@ public abstract class AbstractSchedule {
|
|||||||
return startTime != null && stopTime == null;
|
return startTime != null && stopTime == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isCompleted() {
|
||||||
|
return startTime != null && stopTime != null;
|
||||||
|
}
|
||||||
|
|
||||||
public int getActiveTime() {
|
public int getActiveTime() {
|
||||||
if(startTime == null) {
|
if(startTime == null) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -188,4 +188,14 @@ public class Task {
|
|||||||
public void setFinishable(boolean finishable) {
|
public void setFinishable(boolean finishable) {
|
||||||
this.finishable = finishable;
|
this.finishable = finishable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int calcOverallActivityInfo(LocalDate date) {
|
||||||
|
int activeMinutes = 0;
|
||||||
|
for(AbstractSchedule schedule : getBasicTaskSchedules()) {
|
||||||
|
if(schedule.isCompleted() && schedule.getStartTime().toLocalDate().isEqual(date)) {
|
||||||
|
activeMinutes += schedule.calcActiveMinutes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return activeMinutes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
package core.entities.timemanager;
|
package core.entities.timemanager;
|
||||||
|
|
||||||
|
import core.api.models.timemanager.history.TaskgroupActivityInfo;
|
||||||
|
import core.api.models.timemanager.taskgroup.TaskgroupEntityInfo;
|
||||||
import core.entities.User;
|
import core.entities.User;
|
||||||
|
|
||||||
import javax.persistence.*;
|
import javax.persistence.*;
|
||||||
import javax.validation.constraints.NotBlank;
|
import javax.validation.constraints.NotBlank;
|
||||||
|
import java.time.LocalDate;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@ -132,4 +135,55 @@ public class Taskgroup {
|
|||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(taskgroupID);
|
return Objects.hash(taskgroupID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<TaskgroupActivityInfo> calcActivityInfo(boolean includeChildTasks, LocalDate start, LocalDate end) {
|
||||||
|
HashMap<LocalDate, Integer> activityInfos = new HashMap<>();
|
||||||
|
if(includeChildTasks) {
|
||||||
|
|
||||||
|
Queue<Taskgroup> queue = new LinkedList<>(children);
|
||||||
|
while(!queue.isEmpty()) {
|
||||||
|
Taskgroup childTraskgroup = queue.poll();
|
||||||
|
LocalDate currentDate = start;
|
||||||
|
while(!currentDate.isAfter(end)) {
|
||||||
|
int activeMinutes = 0;
|
||||||
|
for(Task task : childTraskgroup.getTasks()) {
|
||||||
|
activeMinutes += task.calcOverallActivityInfo(currentDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(activityInfos.containsKey(currentDate)) {
|
||||||
|
activityInfos.put(currentDate, activityInfos.get(currentDate) + activeMinutes);
|
||||||
|
} else {
|
||||||
|
activityInfos.put(currentDate, activeMinutes);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentDate = currentDate.plusDays(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
queue.addAll(childTraskgroup.getChildren());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalDate currentDate = start;
|
||||||
|
while(!currentDate.isAfter(end)) {
|
||||||
|
int activeMinutes = 0;
|
||||||
|
for(Task task : tasks) {
|
||||||
|
activeMinutes += task.calcOverallActivityInfo(currentDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(activityInfos.containsKey(currentDate)) {
|
||||||
|
activityInfos.put(currentDate, activityInfos.get(currentDate) + activeMinutes);
|
||||||
|
} else {
|
||||||
|
activityInfos.put(currentDate, activeMinutes);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentDate = currentDate.plusDays(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<TaskgroupActivityInfo> taskgroupActivityInfos = new ArrayList<>();
|
||||||
|
for(Map.Entry<LocalDate, Integer> entry : activityInfos.entrySet()) {
|
||||||
|
taskgroupActivityInfos.add(new TaskgroupActivityInfo(entry.getValue(), entry.getKey()));
|
||||||
|
}
|
||||||
|
return taskgroupActivityInfos;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
package core.repositories.timemanager;
|
package core.repositories.timemanager;
|
||||||
|
|
||||||
import core.entities.timemanager.AbstractSchedule;
|
import core.entities.timemanager.AbstractSchedule;
|
||||||
|
import core.entities.timemanager.Taskgroup;
|
||||||
import org.springframework.data.jpa.repository.Query;
|
import org.springframework.data.jpa.repository.Query;
|
||||||
import org.springframework.data.repository.CrudRepository;
|
import org.springframework.data.repository.CrudRepository;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@ -107,4 +107,8 @@ public class TaskgroupService {
|
|||||||
public void deleteTaskgroupByUser(User user) {
|
public void deleteTaskgroupByUser(User user) {
|
||||||
taskgroupRepository.deleteAllByUser(user);
|
taskgroupRepository.deleteAllByUser(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Taskgroup> getTaskgroupsByUser(String name) {
|
||||||
|
return taskgroupRepository.findAllByUser(name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,9 @@
|
|||||||
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
|
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
|
||||||
"src/styles.css"
|
"src/styles.css"
|
||||||
],
|
],
|
||||||
"scripts": []
|
"scripts": [
|
||||||
|
"node_modules/apexcharts/dist/apexcharts.min.js"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"production": {
|
"production": {
|
||||||
@ -100,7 +102,9 @@
|
|||||||
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
|
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
|
||||||
"src/styles.css"
|
"src/styles.css"
|
||||||
],
|
],
|
||||||
"scripts": []
|
"scripts": [
|
||||||
|
"node_modules/apexcharts/dist/apexcharts.min.js"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
147
frontend/package-lock.json
generated
147
frontend/package-lock.json
generated
@ -22,10 +22,13 @@
|
|||||||
"@angular/platform-browser-dynamic": "^16.2.7",
|
"@angular/platform-browser-dynamic": "^16.2.7",
|
||||||
"@angular/router": "^16.2.7",
|
"@angular/router": "^16.2.7",
|
||||||
"angular-calendar": "^0.31.0",
|
"angular-calendar": "^0.31.0",
|
||||||
|
"apexcharts": "^3.44.0",
|
||||||
"date-fns": "^2.29.3",
|
"date-fns": "^2.29.3",
|
||||||
"luxon": "^3.4.3",
|
"luxon": "^3.4.3",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
|
"ng-apexcharts": "^1.8.0",
|
||||||
"ngx-material-timepicker": "^13.1.1",
|
"ngx-material-timepicker": "^13.1.1",
|
||||||
|
"ngx-slider-v2": "^16.0.2",
|
||||||
"rxjs": "~7.5.0",
|
"rxjs": "~7.5.0",
|
||||||
"tslib": "^2.3.0",
|
"tslib": "^2.3.0",
|
||||||
"zone.js": "~0.13.3"
|
"zone.js": "~0.13.3"
|
||||||
@ -4738,6 +4741,11 @@
|
|||||||
"integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==",
|
"integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@yr/monotone-cubic-spline": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@yr/monotone-cubic-spline/-/monotone-cubic-spline-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-FQXkOta0XBSUPHndIKON2Y9JeQz5ZeMqLYZVVK93FliNBFm7LNMIZmY6FrMEB9XPcDbE2bekMbZD6kzDkxwYjA=="
|
||||||
|
},
|
||||||
"node_modules/abab": {
|
"node_modules/abab": {
|
||||||
"version": "2.0.6",
|
"version": "2.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
|
||||||
@ -5036,6 +5044,20 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/apexcharts": {
|
||||||
|
"version": "3.44.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.44.0.tgz",
|
||||||
|
"integrity": "sha512-u7Xzrbcxc2yWznN78Jh5NMCYVAsWDfBjRl5ea++rVzFAqjU2hLz4RgKIFwYOBDRQtW1e/Qz8azJTqIJ1+Vu9Qg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@yr/monotone-cubic-spline": "^1.0.3",
|
||||||
|
"svg.draggable.js": "^2.2.2",
|
||||||
|
"svg.easing.js": "^2.0.0",
|
||||||
|
"svg.filter.js": "^2.0.2",
|
||||||
|
"svg.pathmorphing.js": "^0.1.3",
|
||||||
|
"svg.resize.js": "^1.4.3",
|
||||||
|
"svg.select.js": "^3.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/aproba": {
|
"node_modules/aproba": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
|
||||||
@ -6334,12 +6356,25 @@
|
|||||||
"npm": "1.2.8000 || >= 1.4.16"
|
"npm": "1.2.8000 || >= 1.4.16"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/detect-it": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/detect-it/-/detect-it-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-dg5YBTJYvogK1+dA2mBUDKzOWfYZtHVba89SyZUhc4+e3i2tzgjANFg5lDRCd3UOtRcw00vUTMK8LELcMdicug=="
|
||||||
|
},
|
||||||
"node_modules/detect-node": {
|
"node_modules/detect-node": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz",
|
||||||
"integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==",
|
"integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/detect-passive-events": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/detect-passive-events/-/detect-passive-events-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-QN/1X65Axis6a9D8qg8Py9cwY/fkWAmAH/edTbmLMcv4m5dboLJ7LcAi8CfaCON2tjk904KwKX/HTdsHC6yeRg==",
|
||||||
|
"dependencies": {
|
||||||
|
"detect-it": "^4.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/di": {
|
"node_modules/di": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz",
|
||||||
@ -9753,6 +9788,20 @@
|
|||||||
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
|
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/ng-apexcharts": {
|
||||||
|
"version": "1.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ng-apexcharts/-/ng-apexcharts-1.8.0.tgz",
|
||||||
|
"integrity": "sha512-NwJuMLHoLm52LSzM08RXV6oOOTyUYREAV53WHVGs+L2qi8UWbxCz19hX0kk+F/xFLEhhuiLegO3T1v30jLbKSQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/common": ">=13.0.0",
|
||||||
|
"@angular/core": ">=13.0.0",
|
||||||
|
"apexcharts": "^3.41.0",
|
||||||
|
"rxjs": "^6.5.3 || ^7.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ngx-material-timepicker": {
|
"node_modules/ngx-material-timepicker": {
|
||||||
"version": "13.1.1",
|
"version": "13.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/ngx-material-timepicker/-/ngx-material-timepicker-13.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ngx-material-timepicker/-/ngx-material-timepicker-13.1.1.tgz",
|
||||||
@ -9765,6 +9814,21 @@
|
|||||||
"luxon": ">= 1.24.0"
|
"luxon": ">= 1.24.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/ngx-slider-v2": {
|
||||||
|
"version": "16.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ngx-slider-v2/-/ngx-slider-v2-16.0.2.tgz",
|
||||||
|
"integrity": "sha512-Lpl7SlErL+tJJvTRZYdyZoXTThKN8Ro1z3vscJQ1O5azHXwvbv3pnTcsOwY4ltfaP+dpzY27KL1QXyDr6QMaxQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"detect-passive-events": "^2.0.3",
|
||||||
|
"rxjs": "^7.4.0",
|
||||||
|
"tslib": "^2.3.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/common": "^16.0.0",
|
||||||
|
"@angular/core": "^16.0.0",
|
||||||
|
"@angular/forms": "^16.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/nice-napi": {
|
"node_modules/nice-napi": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz",
|
||||||
@ -12181,6 +12245,89 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/svg.draggable.js": {
|
||||||
|
"version": "2.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/svg.draggable.js/-/svg.draggable.js-2.2.2.tgz",
|
||||||
|
"integrity": "sha512-JzNHBc2fLQMzYCZ90KZHN2ohXL0BQJGQimK1kGk6AvSeibuKcIdDX9Kr0dT9+UJ5O8nYA0RB839Lhvk4CY4MZw==",
|
||||||
|
"dependencies": {
|
||||||
|
"svg.js": "^2.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/svg.easing.js": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/svg.easing.js/-/svg.easing.js-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-//ctPdJMGy22YoYGV+3HEfHbm6/69LJUTAqI2/5qBvaNHZ9uUFVC82B0Pl299HzgH13rKrBgi4+XyXXyVWWthA==",
|
||||||
|
"dependencies": {
|
||||||
|
"svg.js": ">=2.3.x"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/svg.filter.js": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/svg.filter.js/-/svg.filter.js-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-xkGBwU+dKBzqg5PtilaTb0EYPqPfJ9Q6saVldX+5vCRy31P6TlRCP3U9NxH3HEufkKkpNgdTLBJnmhDHeTqAkw==",
|
||||||
|
"dependencies": {
|
||||||
|
"svg.js": "^2.2.5"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/svg.js": {
|
||||||
|
"version": "2.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/svg.js/-/svg.js-2.7.1.tgz",
|
||||||
|
"integrity": "sha512-ycbxpizEQktk3FYvn/8BH+6/EuWXg7ZpQREJvgacqn46gIddG24tNNe4Son6omdXCnSOaApnpZw6MPCBA1dODA=="
|
||||||
|
},
|
||||||
|
"node_modules/svg.pathmorphing.js": {
|
||||||
|
"version": "0.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/svg.pathmorphing.js/-/svg.pathmorphing.js-0.1.3.tgz",
|
||||||
|
"integrity": "sha512-49HWI9X4XQR/JG1qXkSDV8xViuTLIWm/B/7YuQELV5KMOPtXjiwH4XPJvr/ghEDibmLQ9Oc22dpWpG0vUDDNww==",
|
||||||
|
"dependencies": {
|
||||||
|
"svg.js": "^2.4.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/svg.resize.js": {
|
||||||
|
"version": "1.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/svg.resize.js/-/svg.resize.js-1.4.3.tgz",
|
||||||
|
"integrity": "sha512-9k5sXJuPKp+mVzXNvxz7U0uC9oVMQrrf7cFsETznzUDDm0x8+77dtZkWdMfRlmbkEEYvUn9btKuZ3n41oNA+uw==",
|
||||||
|
"dependencies": {
|
||||||
|
"svg.js": "^2.6.5",
|
||||||
|
"svg.select.js": "^2.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/svg.resize.js/node_modules/svg.select.js": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-tH6ABEyJsAOVAhwcCjF8mw4crjXSI1aa7j2VQR8ZuJ37H2MBUbyeqYr5nEO7sSN3cy9AR9DUwNg0t/962HlDbQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"svg.js": "^2.2.5"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/svg.select.js": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-h5IS/hKkuVCbKSieR9uQCj9w+zLHoPh+ce19bBYyqF53g6mnPB8sAtIbe1s9dh2S2fCmYX2xel1Ln3PJBbK4kw==",
|
||||||
|
"dependencies": {
|
||||||
|
"svg.js": "^2.6.5"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/symbol-observable": {
|
"node_modules/symbol-observable": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz",
|
||||||
|
@ -24,10 +24,13 @@
|
|||||||
"@angular/platform-browser-dynamic": "^16.2.7",
|
"@angular/platform-browser-dynamic": "^16.2.7",
|
||||||
"@angular/router": "^16.2.7",
|
"@angular/router": "^16.2.7",
|
||||||
"angular-calendar": "^0.31.0",
|
"angular-calendar": "^0.31.0",
|
||||||
|
"apexcharts": "^3.44.0",
|
||||||
"date-fns": "^2.29.3",
|
"date-fns": "^2.29.3",
|
||||||
"luxon": "^3.4.3",
|
"luxon": "^3.4.3",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
|
"ng-apexcharts": "^1.8.0",
|
||||||
"ngx-material-timepicker": "^13.1.1",
|
"ngx-material-timepicker": "^13.1.1",
|
||||||
|
"ngx-slider-v2": "^16.0.2",
|
||||||
"rxjs": "~7.5.0",
|
"rxjs": "~7.5.0",
|
||||||
"tslib": "^2.3.0",
|
"tslib": "^2.3.0",
|
||||||
"zone.js": "~0.13.3"
|
"zone.js": "~0.13.3"
|
||||||
|
@ -47,9 +47,11 @@ model/taskOverviewInfo.ts
|
|||||||
model/taskScheduleStopResponse.ts
|
model/taskScheduleStopResponse.ts
|
||||||
model/taskShortInfo.ts
|
model/taskShortInfo.ts
|
||||||
model/taskTaskgroupInfo.ts
|
model/taskTaskgroupInfo.ts
|
||||||
|
model/taskgroupActivityInfo.ts
|
||||||
model/taskgroupDetailInfo.ts
|
model/taskgroupDetailInfo.ts
|
||||||
model/taskgroupEntityInfo.ts
|
model/taskgroupEntityInfo.ts
|
||||||
model/taskgroupFieldInfo.ts
|
model/taskgroupFieldInfo.ts
|
||||||
|
model/taskgroupPathInfo.ts
|
||||||
model/userAddInfo.ts
|
model/userAddInfo.ts
|
||||||
model/userInfo.ts
|
model/userInfo.ts
|
||||||
model/userUpdateInfo.ts
|
model/userUpdateInfo.ts
|
||||||
|
@ -20,6 +20,7 @@ import { Observable } from 'rxjs';
|
|||||||
|
|
||||||
import { ScheduleStatus } from '../model/models';
|
import { ScheduleStatus } from '../model/models';
|
||||||
import { SimpleStatusResponse } from '../model/models';
|
import { SimpleStatusResponse } from '../model/models';
|
||||||
|
import { TaskgroupActivityInfo } from '../model/models';
|
||||||
|
|
||||||
import { BASE_PATH, COLLECTION_FORMATS } from '../variables';
|
import { BASE_PATH, COLLECTION_FORMATS } from '../variables';
|
||||||
import { Configuration } from '../configuration';
|
import { Configuration } from '../configuration';
|
||||||
@ -141,4 +142,73 @@ export class HistoryService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param taskgroupID internal id of taskgroup
|
||||||
|
* @param startingDate starting date
|
||||||
|
* @param endingDate starting date
|
||||||
|
* @param includeSubTaskgroups determines whether to include subtaskgroups or not
|
||||||
|
* @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 statisticsTaskgroupActivityTaskgroupIDStartingDateEndingDateIncludeSubTaskgroupsGet(taskgroupID: number, startingDate: string, endingDate: string, includeSubTaskgroups: boolean, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<Array<TaskgroupActivityInfo>>;
|
||||||
|
public statisticsTaskgroupActivityTaskgroupIDStartingDateEndingDateIncludeSubTaskgroupsGet(taskgroupID: number, startingDate: string, endingDate: string, includeSubTaskgroups: boolean, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<HttpResponse<Array<TaskgroupActivityInfo>>>;
|
||||||
|
public statisticsTaskgroupActivityTaskgroupIDStartingDateEndingDateIncludeSubTaskgroupsGet(taskgroupID: number, startingDate: string, endingDate: string, includeSubTaskgroups: boolean, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<HttpEvent<Array<TaskgroupActivityInfo>>>;
|
||||||
|
public statisticsTaskgroupActivityTaskgroupIDStartingDateEndingDateIncludeSubTaskgroupsGet(taskgroupID: number, startingDate: string, endingDate: string, includeSubTaskgroups: boolean, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<any> {
|
||||||
|
if (taskgroupID === null || taskgroupID === undefined) {
|
||||||
|
throw new Error('Required parameter taskgroupID was null or undefined when calling statisticsTaskgroupActivityTaskgroupIDStartingDateEndingDateIncludeSubTaskgroupsGet.');
|
||||||
|
}
|
||||||
|
if (startingDate === null || startingDate === undefined) {
|
||||||
|
throw new Error('Required parameter startingDate was null or undefined when calling statisticsTaskgroupActivityTaskgroupIDStartingDateEndingDateIncludeSubTaskgroupsGet.');
|
||||||
|
}
|
||||||
|
if (endingDate === null || endingDate === undefined) {
|
||||||
|
throw new Error('Required parameter endingDate was null or undefined when calling statisticsTaskgroupActivityTaskgroupIDStartingDateEndingDateIncludeSubTaskgroupsGet.');
|
||||||
|
}
|
||||||
|
if (includeSubTaskgroups === null || includeSubTaskgroups === undefined) {
|
||||||
|
throw new Error('Required parameter includeSubTaskgroups was null or undefined when calling statisticsTaskgroupActivityTaskgroupIDStartingDateEndingDateIncludeSubTaskgroupsGet.');
|
||||||
|
}
|
||||||
|
|
||||||
|
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<TaskgroupActivityInfo>>(`${this.configuration.basePath}/statistics/taskgroup-activity/${encodeURIComponent(String(taskgroupID))}/${encodeURIComponent(String(startingDate))}/${encodeURIComponent(String(endingDate))}/${encodeURIComponent(String(includeSubTaskgroups))}`,
|
||||||
|
{
|
||||||
|
context: localVarHttpContext,
|
||||||
|
responseType: <any>responseType_,
|
||||||
|
withCredentials: this.configuration.withCredentials,
|
||||||
|
headers: localVarHeaders,
|
||||||
|
observe: observe,
|
||||||
|
reportProgress: reportProgress
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ 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';
|
||||||
import { TaskgroupFieldInfo } from '../model/models';
|
import { TaskgroupFieldInfo } from '../model/models';
|
||||||
|
import { TaskgroupPathInfo } from '../model/models';
|
||||||
|
|
||||||
import { BASE_PATH, COLLECTION_FORMATS } from '../variables';
|
import { BASE_PATH, COLLECTION_FORMATS } from '../variables';
|
||||||
import { Configuration } from '../configuration';
|
import { Configuration } from '../configuration';
|
||||||
@ -201,6 +202,61 @@ export class TaskgroupService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lists all taskgrouppaths
|
||||||
|
* lists all taskgroup-paths
|
||||||
|
* @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 taskgroupsPathGet(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<Array<TaskgroupPathInfo>>;
|
||||||
|
public taskgroupsPathGet(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<HttpResponse<Array<TaskgroupPathInfo>>>;
|
||||||
|
public taskgroupsPathGet(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<HttpEvent<Array<TaskgroupPathInfo>>>;
|
||||||
|
public taskgroupsPathGet(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<TaskgroupPathInfo>>(`${this.configuration.basePath}/taskgroups/path`,
|
||||||
|
{
|
||||||
|
context: localVarHttpContext,
|
||||||
|
responseType: <any>responseType_,
|
||||||
|
withCredentials: this.configuration.withCredentials,
|
||||||
|
headers: localVarHeaders,
|
||||||
|
observe: observe,
|
||||||
|
reportProgress: reportProgress
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* creates taskgroup
|
* creates taskgroup
|
||||||
* creates taskgroup
|
* creates taskgroup
|
||||||
|
@ -30,9 +30,11 @@ export * from './taskOverviewInfo';
|
|||||||
export * from './taskScheduleStopResponse';
|
export * from './taskScheduleStopResponse';
|
||||||
export * from './taskShortInfo';
|
export * from './taskShortInfo';
|
||||||
export * from './taskTaskgroupInfo';
|
export * from './taskTaskgroupInfo';
|
||||||
|
export * from './taskgroupActivityInfo';
|
||||||
export * from './taskgroupDetailInfo';
|
export * from './taskgroupDetailInfo';
|
||||||
export * from './taskgroupEntityInfo';
|
export * from './taskgroupEntityInfo';
|
||||||
export * from './taskgroupFieldInfo';
|
export * from './taskgroupFieldInfo';
|
||||||
|
export * from './taskgroupPathInfo';
|
||||||
export * from './userAddInfo';
|
export * from './userAddInfo';
|
||||||
export * from './userInfo';
|
export * from './userInfo';
|
||||||
export * from './userUpdateInfo';
|
export * from './userUpdateInfo';
|
||||||
|
21
frontend/src/api/model/taskgroupActivityInfo.ts
Normal file
21
frontend/src/api/model/taskgroupActivityInfo.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* 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 TaskgroupActivityInfo {
|
||||||
|
date: string;
|
||||||
|
/**
|
||||||
|
* Number of minutes the task was active
|
||||||
|
*/
|
||||||
|
activeMinutes: number;
|
||||||
|
}
|
||||||
|
|
22
frontend/src/api/model/taskgroupPathInfo.ts
Normal file
22
frontend/src/api/model/taskgroupPathInfo.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* 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 { TaskgroupEntityInfo } from './taskgroupEntityInfo';
|
||||||
|
|
||||||
|
|
||||||
|
export interface TaskgroupPathInfo {
|
||||||
|
/**
|
||||||
|
* TaskgroupPath
|
||||||
|
*/
|
||||||
|
taskgroupPath: string;
|
||||||
|
directChildren: Array<TaskgroupEntityInfo>;
|
||||||
|
}
|
||||||
|
|
@ -15,6 +15,7 @@ import {DraggableSchedulerComponent} from "./schedules/draggable-scheduler/dragg
|
|||||||
import {
|
import {
|
||||||
ForgottenTaskStartDialogComponent
|
ForgottenTaskStartDialogComponent
|
||||||
} from "./dashboard/forgotten-task-start-dialog/forgotten-task-start-dialog.component";
|
} from "./dashboard/forgotten-task-start-dialog/forgotten-task-start-dialog.component";
|
||||||
|
import {TaskgroupActivityComponent} from "./statistics/taskgroup-activity/taskgroup-activity.component";
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{path: '', component: MainComponent},
|
{path: '', component: MainComponent},
|
||||||
@ -30,7 +31,8 @@ const routes: Routes = [
|
|||||||
{path: 'upcoming', component: UpcomingTaskOverviewComponent},
|
{path: 'upcoming', component: UpcomingTaskOverviewComponent},
|
||||||
{path: 'active', component: ActiveTaskOverviewComponent},
|
{path: 'active', component: ActiveTaskOverviewComponent},
|
||||||
{path: 'scheduler', component: DraggableSchedulerComponent},
|
{path: 'scheduler', component: DraggableSchedulerComponent},
|
||||||
{path: 'forgotten', component: ForgottenTaskStartDialogComponent}
|
{path: 'forgotten', component: ForgottenTaskStartDialogComponent},
|
||||||
|
{path: 'statistics/taskgroup-activity', component: TaskgroupActivityComponent}
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
<span>TimeManager</span>
|
<span>TimeManager</span>
|
||||||
|
|
||||||
<button mat-button aria-label="Organize" *ngIf="authService.hasKey" [matMenuTriggerFor]="organizeMenu">Organize ▾</button>
|
<button mat-button aria-label="Organize" *ngIf="authService.hasKey" [matMenuTriggerFor]="organizeMenu">Organize ▾</button>
|
||||||
|
<button mat-button aria-label="Statistics" *ngIf="authService.hasKey" [matMenuTriggerFor]="statisticsMenu">Statistics ▾</button>
|
||||||
<mat-menu #organizeMenu=matMenu>
|
<mat-menu #organizeMenu=matMenu>
|
||||||
<button mat-menu-item routerLink="taskgroups/" aria-label="Task groups">Taskgroups</button>
|
<button mat-menu-item routerLink="taskgroups/" aria-label="Task groups">Taskgroups</button>
|
||||||
<button mat-menu-item routerLink="scheduler/" aria-label="Task groups">Scheduler</button>
|
<button mat-menu-item routerLink="scheduler/" aria-label="Task groups">Scheduler</button>
|
||||||
@ -10,6 +11,10 @@
|
|||||||
<button mat-menu-item routerLink="overdue/" aria-label="Overdue Tasks">Overdue Tasks</button>
|
<button mat-menu-item routerLink="overdue/" aria-label="Overdue Tasks">Overdue Tasks</button>
|
||||||
<button mat-menu-item routerLink="reschedule/" aria-label="Missed Schedules">Missed Schedules</button>
|
<button mat-menu-item routerLink="reschedule/" aria-label="Missed Schedules">Missed Schedules</button>
|
||||||
</mat-menu>
|
</mat-menu>
|
||||||
|
|
||||||
|
<mat-menu #statisticsMenu=matMenu>
|
||||||
|
<button mat-menu-item routerLink="statistics/taskgroup-activity">Activity</button>
|
||||||
|
</mat-menu>
|
||||||
<span class="example-spacer"></span>
|
<span class="example-spacer"></span>
|
||||||
|
|
||||||
|
|
||||||
|
@ -80,6 +80,9 @@ import { DateTimePickerComponent } from './date-time-picker/date-time-picker.com
|
|||||||
import {MatSliderModule} from "@angular/material/slider";
|
import {MatSliderModule} from "@angular/material/slider";
|
||||||
import {MatLegacySliderModule} from "@angular/material/legacy-slider";
|
import {MatLegacySliderModule} from "@angular/material/legacy-slider";
|
||||||
import { DraggableSchedulerComponent } from './schedules/draggable-scheduler/draggable-scheduler.component';
|
import { DraggableSchedulerComponent } from './schedules/draggable-scheduler/draggable-scheduler.component';
|
||||||
|
import { TaskgroupActivityComponent } from './statistics/taskgroup-activity/taskgroup-activity.component';
|
||||||
|
import {NgxSliderModule} from "ngx-slider-v2";
|
||||||
|
import {NgApexchartsModule} from "ng-apexcharts";
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
AppComponent,
|
AppComponent,
|
||||||
@ -120,6 +123,7 @@ import { DraggableSchedulerComponent } from './schedules/draggable-scheduler/dra
|
|||||||
AdvancedSchedulerComponent,
|
AdvancedSchedulerComponent,
|
||||||
DateTimePickerComponent,
|
DateTimePickerComponent,
|
||||||
DraggableSchedulerComponent,
|
DraggableSchedulerComponent,
|
||||||
|
TaskgroupActivityComponent,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
@ -158,7 +162,9 @@ import { DraggableSchedulerComponent } from './schedules/draggable-scheduler/dra
|
|||||||
MatTreeModule,
|
MatTreeModule,
|
||||||
MatAutocompleteModule,
|
MatAutocompleteModule,
|
||||||
NgxMaterialTimepickerModule,
|
NgxMaterialTimepickerModule,
|
||||||
MatSliderModule
|
MatSliderModule,
|
||||||
|
NgxSliderModule,
|
||||||
|
NgApexchartsModule,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
HttpClientModule,
|
HttpClientModule,
|
||||||
|
@ -0,0 +1,66 @@
|
|||||||
|
.container {
|
||||||
|
margin: 20px auto;
|
||||||
|
width: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spacer {
|
||||||
|
margin-bottom: 2.5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@media screen and (max-width: 600px) {
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
margin: 20px 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#date-range-selector {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep {
|
||||||
|
.custom-slider .ngx-slider .ngx-slider-bar {
|
||||||
|
background: #ffe4d1;
|
||||||
|
height: 2px;
|
||||||
|
}
|
||||||
|
.custom-slider .ngx-slider .ngx-slider-selection {
|
||||||
|
background: orange;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-slider .ngx-slider .ngx-slider-pointer {
|
||||||
|
width: 8px;
|
||||||
|
height: 16px;
|
||||||
|
top: auto; /* to remove the default positioning */
|
||||||
|
bottom: 0;
|
||||||
|
background-color: #333;
|
||||||
|
border-top-left-radius: 3px;
|
||||||
|
border-top-right-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-slider .ngx-slider .ngx-slider-pointer:after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-slider .ngx-slider .ngx-slider-bubble {
|
||||||
|
bottom: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-slider .ngx-slider .ngx-slider-limit {
|
||||||
|
font-weight: bold;
|
||||||
|
color: orange;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-slider .ngx-slider .ngx-slider-tick {
|
||||||
|
width: 1px;
|
||||||
|
height: 10px;
|
||||||
|
margin-left: 4px;
|
||||||
|
border-radius: 0;
|
||||||
|
background: #ffe4d1;
|
||||||
|
top: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-slider .ngx-slider .ngx-slider-tick.ngx-slider-selected {
|
||||||
|
background: orange;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
<div class="container">
|
||||||
|
<app-navigation-link-list #navLinkList [navigationLinks]="defaultNavigationLinkPath"></app-navigation-link-list>
|
||||||
|
<mat-form-field style="width: 90%">
|
||||||
|
<mat-label>Toppings</mat-label>
|
||||||
|
<mat-select [(ngModel)]="selectedTaskgroupPath" (ngModelChange)="updateSerieSelection()">
|
||||||
|
<mat-option *ngFor="let topping of taskgroupPaths" [value]="topping">{{topping.taskgroupPath}}</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field style="width: 10%;">
|
||||||
|
<mat-label>Toppings</mat-label>
|
||||||
|
<mat-select [(ngModel)]="selectedChartype" (ngModelChange)="updateSerieSelection()">
|
||||||
|
<mat-option *ngFor="let topping of availableChartTypes" [value]="topping">{{topping}}</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<div style="text-align:center">
|
||||||
|
<apx-chart
|
||||||
|
[series]="chartOptions.series!"
|
||||||
|
[chart]="chartOptions.chart!"
|
||||||
|
[xaxis]="chartOptions.xaxis!"
|
||||||
|
[title]="chartOptions.title!"
|
||||||
|
></apx-chart>
|
||||||
|
</div>
|
||||||
|
<div class="custom-slider">
|
||||||
|
<ngx-slider class="ngx-slider" [options]="options" [formControl]="sliderControl" (userChangeStart)="onUserChangeStart($event)"
|
||||||
|
(userChangeEnd)="onUserChangeStop($event)" (userChange)="onUserChange($event)"></ngx-slider>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,21 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { TaskgroupActivityComponent } from './taskgroup-activity.component';
|
||||||
|
|
||||||
|
describe('TaskgroupActivityComponent', () => {
|
||||||
|
let component: TaskgroupActivityComponent;
|
||||||
|
let fixture: ComponentFixture<TaskgroupActivityComponent>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [TaskgroupActivityComponent]
|
||||||
|
});
|
||||||
|
fixture = TestBed.createComponent(TaskgroupActivityComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,156 @@
|
|||||||
|
import {Component, OnInit, ViewChild} from '@angular/core';
|
||||||
|
import {NavigationLink} from "../../navigation-link-list/navigation-link-list.component";
|
||||||
|
import * as moment from "moment";
|
||||||
|
import {ChangeContext, LabelType, Options} from "ngx-slider-v2";
|
||||||
|
import {
|
||||||
|
ChartComponent,
|
||||||
|
ApexAxisChartSeries,
|
||||||
|
ApexChart,
|
||||||
|
ApexXAxis,
|
||||||
|
ApexTitleSubtitle, ChartType
|
||||||
|
} from "ng-apexcharts";
|
||||||
|
import {timeInterval} from "rxjs";
|
||||||
|
import {FormControl} from "@angular/forms";
|
||||||
|
import {HistoryService, TaskgroupPathInfo, TaskgroupService} from "../../../api";
|
||||||
|
|
||||||
|
export type ChartOptions = {
|
||||||
|
series: ApexAxisChartSeries;
|
||||||
|
chart: ApexChart;
|
||||||
|
xaxis: ApexXAxis;
|
||||||
|
title: ApexTitleSubtitle;
|
||||||
|
};
|
||||||
|
@Component({
|
||||||
|
selector: 'app-taskgroup-activity',
|
||||||
|
templateUrl: './taskgroup-activity.component.html',
|
||||||
|
styleUrls: ['./taskgroup-activity.component.css']
|
||||||
|
})
|
||||||
|
export class TaskgroupActivityComponent implements OnInit{
|
||||||
|
defaultNavigationLinkPath: NavigationLink[] = [
|
||||||
|
{
|
||||||
|
linkText: 'Dashboard',
|
||||||
|
routerLink: ['/']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
linkText: 'Statistics',
|
||||||
|
routerLink: []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
linkText: 'Taskgroup Activity',
|
||||||
|
routerLink: ['/statistics/taskgroup-activity']
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
selectedChartype: string = "bar";
|
||||||
|
availableChartTypes: string[] = ["bar", "line", "area"]
|
||||||
|
selectedTaskgroupPath: TaskgroupPathInfo | undefined
|
||||||
|
taskgroupPaths: TaskgroupPathInfo[] = []
|
||||||
|
sliderControl: FormControl = new FormControl()
|
||||||
|
dateRange: Date[] = this.createDateRange();
|
||||||
|
selectedDateRange: Date[] = this.dateRange;
|
||||||
|
value: number = this.dateRange[0].getTime();
|
||||||
|
options: Options = {
|
||||||
|
stepsArray: this.dateRange.map((date: Date) => {
|
||||||
|
return { value: date.getTime() };
|
||||||
|
}),
|
||||||
|
translate: (value: number, label: LabelType): string => {
|
||||||
|
return new Date(value).toDateString();
|
||||||
|
},
|
||||||
|
floor: 0,
|
||||||
|
ceil: this.dateRange.length,
|
||||||
|
};
|
||||||
|
|
||||||
|
@ViewChild("chart") chart?: ChartComponent;
|
||||||
|
public chartOptions: Partial<ChartOptions> = this.generateChartOptions()
|
||||||
|
|
||||||
|
constructor(private taskgroupService: TaskgroupService,
|
||||||
|
private historyService: HistoryService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.taskgroupService.taskgroupsPathGet().subscribe({
|
||||||
|
next: resp => {
|
||||||
|
this.taskgroupPaths = resp;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
createDateRange(): Date[] {
|
||||||
|
const dates: Date[] = [];
|
||||||
|
for (let i: number = 1; i <= 31; i++) {
|
||||||
|
|
||||||
|
dates.push(moment().subtract(30-i, 'd').toDate());
|
||||||
|
}
|
||||||
|
this.sliderControl.setValue([dates[0], dates[dates.length-1]])
|
||||||
|
return dates;
|
||||||
|
}
|
||||||
|
|
||||||
|
createDateRangeBetween(minValue: moment.Moment, maxValue: moment.Moment) {
|
||||||
|
const dates: Date[] = [];
|
||||||
|
const numberDays = maxValue.diff(minValue, 'days');
|
||||||
|
for(let i=0; i<=numberDays; i++) {
|
||||||
|
dates.push(minValue.add(i, 'd').toDate());
|
||||||
|
}
|
||||||
|
return dates;
|
||||||
|
}
|
||||||
|
|
||||||
|
onUserChangeStart(changeContext: ChangeContext) {
|
||||||
|
|
||||||
|
//console.log("onUserChangeStart" + new Date(changeContext.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
onUserChangeStop(changeContext: ChangeContext) {
|
||||||
|
//console.log("onUserChangeStop" + new Date(changeContext.highValue!))
|
||||||
|
}
|
||||||
|
|
||||||
|
onUserChange(changeContext: ChangeContext) {
|
||||||
|
const dateRange = this.createDateRangeBetween(moment(changeContext.value), moment(changeContext.highValue!))
|
||||||
|
this.chartOptions = this.generateChartOptions()
|
||||||
|
}
|
||||||
|
|
||||||
|
generateChartOptions(): Partial<ChartOptions> {
|
||||||
|
|
||||||
|
return {
|
||||||
|
series: this.generateSeries(),
|
||||||
|
chart: {
|
||||||
|
height: 350,
|
||||||
|
type: this.selectedChartype as ChartType,
|
||||||
|
stacked: true
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
text: ""
|
||||||
|
},
|
||||||
|
xaxis: {
|
||||||
|
categories: this.selectedDateRange.map(date => date.toDateString())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
generateSeries() : ApexAxisChartSeries {
|
||||||
|
const series: ApexAxisChartSeries = []
|
||||||
|
this.selectedTaskgroupPath?.directChildren.forEach(taskgroup => {
|
||||||
|
this.historyService.statisticsTaskgroupActivityTaskgroupIDStartingDateEndingDateIncludeSubTaskgroupsGet(
|
||||||
|
taskgroup.taskgroupID,
|
||||||
|
moment(this.selectedDateRange[0]).format("YYYY-MM-DD"),
|
||||||
|
moment(this.selectedDateRange[this.selectedDateRange.length-1]).format("YYYY-MM-DD"), true
|
||||||
|
).subscribe({
|
||||||
|
next: resp => {
|
||||||
|
series.push(
|
||||||
|
{
|
||||||
|
name: taskgroup.taskgroupName,
|
||||||
|
data: resp.map(dailyActivityInfo => dailyActivityInfo.activeMinutes)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
return series;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSerieSelection() {
|
||||||
|
this.chartOptions = this.generateChartOptions()
|
||||||
|
}
|
||||||
|
}
|
100
openapi.yaml
100
openapi.yaml
@ -883,6 +883,23 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
type: object
|
type: object
|
||||||
$ref: "#/components/schemas/SimpleStatusResponse"
|
$ref: "#/components/schemas/SimpleStatusResponse"
|
||||||
|
/taskgroups/path:
|
||||||
|
get:
|
||||||
|
security:
|
||||||
|
- API_TOKEN: []
|
||||||
|
tags:
|
||||||
|
- taskgroup
|
||||||
|
summary: lists all taskgrouppaths
|
||||||
|
description: lists all taskgroup-paths
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Operation successfull
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/TaskgroupPathInfo'
|
||||||
/tasks/all/{scope}/{detailed}:
|
/tasks/all/{scope}/{detailed}:
|
||||||
get:
|
get:
|
||||||
security:
|
security:
|
||||||
@ -1888,6 +1905,63 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
type: object
|
type: object
|
||||||
$ref: "#/components/schemas/SimpleStatusResponse"
|
$ref: "#/components/schemas/SimpleStatusResponse"
|
||||||
|
/statistics/taskgroup-activity/{taskgroupID}/{startingDate}/{endingDate}/{includeSubTaskgroups}:
|
||||||
|
get:
|
||||||
|
security:
|
||||||
|
- API_TOKEN: []
|
||||||
|
tags:
|
||||||
|
- history
|
||||||
|
parameters:
|
||||||
|
- name: taskgroupID
|
||||||
|
in: path
|
||||||
|
description: internal id of taskgroup
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: number
|
||||||
|
example: 1
|
||||||
|
- name: startingDate
|
||||||
|
in: path
|
||||||
|
description: starting date
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: date
|
||||||
|
- name: endingDate
|
||||||
|
in: path
|
||||||
|
description: starting date
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: date
|
||||||
|
- name: includeSubTaskgroups
|
||||||
|
in: path
|
||||||
|
description: determines whether to include subtaskgroups or not
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: boolean
|
||||||
|
example: false
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Operation successfull
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/TaskgroupActivityInfo'
|
||||||
|
403:
|
||||||
|
description: No permission
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/SimpleStatusResponse'
|
||||||
|
404:
|
||||||
|
description: Schedule not found
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/SimpleStatusResponse'
|
||||||
|
|
||||||
|
|
||||||
components:
|
components:
|
||||||
securitySchemes:
|
securitySchemes:
|
||||||
@ -2530,3 +2604,29 @@ components:
|
|||||||
scheduleStopTime:
|
scheduleStopTime:
|
||||||
type: string
|
type: string
|
||||||
format: date
|
format: date
|
||||||
|
TaskgroupPathInfo:
|
||||||
|
required:
|
||||||
|
- taskgroupPath
|
||||||
|
- directChildren
|
||||||
|
additionalProperties: false
|
||||||
|
properties:
|
||||||
|
taskgroupPath:
|
||||||
|
type: string
|
||||||
|
description: TaskgroupPath
|
||||||
|
directChildren:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/TaskgroupEntityInfo'
|
||||||
|
TaskgroupActivityInfo:
|
||||||
|
required:
|
||||||
|
- date
|
||||||
|
- activeMinutes
|
||||||
|
additionalProperties: false
|
||||||
|
properties:
|
||||||
|
date:
|
||||||
|
type: string
|
||||||
|
format: date
|
||||||
|
activeMinutes:
|
||||||
|
type: number
|
||||||
|
description: Number of minutes the task was active
|
||||||
|
example: 122
|
Loading…
Reference in New Issue
Block a user