issue-106 #107
@ -13,6 +13,7 @@ import org.springframework.web.bind.annotation.*;
|
|||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@CrossOrigin(origins = "*", maxAge = 3600)
|
@CrossOrigin(origins = "*", maxAge = 3600)
|
||||||
@ -72,7 +73,11 @@ public class TaskController {
|
|||||||
return ResponseEntity.status(404).body(new SimpleStatusResponse("failed"));
|
return ResponseEntity.status(404).body(new SimpleStatusResponse("failed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResponseEntity.ok(taskgroupPermissionResult.getResult().getTasks().stream().map(TaskEntityInfo::new));
|
List<Task> tasks = new ArrayList<>();
|
||||||
|
for(Task task : taskgroupPermissionResult.getResult().getTasks()) {
|
||||||
|
if(task.getParent() == null) tasks.add(task);
|
||||||
|
}
|
||||||
|
return ResponseEntity.ok(tasks.stream().map(TaskEntityInfo::new));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/tasks/{taskgroupID}")
|
@PutMapping("/tasks/{taskgroupID}")
|
||||||
@ -164,4 +169,42 @@ public class TaskController {
|
|||||||
taskService.finishTask(taskPermissionResult.getResult());
|
taskService.finishTask(taskPermissionResult.getResult());
|
||||||
return ResponseEntity.ok(new SimpleStatusResponse("success"));
|
return ResponseEntity.ok(new SimpleStatusResponse("success"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PutMapping("/tasks/{taskID}/subtasks")
|
||||||
|
public ResponseEntity<?> onCreateSubTask(@PathVariable long taskID, @Valid @RequestBody TaskFieldInfo taskFieldInfo) {
|
||||||
|
var taskPermissionResult = taskService.getTaskPermissions(taskID, SecurityContextHolder.getContext().getAuthentication().getName());
|
||||||
|
if(taskPermissionResult.hasIssue()) {
|
||||||
|
return taskPermissionResult.mapToResponseEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
var serviceResult = taskService.createSubTask(taskPermissionResult.getResult(), taskFieldInfo);
|
||||||
|
if(serviceResult.hasIssue()) {
|
||||||
|
return serviceResult.mapToResponseEntity();
|
||||||
|
} else {
|
||||||
|
return ResponseEntity.ok(new TaskEntityInfo(serviceResult.getResult()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/tasks/{taskID}/subtasks")
|
||||||
|
public ResponseEntity<?> onListSubtasks(@PathVariable long taskID) {
|
||||||
|
var taskPermissionResult = taskService.getTaskPermissions(taskID, SecurityContextHolder.getContext().getAuthentication().getName());
|
||||||
|
if(taskPermissionResult.hasIssue()) {
|
||||||
|
return taskPermissionResult.mapToResponseEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
Collection<Task> subtasks = taskPermissionResult.getResult().getSubtasks();
|
||||||
|
return ResponseEntity.ok(subtasks.stream().map(TaskEntityInfo::new).toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/tasks/{taskID}/subtasks")
|
||||||
|
public ResponseEntity<?> onClearSubtasks(@PathVariable long taskID) {
|
||||||
|
var taskPermissionResult = taskService.getTaskPermissions(taskID, SecurityContextHolder.getContext().getAuthentication().getName());
|
||||||
|
if(taskPermissionResult.hasIssue()) {
|
||||||
|
return taskPermissionResult.mapToResponseEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
ServiceExitCode result = taskService.clearSubTasks(taskPermissionResult.getResult());
|
||||||
|
return result.mapToResponseEntity();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,10 +31,13 @@ public class RecursiveTaskgroupInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for(Task task : taskgroup.getActiveTasks()) {
|
for(Task task : taskgroup.getActiveTasks()) {
|
||||||
this.activeTasks.add(new TaskOverviewInfo(task));
|
if(task.getParent() == null) {
|
||||||
if(task.getDeadline() != null && task.getDeadline().isBefore(LocalDate.now())) {
|
this.activeTasks.add(new TaskOverviewInfo(task));
|
||||||
this.hasOverdueTask = true;
|
if(task.getDeadline() != null && task.getDeadline().isBefore(LocalDate.now())) {
|
||||||
|
this.hasOverdueTask = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
this.amountActiveTasks = taskgroup.getAmountOfActiveTasks();
|
this.amountActiveTasks = taskgroup.getAmountOfActiveTasks();
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,8 @@ public class TaskEntityInfo {
|
|||||||
|
|
||||||
private boolean hasPlannedSchedules;
|
private boolean hasPlannedSchedules;
|
||||||
private boolean hasTaskSerie;
|
private boolean hasTaskSerie;
|
||||||
|
private boolean hasSubtasks;
|
||||||
|
private boolean hasParent;
|
||||||
|
|
||||||
public TaskEntityInfo(Task task) {
|
public TaskEntityInfo(Task task) {
|
||||||
this.taskID = task.getTaskID();
|
this.taskID = task.getTaskID();
|
||||||
@ -49,6 +51,8 @@ public class TaskEntityInfo {
|
|||||||
this.hasActiveSchedules = task.hasActiveSchedule();
|
this.hasActiveSchedules = task.hasActiveSchedule();
|
||||||
this.hasPlannedSchedules = task.hasPlannedSchedules();
|
this.hasPlannedSchedules = task.hasPlannedSchedules();
|
||||||
this.hasTaskSerie = task.getTaskSerieItem() != null;
|
this.hasTaskSerie = task.getTaskSerieItem() != null;
|
||||||
|
this.hasSubtasks = task.getSubtasks() != null && !task.getSubtasks().isEmpty();
|
||||||
|
this.hasParent = task.getParent() != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getTaskID() {
|
public long getTaskID() {
|
||||||
@ -146,4 +150,20 @@ public class TaskEntityInfo {
|
|||||||
public void setHasTaskSerie(boolean hasTaskSerie) {
|
public void setHasTaskSerie(boolean hasTaskSerie) {
|
||||||
this.hasTaskSerie = hasTaskSerie;
|
this.hasTaskSerie = hasTaskSerie;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isHasSubtasks() {
|
||||||
|
return hasSubtasks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHasSubtasks(boolean hasSubtasks) {
|
||||||
|
this.hasSubtasks = hasSubtasks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isHasParent() {
|
||||||
|
return hasParent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHasParent(boolean hasParent) {
|
||||||
|
this.hasParent = hasParent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ public class TaskOverviewInfo {
|
|||||||
private boolean overdue;
|
private boolean overdue;
|
||||||
|
|
||||||
private boolean finishable;
|
private boolean finishable;
|
||||||
|
private boolean hasSubtasks;
|
||||||
|
|
||||||
public TaskOverviewInfo(Task task) {
|
public TaskOverviewInfo(Task task) {
|
||||||
this.taskID = task.getTaskID();
|
this.taskID = task.getTaskID();
|
||||||
@ -33,6 +34,7 @@ public class TaskOverviewInfo {
|
|||||||
this.overdue = LocalDate.now().isAfter(task.getDeadline());
|
this.overdue = LocalDate.now().isAfter(task.getDeadline());
|
||||||
}
|
}
|
||||||
this.finishable = task.isFinishable();
|
this.finishable = task.isFinishable();
|
||||||
|
this.hasSubtasks = !task.getSubtasks().isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getTaskID() {
|
public long getTaskID() {
|
||||||
@ -90,4 +92,12 @@ public class TaskOverviewInfo {
|
|||||||
public void setFinishable(boolean finishable) {
|
public void setFinishable(boolean finishable) {
|
||||||
this.finishable = finishable;
|
this.finishable = finishable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isHasSubtasks() {
|
||||||
|
return hasSubtasks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHasSubtasks(boolean hasSubtasks) {
|
||||||
|
this.hasSubtasks = hasSubtasks;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ import java.time.LocalDate;
|
|||||||
public class TaskRepeatDayInfo {
|
public class TaskRepeatDayInfo {
|
||||||
|
|
||||||
private int offset;
|
private int offset;
|
||||||
private DeadlineStrategy deadlineStrategy;
|
|
||||||
|
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX")
|
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX")
|
||||||
private LocalDate endingDate;
|
private LocalDate endingDate;
|
||||||
@ -20,14 +19,6 @@ public class TaskRepeatDayInfo {
|
|||||||
this.offset = offset;
|
this.offset = offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DeadlineStrategy getDeadlineStrategy() {
|
|
||||||
return deadlineStrategy;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDeadlineStrategy(DeadlineStrategy deadlineStrategy) {
|
|
||||||
this.deadlineStrategy = deadlineStrategy;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LocalDate getEndingDate() {
|
public LocalDate getEndingDate() {
|
||||||
return endingDate;
|
return endingDate;
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import java.time.DayOfWeek;
|
|||||||
public class TaskRepeatWeekDayInfo {
|
public class TaskRepeatWeekDayInfo {
|
||||||
private int offset;
|
private int offset;
|
||||||
private long taskID;
|
private long taskID;
|
||||||
|
private DayOfWeek dayOfWeek;
|
||||||
|
|
||||||
public int getOffset() {
|
public int getOffset() {
|
||||||
return offset;
|
return offset;
|
||||||
@ -24,4 +25,12 @@ public class TaskRepeatWeekDayInfo {
|
|||||||
public void setTaskID(long taskID) {
|
public void setTaskID(long taskID) {
|
||||||
this.taskID = taskID;
|
this.taskID = taskID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DayOfWeek getDayOfWeek() {
|
||||||
|
return dayOfWeek;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDayOfWeek(DayOfWeek dayOfWeek) {
|
||||||
|
this.dayOfWeek = dayOfWeek;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,16 +2,19 @@ package core.api.models.timemanager.tasks.repeatinginfo;
|
|||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
import org.hibernate.validator.constraints.Length;
|
import org.hibernate.validator.constraints.Length;
|
||||||
|
import util.Tupel;
|
||||||
|
|
||||||
import javax.validation.constraints.Size;
|
import javax.validation.constraints.Size;
|
||||||
|
import java.time.DayOfWeek;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class TaskRepeatWeekInfo {
|
public class TaskRepeatWeekInfo {
|
||||||
|
|
||||||
@Size(min = 1, max = 7)
|
@Size(min = 1, max = 7)
|
||||||
private List<TaskRepeatWeekDayInfo> weekDayInfos;
|
private List<TaskRepeatWeekDayInfo> weekDayInfos;
|
||||||
private DeadlineStrategy deadlineStrategy;
|
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX")
|
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX")
|
||||||
private LocalDate endDate;
|
private LocalDate endDate;
|
||||||
|
|
||||||
@ -22,15 +25,6 @@ public class TaskRepeatWeekInfo {
|
|||||||
public void setWeekDayInfos(List<TaskRepeatWeekDayInfo> weekDayInfos) {
|
public void setWeekDayInfos(List<TaskRepeatWeekDayInfo> weekDayInfos) {
|
||||||
this.weekDayInfos = weekDayInfos;
|
this.weekDayInfos = weekDayInfos;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DeadlineStrategy getDeadlineStrategy() {
|
|
||||||
return deadlineStrategy;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDeadlineStrategy(DeadlineStrategy deadlineStrategy) {
|
|
||||||
this.deadlineStrategy = deadlineStrategy;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LocalDate getEndDate() {
|
public LocalDate getEndDate() {
|
||||||
return endDate;
|
return endDate;
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
package core.entities.timemanager;
|
package core.entities.timemanager;
|
||||||
|
|
||||||
import core.api.models.timemanager.tasks.TaskFieldInfo;
|
import core.api.models.timemanager.tasks.TaskFieldInfo;
|
||||||
|
import core.api.models.timemanager.tasks.repeatinginfo.DeadlineStrategy;
|
||||||
|
import util.Tripel;
|
||||||
|
import util.Tupel;
|
||||||
|
|
||||||
import javax.persistence.*;
|
import javax.persistence.*;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.ArrayList;
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.List;
|
import java.util.*;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "tasks")
|
@Table(name = "tasks")
|
||||||
@ -33,6 +34,13 @@ public class Task {
|
|||||||
@OneToOne(mappedBy = "task", cascade = CascadeType.ALL, orphanRemoval = true)
|
@OneToOne(mappedBy = "task", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||||
private TaskSerieItem taskSerieItem;
|
private TaskSerieItem taskSerieItem;
|
||||||
|
|
||||||
|
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
|
||||||
|
private Set<Task> subtasks;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "parent")
|
||||||
|
private Task parent;
|
||||||
|
|
||||||
public Task() {
|
public Task() {
|
||||||
this.basicTaskSchedules = new ArrayList<>();
|
this.basicTaskSchedules = new ArrayList<>();
|
||||||
}
|
}
|
||||||
@ -47,14 +55,52 @@ public class Task {
|
|||||||
this.finishable = taskFieldInfo.isFinishable();
|
this.finishable = taskFieldInfo.isFinishable();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Task cloneTask(Task task) {
|
public Tripel<Task, Collection<Task>, Collection<AbstractSchedule>> cloneTask() {
|
||||||
|
Collection<Task> clonedTasks = new ArrayList<>();
|
||||||
|
Collection<AbstractSchedule> clonedSchedules = new ArrayList<>();
|
||||||
|
|
||||||
Task clonedTask = new Task();
|
Task clonedTask = new Task();
|
||||||
clonedTask.setTaskgroup(task.getTaskgroup());
|
clonedTasks.add(clonedTask);
|
||||||
clonedTask.setTaskName(task.taskName);
|
|
||||||
clonedTask.setEta(task.eta);
|
clonedTask.setTaskgroup(this.getTaskgroup());
|
||||||
clonedTask.setFinished(false);
|
clonedTask.setTaskName(this.taskName);
|
||||||
clonedTask.setFinishable(task.finishable);
|
clonedTask.setEta(this.eta);
|
||||||
return clonedTask;
|
clonedTask.setFinished(this.finished);
|
||||||
|
clonedTask.setFinishable(this.finishable);
|
||||||
|
clonedTask.setStartDate(this.startDate);
|
||||||
|
clonedTask.setDeadline(this.deadline);
|
||||||
|
|
||||||
|
for(AbstractSchedule abstractSchedule : this.basicTaskSchedules) {
|
||||||
|
AbstractSchedule clonedSchedule = abstractSchedule.cloneSchedule();
|
||||||
|
clonedSchedules.add(clonedSchedule);
|
||||||
|
clonedTask.getBasicTaskSchedules().clear();
|
||||||
|
clonedTask.getBasicTaskSchedules().add(clonedSchedule);
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<Task> clonedSubtasks = new HashSet<>();
|
||||||
|
for(Task task : this.subtasks) {
|
||||||
|
Tripel<Task, Collection<Task>, Collection<AbstractSchedule>> clonedSubtask = task.cloneTask();
|
||||||
|
clonedSubtask.getValue00().setParent(clonedTask);
|
||||||
|
clonedSubtasks.add(clonedSubtask.getValue00());
|
||||||
|
|
||||||
|
clonedTasks.addAll(clonedSubtask.getValue01());
|
||||||
|
clonedSchedules.addAll(clonedSubtask.getValue02());
|
||||||
|
}
|
||||||
|
clonedTask.setSubtasks(clonedSubtasks);
|
||||||
|
return new Tripel<>(clonedTask, clonedTasks, clonedSchedules);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void shiftTask(long offset) {
|
||||||
|
this.setStartDate(this.getStartDate().plusDays(offset));
|
||||||
|
this.setDeadline(this.getDeadline().plusDays(offset));
|
||||||
|
|
||||||
|
for(AbstractSchedule abstractSchedule : this.basicTaskSchedules) {
|
||||||
|
abstractSchedule.shiftSchedule(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(Task subtask: this.subtasks) {
|
||||||
|
subtask.shiftTask(offset);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -149,6 +195,27 @@ public class Task {
|
|||||||
this.basicTaskSchedules = basicTaskSchedules;
|
this.basicTaskSchedules = basicTaskSchedules;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addSubtask(Task subtask) {
|
||||||
|
subtask.setParent(this);
|
||||||
|
this.subtasks.add(subtask);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Task> getSubtasks() {
|
||||||
|
return subtasks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSubtasks(Set<Task> subtasks) {
|
||||||
|
this.subtasks = subtasks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task getParent() {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParent(Task parent) {
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
|
@ -39,4 +39,9 @@ public class TaskSerie {
|
|||||||
this.tasks.add(taskSerieItem);
|
this.tasks.add(taskSerieItem);
|
||||||
return taskSerieItem;
|
return taskSerieItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addItem(TaskSerieItem taskSerieItem) {
|
||||||
|
this.tasks.add(taskSerieItem);
|
||||||
|
taskSerieItem.setTaskSerie(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,12 @@ public class TaskSerieItem {
|
|||||||
public TaskSerieItem() {
|
public TaskSerieItem() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TaskSerieItem(Task task, int itemIndex) {
|
||||||
|
this.taskSerie = null;
|
||||||
|
this.seriesIndex = itemIndex;
|
||||||
|
this.task = task;
|
||||||
|
}
|
||||||
|
|
||||||
public long getItemID() {
|
public long getItemID() {
|
||||||
return itemID;
|
return itemID;
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,12 @@ package core.repositories.timemanager;
|
|||||||
|
|
||||||
import core.entities.timemanager.AbstractSchedule;
|
import core.entities.timemanager.AbstractSchedule;
|
||||||
import core.entities.timemanager.Taskgroup;
|
import core.entities.timemanager.Taskgroup;
|
||||||
|
import org.springframework.data.jpa.repository.Modifying;
|
||||||
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 javax.transaction.Transactional;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -20,4 +22,9 @@ public interface ScheduleRepository extends CrudRepository<AbstractSchedule, Lon
|
|||||||
@Query(value = "SELECT s FROM AbstractSchedule s WHERE s.task.taskgroup.user.username = ?1 AND s.startTime is NOT NULL and s.stopTime is NULL")
|
@Query(value = "SELECT s FROM AbstractSchedule s WHERE s.task.taskgroup.user.username = ?1 AND s.startTime is NOT NULL and s.stopTime is NULL")
|
||||||
Optional<AbstractSchedule> getActiveScheduleOfUser(String username);
|
Optional<AbstractSchedule> getActiveScheduleOfUser(String username);
|
||||||
|
|
||||||
|
|
||||||
|
@Modifying
|
||||||
|
@Transactional
|
||||||
|
@Query(value = "DELETE FROM AbstractSchedule a WHERE a.task IN (SELECT t FROM Task t WHERE t.taskgroup = ?1)")
|
||||||
|
void deleteByTaskgroup(Taskgroup taskgroup);
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ public interface TaskRepository extends CrudRepository<Task, Long> {
|
|||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@Modifying
|
@Modifying
|
||||||
|
@Query(value = "DELETE FROM Task t WHERE t.taskID = ?1")
|
||||||
void deleteByTaskID(long taskID);
|
void deleteByTaskID(long taskID);
|
||||||
|
|
||||||
@Query(value = "SELECT t FROM Task t WHERE t.taskgroup.user.username = ?1 AND t.deadline is NOT NULL AND t.deadline < ?2 AND t.finished = FALSE")
|
@Query(value = "SELECT t FROM Task t WHERE t.taskgroup.user.username = ?1 AND t.deadline is NOT NULL AND t.deadline < ?2 AND t.finished = FALSE")
|
||||||
@ -35,4 +36,17 @@ public interface TaskRepository extends CrudRepository<Task, Long> {
|
|||||||
|
|
||||||
@Query(value = "SELECT t FROM Task t WHERE t.taskgroup.user.username = ?1 AND (t.startDate IS NULL OR t.startDate <= ?2) AND t.finished = FALSE")
|
@Query(value = "SELECT t FROM Task t WHERE t.taskgroup.user.username = ?1 AND (t.startDate IS NULL OR t.startDate <= ?2) AND t.finished = FALSE")
|
||||||
List<Task> findAllActive(String username, LocalDate now);
|
List<Task> findAllActive(String username, LocalDate now);
|
||||||
|
|
||||||
|
@Query(value = "SELECT t FROM Task t WHERE t.taskgroup = ?1")
|
||||||
|
List<Task> findAllByTaskgroup(Taskgroup taskgroup);
|
||||||
|
|
||||||
|
@Modifying
|
||||||
|
@Transactional
|
||||||
|
@Query(value = "UPDATE Task t SET t.parent = null WHERE t.taskgroup = ?1")
|
||||||
|
void deleteTaskHierarchyWhereTaskgroup(Taskgroup taskgroup);
|
||||||
|
|
||||||
|
@Modifying
|
||||||
|
@Transactional
|
||||||
|
@Query(value = "DELETE Task t WHERE t.parent = ?1")
|
||||||
|
void deleteTasksByParent(Task parentTask);
|
||||||
}
|
}
|
||||||
|
@ -7,10 +7,7 @@ import core.api.models.timemanager.taskSchedule.scheduleInfos.AdvancedScheduleIn
|
|||||||
import core.api.models.timemanager.taskSchedule.scheduleInfos.BasicScheduleFieldInfo;
|
import core.api.models.timemanager.taskSchedule.scheduleInfos.BasicScheduleFieldInfo;
|
||||||
import core.api.models.timemanager.taskSchedule.ForgottenScheduleInfo;
|
import core.api.models.timemanager.taskSchedule.ForgottenScheduleInfo;
|
||||||
import core.api.models.timemanager.taskSchedule.scheduleInfos.ScheduleInfo;
|
import core.api.models.timemanager.taskSchedule.scheduleInfos.ScheduleInfo;
|
||||||
import core.entities.timemanager.AbstractSchedule;
|
import core.entities.timemanager.*;
|
||||||
import core.entities.timemanager.AdvancedTaskSchedule;
|
|
||||||
import core.entities.timemanager.BasicTaskSchedule;
|
|
||||||
import core.entities.timemanager.Task;
|
|
||||||
import core.repositories.UserRepository;
|
import core.repositories.UserRepository;
|
||||||
import core.repositories.timemanager.AdvancedScheduleRepository;
|
import core.repositories.timemanager.AdvancedScheduleRepository;
|
||||||
import core.repositories.timemanager.ScheduleRepository;
|
import core.repositories.timemanager.ScheduleRepository;
|
||||||
@ -259,4 +256,8 @@ public class TaskScheduleService {
|
|||||||
schedule.setStopTime(schedule.getStartTime().plusMinutes(manualScheduleStopInfo.getDuration()));
|
schedule.setStopTime(schedule.getStartTime().plusMinutes(manualScheduleStopInfo.getDuration()));
|
||||||
scheduleRepository.save(schedule);
|
scheduleRepository.save(schedule);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void deleteSchedulesByTaskgroup(Taskgroup taskgroup) {
|
||||||
|
scheduleRepository.deleteByTaskgroup(taskgroup);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,14 +11,13 @@ import core.repositories.timemanager.TaskSerieItemRepository;
|
|||||||
import core.repositories.timemanager.TaskSeriesRepository;
|
import core.repositories.timemanager.TaskSeriesRepository;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import util.Tupel;
|
||||||
|
|
||||||
|
import java.time.DayOfWeek;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class TaskSeriesService {
|
public class TaskSeriesService {
|
||||||
@ -30,106 +29,103 @@ public class TaskSeriesService {
|
|||||||
|
|
||||||
|
|
||||||
public ServiceExitCode createTaskSeries(TaskRepeatWeekInfo taskRepeatInfo) {
|
public ServiceExitCode createTaskSeries(TaskRepeatWeekInfo taskRepeatInfo) {
|
||||||
List<Task> createdTasks = new ArrayList<>();
|
HashMap<Task, Integer> offsetMap = calcWeeklyOffsetMap(taskRepeatInfo);
|
||||||
|
|
||||||
TaskSerie taskSerie = new TaskSerie();
|
TaskSerie taskSerie = new TaskSerie();
|
||||||
List<AbstractSchedule> abstractSchedules = new ArrayList<>();
|
List<Task> clonedTasks = new ArrayList<>();
|
||||||
|
List<AbstractSchedule> clonedSchedules = new ArrayList<>();
|
||||||
|
int weekDayIndex = 0;
|
||||||
|
for(Map.Entry<Task, Integer> repeatingTaskInfo: offsetMap.entrySet()) {
|
||||||
|
Task rootTask = repeatingTaskInfo.getKey();
|
||||||
|
addRootSubTasksToTaskSerie(taskSerie, rootTask, weekDayIndex);
|
||||||
|
|
||||||
for(TaskRepeatWeekDayInfo taskRepeatDayInfo : taskRepeatInfo.getWeekDayInfos()) {
|
int itemIndex = weekDayIndex +1;
|
||||||
Optional<Task> task = taskRepository.findById(taskRepeatDayInfo.getTaskID());
|
Tupel<Collection<Task>, Collection<AbstractSchedule>> repeatingResult = repeatTask(rootTask, taskRepeatInfo.getEndDate(), offsetMap, taskSerie, itemIndex);
|
||||||
if(task.isEmpty()) return ServiceExitCode.MISSING_ENTITY;
|
|
||||||
|
|
||||||
TaskSerieItem rootItem = taskSerie.addTask(task.get());
|
clonedTasks.addAll(repeatingResult.getValue00());
|
||||||
task.get().setTaskSerieItem(rootItem);
|
clonedSchedules.addAll(repeatingResult.getValue01());
|
||||||
|
|
||||||
LocalDate currentTaskDate = task.get().getStartDate().plusDays(taskRepeatDayInfo.getOffset());
|
weekDayIndex++;
|
||||||
while(currentTaskDate.isBefore(taskRepeatInfo.getEndDate())) {
|
|
||||||
Task clonedTask = Task.cloneTask(task.get());
|
|
||||||
clonedTask.setStartDate(currentTaskDate);
|
|
||||||
|
|
||||||
TaskSerieItem taskSerieItem = taskSerie.addTask(clonedTask);
|
|
||||||
clonedTask.setTaskSerieItem(taskSerieItem);
|
|
||||||
createdTasks.add(clonedTask);
|
|
||||||
|
|
||||||
abstractSchedules.addAll(cloneSchedules(task.get(), clonedTask));
|
|
||||||
currentTaskDate = currentTaskDate.plusDays(taskRepeatDayInfo.getOffset());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
taskSerie.getTasks().sort(Comparator.comparing(o -> o.getTask().getStartDate()));
|
|
||||||
for(int i=0; i<taskSerie.getTasks().size(); i++) {
|
|
||||||
taskSerie.getTasks().get(i).setSeriesIndex(i+1);
|
|
||||||
if(taskRepeatInfo.getDeadlineStrategy() == DeadlineStrategy.DEADLINE_EQUAL_START) {
|
|
||||||
taskSerie.getTasks().get(i).getTask().setDeadline(taskSerie.getTasks().get(i).getTask().getStartDate());
|
|
||||||
} else {
|
|
||||||
if(i + 1 == taskSerie.getTasks().size()) {
|
|
||||||
int firstWeekDayIndex = i % taskRepeatInfo.getWeekDayInfos().size();
|
|
||||||
LocalDate reference_start = taskSerie.getTasks().get(firstWeekDayIndex).getTask().getStartDate();
|
|
||||||
LocalDate reference_deadline = taskSerie.getTasks().get(firstWeekDayIndex).getTask().getDeadline();
|
|
||||||
|
|
||||||
Duration duration = Duration.between(reference_start, reference_deadline);
|
|
||||||
long days = duration.toDays();
|
|
||||||
taskSerie.getTasks().get(i).getTask().setDeadline(taskSerie.getTasks().get(i).getTask().getStartDate().plusDays(days));
|
|
||||||
} else {
|
|
||||||
taskSerie.getTasks().get(i).getTask().setDeadline(taskSerie.getTasks().get(i+1).getTask().getStartDate().minusDays(1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
taskSeriesRepository.save(taskSerie);
|
taskSeriesRepository.save(taskSerie);
|
||||||
taskRepository.saveAll(createdTasks);
|
taskRepository.saveAll(clonedTasks);
|
||||||
taskSerieItemRepository.saveAll(taskSerie.getTasks());
|
taskSerieItemRepository.saveAll(taskSerie.getTasks());
|
||||||
scheduleRepository.saveAll(abstractSchedules);
|
scheduleRepository.saveAll(clonedSchedules);
|
||||||
|
|
||||||
return ServiceExitCode.OK;
|
return ServiceExitCode.OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Tupel<Collection<Task>, Collection<AbstractSchedule>> repeatTask(Task rootTask, LocalDate endingDate, HashMap<Task, Integer> offsetMap,TaskSerie taskSerie, int itemIndex) {
|
||||||
|
List<Task> clonedTasks = new ArrayList<>();
|
||||||
|
List<AbstractSchedule> clonedSchedules = new ArrayList<>();
|
||||||
|
|
||||||
|
LocalDate currentDate = rootTask.getStartDate().plusDays(offsetMap.get(rootTask));
|
||||||
|
while(currentDate.isBefore(endingDate)) {
|
||||||
|
var cloneResult = rootTask.cloneTask();
|
||||||
|
Task clonedRootTask = cloneResult.getValue00();
|
||||||
|
clonedTasks.addAll(cloneResult.getValue01());
|
||||||
|
clonedSchedules.addAll(cloneResult.getValue02());
|
||||||
|
|
||||||
|
for(Task clonedTask : cloneResult.getValue01()) {
|
||||||
|
TaskSerieItem item = new TaskSerieItem(clonedTask, itemIndex);
|
||||||
|
taskSerie.addItem(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
clonedRootTask.shiftTask(offsetMap.get(rootTask));
|
||||||
|
|
||||||
|
currentDate = currentDate.plusDays(offsetMap.get(rootTask));
|
||||||
|
itemIndex += offsetMap.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Tupel<>(clonedTasks, clonedSchedules);
|
||||||
|
}
|
||||||
|
|
||||||
|
private HashMap<Task, Integer> calcWeeklyOffsetMap(TaskRepeatWeekInfo weekInfo) throws NoSuchElementException {
|
||||||
|
HashMap<Task,Integer> offsetMap = new HashMap<>();
|
||||||
|
weekInfo.getWeekDayInfos().sort(Comparator.comparing(TaskRepeatWeekDayInfo::getDayOfWeek));
|
||||||
|
for(int i=0; i<weekInfo.getWeekDayInfos().size(); i++) {
|
||||||
|
Optional<Task> requestedTask = taskRepository.findById(weekInfo.getWeekDayInfos().get(i).getTaskID());
|
||||||
|
if(requestedTask.isEmpty()) {
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
} else {
|
||||||
|
int offset = weekInfo.getWeekDayInfos().get(i).getOffset()-1;
|
||||||
|
offsetMap.put(requestedTask.get(), offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return offsetMap;
|
||||||
|
}
|
||||||
|
|
||||||
public ServiceExitCode createTaskSeries(Task rootTask, TaskRepeatDayInfo taskRepeatInfo) {
|
public ServiceExitCode createTaskSeries(Task rootTask, TaskRepeatDayInfo taskRepeatInfo) {
|
||||||
if(taskRepeatInfo.getDeadlineStrategy() == DeadlineStrategy.FIX_DEADLINE) {
|
|
||||||
return ServiceExitCode.INVALID_PARAMETER;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Task> taskList = new ArrayList<>();
|
|
||||||
List<AbstractSchedule> abstractSchedules = new ArrayList<>();
|
|
||||||
TaskSerie taskSerie = new TaskSerie();
|
TaskSerie taskSerie = new TaskSerie();
|
||||||
TaskSerieItem rootItem = taskSerie.addTask(rootTask);
|
addRootSubTasksToTaskSerie(taskSerie, rootTask, 0);
|
||||||
rootTask.setTaskSerieItem(rootItem);
|
|
||||||
|
|
||||||
LocalDate currentTaskDate = rootTask.getStartDate().plusDays(taskRepeatInfo.getOffset());
|
HashMap<Task, Integer> offsetMap = new HashMap<>();
|
||||||
while(currentTaskDate.isBefore(taskRepeatInfo.getEndingDate())) {
|
offsetMap.put(rootTask, taskRepeatInfo.getOffset());
|
||||||
Task task = Task.cloneTask(rootTask);
|
|
||||||
task.setStartDate(currentTaskDate);
|
var repeatingResult = repeatTask(rootTask, taskRepeatInfo.getEndingDate(), offsetMap, taskSerie, 0);
|
||||||
if(taskRepeatInfo.getDeadlineStrategy() == DeadlineStrategy.DEADLINE_EQUAL_START) {
|
List<Task> clonedTasks = new ArrayList<>(repeatingResult.getValue00());
|
||||||
task.setDeadline(currentTaskDate);
|
List<AbstractSchedule> clonedSchedules = new ArrayList<>(repeatingResult.getValue01());
|
||||||
} else if(taskRepeatInfo.getDeadlineStrategy() == DeadlineStrategy.DEADLINE_FIT_START) {
|
|
||||||
task.setDeadline(currentTaskDate.plusDays(taskRepeatInfo.getOffset()-1));
|
|
||||||
}
|
|
||||||
TaskSerieItem taskSerieItem = taskSerie.addTask(task);
|
|
||||||
taskList.add(task);
|
|
||||||
task.setTaskSerieItem(taskSerieItem);
|
|
||||||
|
|
||||||
abstractSchedules.addAll(cloneSchedules(rootTask, task));
|
|
||||||
currentTaskDate = currentTaskDate.plusDays(taskRepeatInfo.getOffset());
|
|
||||||
}
|
|
||||||
|
|
||||||
taskSeriesRepository.save(taskSerie);
|
taskSeriesRepository.save(taskSerie);
|
||||||
taskRepository.saveAll(taskList);
|
taskRepository.saveAll(clonedTasks);
|
||||||
taskSerieItemRepository.saveAll(taskSerie.getTasks());
|
taskSerieItemRepository.saveAll(taskSerie.getTasks());
|
||||||
scheduleRepository.saveAll(abstractSchedules);
|
scheduleRepository.saveAll(clonedSchedules);
|
||||||
|
|
||||||
return ServiceExitCode.OK;
|
return ServiceExitCode.OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<AbstractSchedule> cloneSchedules(Task previousTask, Task nextTask) {
|
private void addRootSubTasksToTaskSerie(TaskSerie taskSerie, Task rootTask, int index) {
|
||||||
long numberDays = ChronoUnit.DAYS.between(previousTask.getStartDate(), nextTask.getStartDate());
|
Queue<Task> taskQueue = new LinkedList<>(Collections.singletonList(rootTask));
|
||||||
|
while(!taskQueue.isEmpty()) {
|
||||||
|
Task currentTask = taskQueue.poll();
|
||||||
|
|
||||||
List<AbstractSchedule> clonedSchedules = new ArrayList<>();
|
TaskSerieItem taskSerieItem = new TaskSerieItem(currentTask, index);
|
||||||
for(AbstractSchedule abstractSchedule : previousTask.getBasicTaskSchedules()) {
|
taskSerie.addItem(taskSerieItem);
|
||||||
AbstractSchedule clonedSchedule = abstractSchedule.cloneSchedule();
|
|
||||||
clonedSchedule.shiftSchedule(numberDays);
|
|
||||||
|
|
||||||
clonedSchedules.add(clonedSchedule);
|
|
||||||
}
|
}
|
||||||
return clonedSchedules;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteTaskSeriesItem(Task task) {
|
public void deleteTaskSeriesItem(Task task) {
|
||||||
@ -144,22 +140,20 @@ public class TaskSeriesService {
|
|||||||
}
|
}
|
||||||
taskSerie.getTasks().clear();
|
taskSerie.getTasks().clear();
|
||||||
taskSeriesRepository.delete(taskSerie);
|
taskSeriesRepository.delete(taskSerie);
|
||||||
} else {
|
} else if(task.getParent() == null){
|
||||||
repearIndexing(taskSerie);
|
repearIndexing(taskSerie, item.getSeriesIndex());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void repearIndexing(TaskSerie taskSerie) {
|
private void repearIndexing(TaskSerie taskSerie, int deletedIndex) {
|
||||||
taskSerie.getTasks().sort(Comparator.comparingInt(TaskSerieItem::getSeriesIndex));
|
taskSerie.getTasks().sort(Comparator.comparingInt(TaskSerieItem::getSeriesIndex));
|
||||||
List<TaskSerieItem> updatedItems = new ArrayList<>();
|
List<TaskSerieItem> updatedItems = new ArrayList<>();
|
||||||
int currentIndex = 1;
|
|
||||||
for(TaskSerieItem taskSerieItem : taskSerie.getTasks()) {
|
for(TaskSerieItem taskSerieItem : taskSerie.getTasks()) {
|
||||||
if(taskSerieItem.getSeriesIndex() != currentIndex) {
|
if(taskSerieItem.getSeriesIndex() > deletedIndex) {
|
||||||
taskSerieItem.setSeriesIndex(currentIndex);
|
taskSerieItem.setSeriesIndex(taskSerieItem.getSeriesIndex() -1);
|
||||||
updatedItems.add(taskSerieItem);
|
updatedItems.add(taskSerieItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
currentIndex++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
taskSerieItemRepository.saveAll(updatedItems);
|
taskSerieItemRepository.saveAll(updatedItems);
|
||||||
|
@ -49,6 +49,21 @@ public class TaskService {
|
|||||||
return new ServiceResult<>(task);
|
return new ServiceResult<>(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ServiceResult<Task> createSubTask(Task parentTask, TaskFieldInfo taskFieldInfo) {
|
||||||
|
if(taskFieldInfo.getStartDate().isBefore(parentTask.getStartDate()) ||
|
||||||
|
taskFieldInfo.getDeadline().isAfter(parentTask.getDeadline()) ||
|
||||||
|
taskFieldInfo.getDeadline().isBefore(taskFieldInfo.getStartDate())) {
|
||||||
|
return new ServiceResult<>(ServiceExitCode.INVALID_PARAMETER);
|
||||||
|
}
|
||||||
|
|
||||||
|
Task task = new Task(parentTask.getTaskgroup(), taskFieldInfo);
|
||||||
|
parentTask.getTaskgroup().getTasks().add(task);
|
||||||
|
parentTask.addSubtask(task);
|
||||||
|
|
||||||
|
taskRepository.save(task);
|
||||||
|
return new ServiceResult<>(task);
|
||||||
|
}
|
||||||
|
|
||||||
private boolean existTaskByName(Collection<Task> tasks, String name) {
|
private boolean existTaskByName(Collection<Task> tasks, String name) {
|
||||||
for(Task task : tasks) {
|
for(Task task : tasks) {
|
||||||
if(task.getTaskName().equals(name)) {
|
if(task.getTaskName().equals(name)) {
|
||||||
@ -94,11 +109,12 @@ public class TaskService {
|
|||||||
taskSeriesService.deleteTaskSeriesItem(task);
|
taskSeriesService.deleteTaskSeriesItem(task);
|
||||||
}
|
}
|
||||||
taskRepository.delete(task);
|
taskRepository.delete(task);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearTasks(Taskgroup taskgroup) {
|
public void clearTasks(Taskgroup taskgroup) {
|
||||||
taskSeriesService.deleteTaskSerieByTaskgroup(taskgroup);
|
taskSeriesService.deleteTaskSerieByTaskgroup(taskgroup);
|
||||||
|
taskScheduleService.deleteSchedulesByTaskgroup(taskgroup);
|
||||||
|
taskRepository.deleteTaskHierarchyWhereTaskgroup(taskgroup);
|
||||||
taskRepository.deleteAllByTaskgroup(taskgroup);
|
taskRepository.deleteAllByTaskgroup(taskgroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,4 +159,9 @@ public class TaskService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ServiceExitCode clearSubTasks(Task parentTask) {
|
||||||
|
taskRepository.deleteTasksByParent(parentTask);
|
||||||
|
return ServiceExitCode.OK;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,10 +21,12 @@ public class TaskgroupService {
|
|||||||
|
|
||||||
private final TaskgroupRepository taskgroupRepository;
|
private final TaskgroupRepository taskgroupRepository;
|
||||||
private final UserRepository userRepository;
|
private final UserRepository userRepository;
|
||||||
|
private final TaskService taskService;
|
||||||
public TaskgroupService(@Autowired TaskgroupRepository taskgroupRepository,
|
public TaskgroupService(@Autowired TaskgroupRepository taskgroupRepository,
|
||||||
@Autowired UserRepository userRepository) {
|
@Autowired UserRepository userRepository, @Autowired TaskService taskService) {
|
||||||
this.taskgroupRepository = taskgroupRepository;
|
this.taskgroupRepository = taskgroupRepository;
|
||||||
this.userRepository = userRepository;
|
this.userRepository = userRepository;
|
||||||
|
this.taskService = taskService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PermissionResult<Taskgroup> getTaskgroupByIDAndUsername(long taskgroupID, String username) {
|
public PermissionResult<Taskgroup> getTaskgroupByIDAndUsername(long taskgroupID, String username) {
|
||||||
@ -97,6 +99,7 @@ public class TaskgroupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void deleteTaskgroup(Taskgroup taskgroup) {
|
public void deleteTaskgroup(Taskgroup taskgroup) {
|
||||||
|
taskService.clearTasks(taskgroup);
|
||||||
taskgroupRepository.delete(taskgroup);
|
taskgroupRepository.delete(taskgroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
15
backend/src/main/java/util/Tripel.java
Normal file
15
backend/src/main/java/util/Tripel.java
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package util;
|
||||||
|
|
||||||
|
public class Tripel<A,B,C> extends Tupel<A, B> {
|
||||||
|
|
||||||
|
private final C value02;
|
||||||
|
|
||||||
|
public Tripel(A value00, B value01, C value02) {
|
||||||
|
super(value00, value01);
|
||||||
|
this.value02 = value02;
|
||||||
|
}
|
||||||
|
|
||||||
|
public C getValue02() {
|
||||||
|
return value02;
|
||||||
|
}
|
||||||
|
}
|
20
backend/src/main/java/util/Tupel.java
Normal file
20
backend/src/main/java/util/Tupel.java
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package util;
|
||||||
|
|
||||||
|
public class Tupel <A, B>{
|
||||||
|
|
||||||
|
private final A value00;
|
||||||
|
private final B value01;
|
||||||
|
|
||||||
|
public Tupel(A value00, B value01) {
|
||||||
|
this.value00 = value00;
|
||||||
|
this.value01 = value01;
|
||||||
|
}
|
||||||
|
|
||||||
|
public A getValue00() {
|
||||||
|
return value00;
|
||||||
|
}
|
||||||
|
|
||||||
|
public B getValue01() {
|
||||||
|
return value01;
|
||||||
|
}
|
||||||
|
}
|
@ -401,6 +401,190 @@ export class TaskService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param taskID internal id of task
|
||||||
|
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
|
||||||
|
* @param reportProgress flag to report request and response progress.
|
||||||
|
*/
|
||||||
|
public tasksTaskIDSubtasksDelete(taskID: number, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<SimpleStatusResponse>;
|
||||||
|
public tasksTaskIDSubtasksDelete(taskID: number, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<HttpResponse<SimpleStatusResponse>>;
|
||||||
|
public tasksTaskIDSubtasksDelete(taskID: number, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<HttpEvent<SimpleStatusResponse>>;
|
||||||
|
public tasksTaskIDSubtasksDelete(taskID: number, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<any> {
|
||||||
|
if (taskID === null || taskID === undefined) {
|
||||||
|
throw new Error('Required parameter taskID was null or undefined when calling tasksTaskIDSubtasksDelete.');
|
||||||
|
}
|
||||||
|
|
||||||
|
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.delete<SimpleStatusResponse>(`${this.configuration.basePath}/tasks/${encodeURIComponent(String(taskID))}/subtasks`,
|
||||||
|
{
|
||||||
|
context: localVarHttpContext,
|
||||||
|
responseType: <any>responseType_,
|
||||||
|
withCredentials: this.configuration.withCredentials,
|
||||||
|
headers: localVarHeaders,
|
||||||
|
observe: observe,
|
||||||
|
reportProgress: reportProgress
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param taskID internal id of task
|
||||||
|
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
|
||||||
|
* @param reportProgress flag to report request and response progress.
|
||||||
|
*/
|
||||||
|
public tasksTaskIDSubtasksGet(taskID: number, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<Array<TaskEntityInfo>>;
|
||||||
|
public tasksTaskIDSubtasksGet(taskID: number, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<HttpResponse<Array<TaskEntityInfo>>>;
|
||||||
|
public tasksTaskIDSubtasksGet(taskID: number, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<HttpEvent<Array<TaskEntityInfo>>>;
|
||||||
|
public tasksTaskIDSubtasksGet(taskID: number, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<any> {
|
||||||
|
if (taskID === null || taskID === undefined) {
|
||||||
|
throw new Error('Required parameter taskID was null or undefined when calling tasksTaskIDSubtasksGet.');
|
||||||
|
}
|
||||||
|
|
||||||
|
let localVarHeaders = this.defaultHeaders;
|
||||||
|
|
||||||
|
let localVarCredential: string | undefined;
|
||||||
|
// authentication (API_TOKEN) required
|
||||||
|
localVarCredential = this.configuration.lookupCredential('API_TOKEN');
|
||||||
|
if (localVarCredential) {
|
||||||
|
localVarHeaders = localVarHeaders.set('Authorization', 'Bearer ' + localVarCredential);
|
||||||
|
}
|
||||||
|
|
||||||
|
let localVarHttpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept;
|
||||||
|
if (localVarHttpHeaderAcceptSelected === undefined) {
|
||||||
|
// to determine the Accept header
|
||||||
|
const httpHeaderAccepts: string[] = [
|
||||||
|
'application/json'
|
||||||
|
];
|
||||||
|
localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts);
|
||||||
|
}
|
||||||
|
if (localVarHttpHeaderAcceptSelected !== undefined) {
|
||||||
|
localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
|
||||||
|
}
|
||||||
|
|
||||||
|
let localVarHttpContext: HttpContext | undefined = options && options.context;
|
||||||
|
if (localVarHttpContext === undefined) {
|
||||||
|
localVarHttpContext = new HttpContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let responseType_: 'text' | 'json' = 'json';
|
||||||
|
if(localVarHttpHeaderAcceptSelected && localVarHttpHeaderAcceptSelected.startsWith('text')) {
|
||||||
|
responseType_ = 'text';
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.httpClient.get<Array<TaskEntityInfo>>(`${this.configuration.basePath}/tasks/${encodeURIComponent(String(taskID))}/subtasks`,
|
||||||
|
{
|
||||||
|
context: localVarHttpContext,
|
||||||
|
responseType: <any>responseType_,
|
||||||
|
withCredentials: this.configuration.withCredentials,
|
||||||
|
headers: localVarHeaders,
|
||||||
|
observe: observe,
|
||||||
|
reportProgress: reportProgress
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates Subtask
|
||||||
|
* Create Subtask
|
||||||
|
* @param taskID internal id of task
|
||||||
|
* @param taskFieldInfo
|
||||||
|
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
|
||||||
|
* @param reportProgress flag to report request and response progress.
|
||||||
|
*/
|
||||||
|
public tasksTaskIDSubtasksPut(taskID: number, taskFieldInfo?: TaskFieldInfo, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<TaskEntityInfo>;
|
||||||
|
public tasksTaskIDSubtasksPut(taskID: number, taskFieldInfo?: TaskFieldInfo, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<HttpResponse<TaskEntityInfo>>;
|
||||||
|
public tasksTaskIDSubtasksPut(taskID: number, taskFieldInfo?: TaskFieldInfo, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<HttpEvent<TaskEntityInfo>>;
|
||||||
|
public tasksTaskIDSubtasksPut(taskID: number, taskFieldInfo?: TaskFieldInfo, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<any> {
|
||||||
|
if (taskID === null || taskID === undefined) {
|
||||||
|
throw new Error('Required parameter taskID was null or undefined when calling tasksTaskIDSubtasksPut.');
|
||||||
|
}
|
||||||
|
|
||||||
|
let localVarHeaders = this.defaultHeaders;
|
||||||
|
|
||||||
|
let localVarCredential: string | undefined;
|
||||||
|
// authentication (API_TOKEN) required
|
||||||
|
localVarCredential = this.configuration.lookupCredential('API_TOKEN');
|
||||||
|
if (localVarCredential) {
|
||||||
|
localVarHeaders = localVarHeaders.set('Authorization', 'Bearer ' + localVarCredential);
|
||||||
|
}
|
||||||
|
|
||||||
|
let localVarHttpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept;
|
||||||
|
if (localVarHttpHeaderAcceptSelected === undefined) {
|
||||||
|
// to determine the Accept header
|
||||||
|
const httpHeaderAccepts: string[] = [
|
||||||
|
'application/json'
|
||||||
|
];
|
||||||
|
localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts);
|
||||||
|
}
|
||||||
|
if (localVarHttpHeaderAcceptSelected !== undefined) {
|
||||||
|
localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
|
||||||
|
}
|
||||||
|
|
||||||
|
let localVarHttpContext: HttpContext | undefined = options && options.context;
|
||||||
|
if (localVarHttpContext === undefined) {
|
||||||
|
localVarHttpContext = new HttpContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// to determine the Content-Type header
|
||||||
|
const consumes: string[] = [
|
||||||
|
'application/json'
|
||||||
|
];
|
||||||
|
const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes);
|
||||||
|
if (httpContentTypeSelected !== undefined) {
|
||||||
|
localVarHeaders = localVarHeaders.set('Content-Type', httpContentTypeSelected);
|
||||||
|
}
|
||||||
|
|
||||||
|
let responseType_: 'text' | 'json' = 'json';
|
||||||
|
if(localVarHttpHeaderAcceptSelected && localVarHttpHeaderAcceptSelected.startsWith('text')) {
|
||||||
|
responseType_ = 'text';
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.httpClient.put<TaskEntityInfo>(`${this.configuration.basePath}/tasks/${encodeURIComponent(String(taskID))}/subtasks`,
|
||||||
|
taskFieldInfo,
|
||||||
|
{
|
||||||
|
context: localVarHttpContext,
|
||||||
|
responseType: <any>responseType_,
|
||||||
|
withCredentials: this.configuration.withCredentials,
|
||||||
|
headers: localVarHeaders,
|
||||||
|
observe: observe,
|
||||||
|
reportProgress: reportProgress
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* creates a new task
|
* creates a new task
|
||||||
* creates tasks
|
* creates tasks
|
||||||
|
@ -60,5 +60,13 @@ export interface TaskEntityInfo {
|
|||||||
* determines whether the task is associated with a taskserie
|
* determines whether the task is associated with a taskserie
|
||||||
*/
|
*/
|
||||||
hasTaskSerie: boolean;
|
hasTaskSerie: boolean;
|
||||||
|
/**
|
||||||
|
* determines whether a task has subtasks
|
||||||
|
*/
|
||||||
|
hasSubtasks: boolean;
|
||||||
|
/**
|
||||||
|
* determines whether a task is a top task or Not
|
||||||
|
*/
|
||||||
|
hasParent: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,5 +42,9 @@ export interface TaskOverviewInfo {
|
|||||||
* determines whether the task can be finished
|
* determines whether the task can be finished
|
||||||
*/
|
*/
|
||||||
finishable: boolean;
|
finishable: boolean;
|
||||||
|
/**
|
||||||
|
* determines whether the task has subtasks
|
||||||
|
*/
|
||||||
|
hasSubtasks: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,18 +16,9 @@ export interface TaskRepeatDayInfo {
|
|||||||
* number repeating days
|
* number repeating days
|
||||||
*/
|
*/
|
||||||
offset: number;
|
offset: number;
|
||||||
deadlineStrategy: TaskRepeatDayInfo.DeadlineStrategyEnum;
|
|
||||||
/**
|
/**
|
||||||
* Date until the tasks repeat
|
* Date until the tasks repeat
|
||||||
*/
|
*/
|
||||||
endingDate: string;
|
endingDate: string;
|
||||||
}
|
}
|
||||||
export namespace TaskRepeatDayInfo {
|
|
||||||
export type DeadlineStrategyEnum = 'DEADLINE_EQUAL_START' | 'DEADLINE_FIT_START';
|
|
||||||
export const DeadlineStrategyEnum = {
|
|
||||||
EqualStart: 'DEADLINE_EQUAL_START' as DeadlineStrategyEnum,
|
|
||||||
FitStart: 'DEADLINE_FIT_START' as DeadlineStrategyEnum
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,5 +20,22 @@ export interface TaskRepeatWeekDayInfo {
|
|||||||
* internal identifier of task
|
* internal identifier of task
|
||||||
*/
|
*/
|
||||||
taskID: number;
|
taskID: number;
|
||||||
|
/**
|
||||||
|
* day of week
|
||||||
|
*/
|
||||||
|
dayOfWeek: TaskRepeatWeekDayInfo.DayOfWeekEnum;
|
||||||
|
}
|
||||||
|
export namespace TaskRepeatWeekDayInfo {
|
||||||
|
export type DayOfWeekEnum = 'MONDAY' | 'TUESDAY' | 'WEDNESDAY' | 'THURSDAY' | 'FRIDAY' | 'SATURDAY' | 'SUNDAY';
|
||||||
|
export const DayOfWeekEnum = {
|
||||||
|
Monday: 'MONDAY' as DayOfWeekEnum,
|
||||||
|
Tuesday: 'TUESDAY' as DayOfWeekEnum,
|
||||||
|
Wednesday: 'WEDNESDAY' as DayOfWeekEnum,
|
||||||
|
Thursday: 'THURSDAY' as DayOfWeekEnum,
|
||||||
|
Friday: 'FRIDAY' as DayOfWeekEnum,
|
||||||
|
Saturday: 'SATURDAY' as DayOfWeekEnum,
|
||||||
|
Sunday: 'SUNDAY' as DayOfWeekEnum
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -13,19 +13,10 @@ import { TaskRepeatWeekDayInfo } from './taskRepeatWeekDayInfo';
|
|||||||
|
|
||||||
|
|
||||||
export interface TaskRepeatWeekInfo {
|
export interface TaskRepeatWeekInfo {
|
||||||
deadlineStrategy: TaskRepeatWeekInfo.DeadlineStrategyEnum;
|
|
||||||
/**
|
/**
|
||||||
* Date until the tasks repeat
|
* Date until the tasks repeat
|
||||||
*/
|
*/
|
||||||
endDate: string;
|
endDate: string;
|
||||||
weekDayInfos: Array<TaskRepeatWeekDayInfo>;
|
weekDayInfos: Array<TaskRepeatWeekDayInfo>;
|
||||||
}
|
}
|
||||||
export namespace TaskRepeatWeekInfo {
|
|
||||||
export type DeadlineStrategyEnum = 'DEADLINE_EQUAL_START' | 'DEADLINE_FIT_START';
|
|
||||||
export const DeadlineStrategyEnum = {
|
|
||||||
EqualStart: 'DEADLINE_EQUAL_START' as DeadlineStrategyEnum,
|
|
||||||
FitStart: 'DEADLINE_FIT_START' as DeadlineStrategyEnum
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -93,7 +93,8 @@ export class ActiveTaskOverviewComponent implements OnInit{
|
|||||||
editTask(editedTask: TaskTaskgroupInfo) {
|
editTask(editedTask: TaskTaskgroupInfo) {
|
||||||
const taskEditorInfo: TaskEditorData = {
|
const taskEditorInfo: TaskEditorData = {
|
||||||
task: editedTask,
|
task: editedTask,
|
||||||
taskgroupID: editedTask.taskgroups[editedTask.taskgroups.length-1].taskgroupID
|
taskgroupID: editedTask.taskgroups[editedTask.taskgroups.length-1].taskgroupID,
|
||||||
|
parentTask: undefined
|
||||||
};
|
};
|
||||||
this.dialog.open(TaskEditorComponent, {data: taskEditorInfo, width: "600px"})
|
this.dialog.open(TaskEditorComponent, {data: taskEditorInfo, width: "600px"})
|
||||||
}
|
}
|
||||||
|
@ -31,3 +31,13 @@
|
|||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.task-title {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtask-link {
|
||||||
|
color: #00bc8c;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
@ -2,7 +2,10 @@
|
|||||||
<button mat-raised-button class="greenBtn long-btn"(click)="openTaskCreation()">Add</button>
|
<button mat-raised-button class="greenBtn long-btn"(click)="openTaskCreation()">Add</button>
|
||||||
<mat-card *ngFor="let task of tasks">
|
<mat-card *ngFor="let task of tasks">
|
||||||
<mat-card-content>
|
<mat-card-content>
|
||||||
<h3><a class="task-link" [routerLink]="['/taskgroups', taskgroupID!, 'tasks', task.taskID]">{{task.taskName}}</a></h3>
|
<h3 class="task-title">
|
||||||
|
<a class="task-link" [routerLink]="['/taskgroups', taskgroupID!, 'tasks', task.taskID]">{{task.taskName}}</a>
|
||||||
|
<button mat-button class="subtask-link" *ngIf="task.hasSubtasks" [routerLink]="['/taskgroups', taskgroupID!, 'tasks', task.taskID]">has Subtasks</button>
|
||||||
|
</h3>
|
||||||
<mat-progress-bar mode="determinate" value="{{task.activeTime}}" class="progress"></mat-progress-bar>
|
<mat-progress-bar mode="determinate" value="{{task.activeTime}}" class="progress"></mat-progress-bar>
|
||||||
<p class="task-info"><i>ETA: </i>{{task.activeTime}} / {{task.eta}}</p>
|
<p class="task-info"><i>ETA: </i>{{task.activeTime}} / {{task.eta}}</p>
|
||||||
<p class="task-info"><i>Limit: </i>{{task.limit}}</p>
|
<p class="task-info"><i>Limit: </i>{{task.limit}}</p>
|
||||||
|
@ -75,7 +75,8 @@ export class TaskOverviewComponent {
|
|||||||
openTaskCreation() {
|
openTaskCreation() {
|
||||||
const editorData: TaskEditorData = {
|
const editorData: TaskEditorData = {
|
||||||
task: undefined,
|
task: undefined,
|
||||||
taskgroupID: this.taskgroupID!
|
taskgroupID: this.taskgroupID!,
|
||||||
|
parentTask: undefined
|
||||||
}
|
}
|
||||||
const dialogRef = this.dialog.open(TaskEditorComponent, {data: editorData, width: "600px"})
|
const dialogRef = this.dialog.open(TaskEditorComponent, {data: editorData, width: "600px"})
|
||||||
dialogRef.afterClosed().subscribe(res => {
|
dialogRef.afterClosed().subscribe(res => {
|
||||||
@ -88,6 +89,7 @@ export class TaskOverviewComponent {
|
|||||||
activeTime: 0,
|
activeTime: 0,
|
||||||
overdue: res.overdue,
|
overdue: res.overdue,
|
||||||
taskgroupPath: [],
|
taskgroupPath: [],
|
||||||
|
hasSubtasks: false,
|
||||||
finishable: res.finishable
|
finishable: res.finishable
|
||||||
}
|
}
|
||||||
this.creationEmitter.emit({
|
this.creationEmitter.emit({
|
||||||
|
@ -104,7 +104,8 @@ export class TaskgroupDashboardComponent implements OnInit {
|
|||||||
openTaskCreation() {
|
openTaskCreation() {
|
||||||
const editorData: TaskEditorData = {
|
const editorData: TaskEditorData = {
|
||||||
task: undefined,
|
task: undefined,
|
||||||
taskgroupID: this.taskgroupID
|
taskgroupID: this.taskgroupID,
|
||||||
|
parentTask: undefined
|
||||||
}
|
}
|
||||||
const dialogRef = this.dialog.open(TaskEditorComponent, {data: editorData, width: "600px"})
|
const dialogRef = this.dialog.open(TaskEditorComponent, {data: editorData, width: "600px"})
|
||||||
dialogRef.afterClosed().subscribe(res => {
|
dialogRef.afterClosed().subscribe(res => {
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
import {Component, Inject, OnInit} from '@angular/core';
|
import {Component, Inject, OnInit} from '@angular/core';
|
||||||
import {TaskEditorData} from "../task-editor/TaskEditorData";
|
import {TaskEditorData} from "../task-editor/TaskEditorData";
|
||||||
import {TaskEntityInfo, TaskgroupService} from "../../../api";
|
import {TaskEntityInfo, TaskgroupService, TaskService} from "../../../api";
|
||||||
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
||||||
import {MatSnackBar} from "@angular/material/snack-bar";
|
import {MatSnackBar} from "@angular/material/snack-bar";
|
||||||
|
|
||||||
export interface ClearTaskDialogData {
|
export interface ClearTaskDialogData {
|
||||||
taskgroupID: number,
|
taskgroupID: number | undefined,
|
||||||
tasks: TaskEntityInfo[]
|
parentTaskID: number | undefined
|
||||||
|
tasks: TaskEntityInfo[],
|
||||||
|
subtasks: boolean
|
||||||
}
|
}
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-clear-task-dialog',
|
selector: 'app-clear-task-dialog',
|
||||||
@ -23,7 +25,8 @@ export class ClearTaskDialogComponent implements OnInit{
|
|||||||
constructor(@Inject(MAT_DIALOG_DATA) public editorData: ClearTaskDialogData,
|
constructor(@Inject(MAT_DIALOG_DATA) public editorData: ClearTaskDialogData,
|
||||||
private taskgroupService: TaskgroupService,
|
private taskgroupService: TaskgroupService,
|
||||||
private dialogRef: MatDialogRef<ClearTaskDialogComponent>,
|
private dialogRef: MatDialogRef<ClearTaskDialogComponent>,
|
||||||
private snackbar: MatSnackBar) {
|
private snackbar: MatSnackBar,
|
||||||
|
private taskService: TaskService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
@ -48,22 +51,43 @@ export class ClearTaskDialogComponent implements OnInit{
|
|||||||
}
|
}
|
||||||
|
|
||||||
clearTasks() {
|
clearTasks() {
|
||||||
this.taskgroupService.taskgroupsTaskgroupIDClearDelete(this.editorData.taskgroupID).subscribe({
|
if(this.editorData.subtasks) {
|
||||||
next: resp => {
|
this.taskService.tasksTaskIDSubtasksDelete(this.editorData.parentTaskID!).subscribe({
|
||||||
if(resp.status == "success") {
|
next: resp => {
|
||||||
const deletedTasks = this.editorData.tasks;
|
if(resp.status == "success") {
|
||||||
this.dialogRef.close(deletedTasks);
|
const deletedTasks = this.editorData.tasks;
|
||||||
|
this.dialogRef.close(deletedTasks);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: err => {
|
||||||
|
if(err.status == 403) {
|
||||||
|
this.snackbar.open("No permission", "", {duration: 2000});
|
||||||
|
} else if(err.status == 404) {
|
||||||
|
this.snackbar.open("Not found", "", {duration: 2000});
|
||||||
|
} else {
|
||||||
|
this.snackbar.open("Unexpected error", "", {duration: 2000});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
error: err => {
|
} else {
|
||||||
if(err.status == 403) {
|
this.taskgroupService.taskgroupsTaskgroupIDClearDelete(this.editorData.taskgroupID!).subscribe({
|
||||||
this.snackbar.open("No permission", "", {duration: 2000});
|
next: resp => {
|
||||||
} else if(err.status == 404) {
|
if(resp.status == "success") {
|
||||||
this.snackbar.open("Not found", "", {duration: 2000});
|
const deletedTasks = this.editorData.tasks;
|
||||||
} else {
|
this.dialogRef.close(deletedTasks);
|
||||||
this.snackbar.open("Unexpected error", "", {duration: 2000});
|
}
|
||||||
|
},
|
||||||
|
error: err => {
|
||||||
|
if(err.status == 403) {
|
||||||
|
this.snackbar.open("No permission", "", {duration: 2000});
|
||||||
|
} else if(err.status == 404) {
|
||||||
|
this.snackbar.open("Not found", "", {duration: 2000});
|
||||||
|
} else {
|
||||||
|
this.snackbar.open("Unexpected error", "", {duration: 2000});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
})
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container matColumnDef="delete">
|
<ng-container matColumnDef="delete">
|
||||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>
|
<th mat-header-cell *matHeaderCellDef mat-sort-header>
|
||||||
<button mat-icon-button color="primary" (click)="repeatSelectedTasks()" [disabled]="selection.isEmpty()"><mat-icon>event_repeat</mat-icon></button>
|
<button *ngIf="subTasks.length == 0" mat-icon-button color="primary" (click)="repeatSelectedTasks()" [disabled]="selection.isEmpty()"><mat-icon>event_repeat</mat-icon></button>
|
||||||
</th>
|
</th>
|
||||||
<td mat-cell *matCellDef="let task">
|
<td mat-cell *matCellDef="let task">
|
||||||
<button mat-icon-button color="warn" (click)="deleteTask(task)"><mat-icon>delete</mat-icon></button>
|
<button mat-icon-button color="warn" (click)="deleteTask(task)"><mat-icon>delete</mat-icon></button>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {Component, Input, OnChanges, ViewChild} from '@angular/core';
|
import {Component, Input, OnChanges, OnInit, ViewChild} from '@angular/core';
|
||||||
import {TaskEntityInfo, TaskService} from "../../../api";
|
import {TaskEntityInfo, TaskService} from "../../../api";
|
||||||
import {MatPaginator} from "@angular/material/paginator";
|
import {MatPaginator} from "@angular/material/paginator";
|
||||||
import {MatSort} from "@angular/material/sort";
|
import {MatSort} from "@angular/material/sort";
|
||||||
@ -20,12 +20,20 @@ import {TaskWeeklySeriesCreatorComponent} from "../task-weekly-series-creator/ta
|
|||||||
})
|
})
|
||||||
export class TaskDashboardComponent implements OnChanges{
|
export class TaskDashboardComponent implements OnChanges{
|
||||||
ngOnChanges(): void {
|
ngOnChanges(): void {
|
||||||
if(this.taskgroupID != undefined) {
|
if(this.subTasks.length == 0 && this.taskgroupID != undefined) {
|
||||||
this.fetchTasks()
|
this.fetchTasks()
|
||||||
|
} else if(this.subTasks.length > 0) {
|
||||||
|
this.datasource.data = this.subTasks;
|
||||||
|
this.datasource.paginator = this.paginator!;
|
||||||
|
this.datasource.sort = this.sort!;
|
||||||
|
|
||||||
|
this.displayedColumns = this.displayedColumns.filter(col => col !== 'select')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Input("taskgroupID") taskgroupID: number | undefined
|
@Input("taskgroupID") taskgroupID: number | undefined
|
||||||
|
@Input("subTasks") subTasks: TaskEntityInfo[] = []
|
||||||
|
@Input("parentTaskID") parentTaskID: number | undefined
|
||||||
@ViewChild(MatPaginator) paginator: MatPaginator | undefined
|
@ViewChild(MatPaginator) paginator: MatPaginator | undefined
|
||||||
@ViewChild(MatSort) sort: MatSort | undefined
|
@ViewChild(MatSort) sort: MatSort | undefined
|
||||||
|
|
||||||
@ -97,15 +105,18 @@ export class TaskDashboardComponent implements OnChanges{
|
|||||||
editTask(task: TaskEntityInfo) {
|
editTask(task: TaskEntityInfo) {
|
||||||
const taskEditorInfo: TaskEditorData = {
|
const taskEditorInfo: TaskEditorData = {
|
||||||
task: task,
|
task: task,
|
||||||
taskgroupID: this.taskgroupID!
|
taskgroupID: this.taskgroupID!,
|
||||||
|
parentTask: undefined
|
||||||
};
|
};
|
||||||
const dialogRef = this.dialog.open(TaskEditorComponent, {data: taskEditorInfo, minWidth: "400px"})
|
const dialogRef = this.dialog.open(TaskEditorComponent, {data: taskEditorInfo, minWidth: "400px"})
|
||||||
}
|
}
|
||||||
|
|
||||||
clearTasks() {
|
clearTasks() {
|
||||||
const clearTaskData: ClearTaskDialogData = {
|
let clearTaskData: ClearTaskDialogData = {
|
||||||
tasks: this.datasource.data,
|
tasks: this.datasource.data,
|
||||||
taskgroupID: this.taskgroupID!
|
taskgroupID: this.taskgroupID,
|
||||||
|
parentTaskID: this.parentTaskID,
|
||||||
|
subtasks: this.subTasks.length > 0
|
||||||
}
|
}
|
||||||
const dialogRef = this.dialog.open(ClearTaskDialogComponent, {data: clearTaskData, width: "600px"});
|
const dialogRef = this.dialog.open(ClearTaskDialogComponent, {data: clearTaskData, width: "600px"});
|
||||||
dialogRef.afterClosed().subscribe(res => {
|
dialogRef.afterClosed().subscribe(res => {
|
||||||
@ -146,5 +157,6 @@ export class TaskDashboardComponent implements OnChanges{
|
|||||||
resp.forEach(task => console.log(task))
|
resp.forEach(task => console.log(task))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<div class="left">
|
<div class="left">
|
||||||
<div>{{taskStatus + " " + task!.taskName}}</div>
|
<div>{{taskStatus + " " + task!.taskName}}</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="right lightBlueBtn" mat-raised-button style="margin-left: auto">Add Subtask</button>
|
<button class="right lightBlueBtn" mat-raised-button style="margin-left: auto" (click)="addSubtask()">Add Subtask</button>
|
||||||
</div>
|
</div>
|
||||||
</mat-card-title>
|
</mat-card-title>
|
||||||
|
|
||||||
@ -29,7 +29,7 @@
|
|||||||
<button mat-flat-button class="borderless-btn" color="primary" [routerLink]="['/taskgroups', taskgroup!.taskgroupID, 'tasks', task!.taskID, 'schedule']">Schedule</button>
|
<button mat-flat-button class="borderless-btn" color="primary" [routerLink]="['/taskgroups', taskgroup!.taskgroupID, 'tasks', task!.taskID, 'schedule']">Schedule</button>
|
||||||
<button mat-flat-button class="yellowBtn" (click)="startTaskNow()">Start now</button>
|
<button mat-flat-button class="yellowBtn" (click)="startTaskNow()">Start now</button>
|
||||||
<button mat-flat-button class="grayBtn" (click)="openTaskEditor()">Edit</button>
|
<button mat-flat-button class="grayBtn" (click)="openTaskEditor()">Edit</button>
|
||||||
<button mat-flat-button class="lightBlueBtn" *ngIf="!task.hasTaskSerie" (click)="openRepeatingTaskEditor()">Copy</button>
|
<button mat-flat-button class="lightBlueBtn" *ngIf="!task.hasTaskSerie && !task.hasParent" (click)="openRepeatingTaskEditor()">Copy</button>
|
||||||
<button mat-flat-button class="greenBtn" *ngIf="task!.finishable" (click)="finishTask()">{{task!.finished ? 'Reopen':'Finish'}}</button>
|
<button mat-flat-button class="greenBtn" *ngIf="task!.finishable" (click)="finishTask()">{{task!.finished ? 'Reopen':'Finish'}}</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -42,6 +42,13 @@
|
|||||||
</mat-card-actions>
|
</mat-card-actions>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
|
|
||||||
|
<mat-expansion-panel *ngIf="subTasks.length > 0" style="margin-top: 20px" expanded>
|
||||||
|
<mat-expansion-panel-header>Subtasks</mat-expansion-panel-header>
|
||||||
|
<div *ngIf="task != undefined">
|
||||||
|
<app-task-dashboard [subTasks]="subTasks" [taskgroupID]="taskgroupID" [parentTaskID]="task!.taskID"></app-task-dashboard>
|
||||||
|
</div>
|
||||||
|
</mat-expansion-panel>
|
||||||
|
|
||||||
|
|
||||||
<mat-expansion-panel *ngIf="taskgroup != undefined && task != undefined" style="margin-top: 20px" expanded>
|
<mat-expansion-panel *ngIf="taskgroup != undefined && task != undefined" style="margin-top: 20px" expanded>
|
||||||
<mat-expansion-panel-header>Schedules</mat-expansion-panel-header>
|
<mat-expansion-panel-header>Schedules</mat-expansion-panel-header>
|
||||||
|
@ -50,6 +50,8 @@ export class TaskDetailOverviewComponent implements OnInit {
|
|||||||
currentProgress: string = "0";
|
currentProgress: string = "0";
|
||||||
futureProgress: string = "0";
|
futureProgress: string = "0";
|
||||||
|
|
||||||
|
subTasks: TaskEntityInfo[] = [];
|
||||||
|
|
||||||
constructor(private activatedRoute: ActivatedRoute,
|
constructor(private activatedRoute: ActivatedRoute,
|
||||||
private taskgroupService: TaskgroupService,
|
private taskgroupService: TaskgroupService,
|
||||||
private taskService: TaskService,
|
private taskService: TaskService,
|
||||||
@ -100,6 +102,12 @@ export class TaskDetailOverviewComponent implements OnInit {
|
|||||||
this.calcProgress();
|
this.calcProgress();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
this.taskService.tasksTaskIDSubtasksGet(Number(params.get('taskID'))).subscribe({
|
||||||
|
next: resp => {
|
||||||
|
this.subTasks = resp;
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -135,7 +143,8 @@ export class TaskDetailOverviewComponent implements OnInit {
|
|||||||
if(this.task != undefined) {
|
if(this.task != undefined) {
|
||||||
const taskEditorInfo: TaskEditorData = {
|
const taskEditorInfo: TaskEditorData = {
|
||||||
task: this.task!,
|
task: this.task!,
|
||||||
taskgroupID: this.taskgroupID!
|
taskgroupID: this.taskgroupID!,
|
||||||
|
parentTask: undefined
|
||||||
};
|
};
|
||||||
this.dialog.open(TaskEditorComponent, {data: taskEditorInfo, width: "600px"})
|
this.dialog.open(TaskEditorComponent, {data: taskEditorInfo, width: "600px"})
|
||||||
}
|
}
|
||||||
@ -166,4 +175,16 @@ export class TaskDetailOverviewComponent implements OnInit {
|
|||||||
minWidth: "400px"
|
minWidth: "400px"
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addSubtask() {
|
||||||
|
const editorData: TaskEditorData = {
|
||||||
|
task: undefined,
|
||||||
|
taskgroupID: this.taskgroupID,
|
||||||
|
parentTask: this.task
|
||||||
|
}
|
||||||
|
const dialogRef = this.dialog.open(TaskEditorComponent, {
|
||||||
|
data: editorData,
|
||||||
|
minWidth: "400px"
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,4 +3,5 @@ import {TaskEntityInfo, TaskTaskgroupInfo} from "../../../api";
|
|||||||
export interface TaskEditorData {
|
export interface TaskEditorData {
|
||||||
taskgroupID: number;
|
taskgroupID: number;
|
||||||
task: TaskTaskgroupInfo | TaskEntityInfo | undefined
|
task: TaskTaskgroupInfo | TaskEntityInfo | undefined
|
||||||
|
parentTask: TaskEntityInfo | undefined
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
<h1 mat-dialog-title *ngIf="editorData.task != undefined">Edit Task ({{editorData.task!.taskName}})</h1>
|
<h1 mat-dialog-title *ngIf="editorData.task != undefined">Edit Task ({{editorData.task!.taskName}})</h1>
|
||||||
<h1 mat-dialog-title *ngIf="editorData.task == undefined">Create New Task</h1>
|
<h1 mat-dialog-title *ngIf="editorData.task == undefined">Create New {{editorData.parentTask != undefined? 'Sub-':''}}Task</h1>
|
||||||
<div mat-dialog-content>
|
<div mat-dialog-content>
|
||||||
|
<p *ngIf="editorData.parentTask != undefined">Create a new Subtask for the Task <i>{{editorData.parentTask!.taskName}}</i></p>
|
||||||
<mat-form-field appearance="outline" class="long-form">
|
<mat-form-field appearance="outline" class="long-form">
|
||||||
<mat-label>Name</mat-label>
|
<mat-label>Name</mat-label>
|
||||||
<input matInput [formControl]="nameCtrl">
|
<input matInput [formControl]="nameCtrl">
|
||||||
|
@ -61,7 +61,36 @@ export class TaskEditorComponent implements OnInit {
|
|||||||
startDate_formatted = moment(this.startDate.value).format('YYYY-MM-DDTHH:mm:ss.SSSZ');
|
startDate_formatted = moment(this.startDate.value).format('YYYY-MM-DDTHH:mm:ss.SSSZ');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.taskService.tasksTaskgroupIDPut(this.editorData.taskgroupID, {
|
if(this.editorData.parentTask != undefined) {
|
||||||
|
this.createSubTask(startDate_formatted, endDate_formatted);
|
||||||
|
} else {
|
||||||
|
this.taskService.tasksTaskgroupIDPut(this.editorData.taskgroupID, {
|
||||||
|
taskName: this.nameCtrl.value,
|
||||||
|
eta: this.etaCtrl.value,
|
||||||
|
startDate: startDate_formatted,
|
||||||
|
deadline: endDate_formatted,
|
||||||
|
finishable: this.finishable,
|
||||||
|
}).subscribe({
|
||||||
|
next: resp => {
|
||||||
|
this.dialog.close(resp);
|
||||||
|
},
|
||||||
|
error: err => {
|
||||||
|
if(err.status == 403) {
|
||||||
|
this.snackbar.open("No permission", "", {duration: 2000});
|
||||||
|
} else if(err.status == 404) {
|
||||||
|
this.snackbar.open("Taskgroup not found", "", {duration: 2000});
|
||||||
|
} else if(err.status == 409) {
|
||||||
|
this.snackbar.open("Task already exists", "", {duration: 2000});
|
||||||
|
} else {
|
||||||
|
this.snackbar.open("Unexpected error", "", {duration: 3000});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createSubTask(startDate_formatted: string|undefined, endDate_formatted: string|undefined) {
|
||||||
|
this.taskService.tasksTaskIDSubtasksPut(this.editorData.parentTask!.taskID, {
|
||||||
taskName: this.nameCtrl.value,
|
taskName: this.nameCtrl.value,
|
||||||
eta: this.etaCtrl.value,
|
eta: this.etaCtrl.value,
|
||||||
startDate: startDate_formatted,
|
startDate: startDate_formatted,
|
||||||
@ -78,7 +107,9 @@ export class TaskEditorComponent implements OnInit {
|
|||||||
this.snackbar.open("Taskgroup not found", "", {duration: 2000});
|
this.snackbar.open("Taskgroup not found", "", {duration: 2000});
|
||||||
} else if(err.status == 409) {
|
} else if(err.status == 409) {
|
||||||
this.snackbar.open("Task already exists", "", {duration: 2000});
|
this.snackbar.open("Task already exists", "", {duration: 2000});
|
||||||
} else {
|
} else if(err.status == 400) {
|
||||||
|
this.snackbar.open("Invalid Dates", "", {duration: 2000})
|
||||||
|
} else {
|
||||||
this.snackbar.open("Unexpected error", "", {duration: 3000});
|
this.snackbar.open("Unexpected error", "", {duration: 3000});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -123,4 +154,6 @@ export class TaskEditorComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected readonly parent = parent;
|
||||||
}
|
}
|
||||||
|
@ -7,13 +7,6 @@
|
|||||||
<mat-label>Offset</mat-label>
|
<mat-label>Offset</mat-label>
|
||||||
<input matInput formControlName="offsetCtrl" type="number" (keypress)="($event.charCode >= 48 && $event.charCode < 58)">
|
<input matInput formControlName="offsetCtrl" type="number" (keypress)="($event.charCode >= 48 && $event.charCode < 58)">
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<mat-form-field appearance="outline" class="long-form">
|
|
||||||
<mat-label>Deadline Strategy</mat-label>
|
|
||||||
<mat-select formControlName="deadlineStrategyCtrl">
|
|
||||||
<mat-option value="DEADLINE_FIT_START">Fit Next Start</mat-option>
|
|
||||||
<mat-option value="DEADLINE_EQUAL_START">Equal Same Start</mat-option>
|
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
<div>
|
<div>
|
||||||
<button mat-raised-button color="primary" matStepperNext>Next</button>
|
<button mat-raised-button color="primary" matStepperNext>Next</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,7 +2,6 @@ import {Component, Inject} from '@angular/core';
|
|||||||
import {FormBuilder, Validators} from "@angular/forms";
|
import {FormBuilder, Validators} from "@angular/forms";
|
||||||
import {TaskEntityInfo, TaskRepeatDayInfo, TaskseriesService} from "../../../api";
|
import {TaskEntityInfo, TaskRepeatDayInfo, TaskseriesService} from "../../../api";
|
||||||
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
||||||
import DeadlineStrategyEnum = TaskRepeatDayInfo.DeadlineStrategyEnum;
|
|
||||||
import * as moment from "moment";
|
import * as moment from "moment";
|
||||||
|
|
||||||
|
|
||||||
@ -15,7 +14,6 @@ export class TaskSeriesCreatorComponent {
|
|||||||
|
|
||||||
dailyFormGroup = this._formBuilder.group({
|
dailyFormGroup = this._formBuilder.group({
|
||||||
offsetCtrl: ['', Validators.required],
|
offsetCtrl: ['', Validators.required],
|
||||||
deadlineStrategyCtrl: ['', Validators.required]
|
|
||||||
})
|
})
|
||||||
|
|
||||||
endDateFormGroup = this._formBuilder.group({
|
endDateFormGroup = this._formBuilder.group({
|
||||||
@ -32,7 +30,6 @@ export class TaskSeriesCreatorComponent {
|
|||||||
save() {
|
save() {
|
||||||
this.taskSeriesService.tasksTaskIDTaskseriesDailyPost(this.task.taskID,{
|
this.taskSeriesService.tasksTaskIDTaskseriesDailyPost(this.task.taskID,{
|
||||||
offset: Number(this.dailyFormGroup.get('offsetCtrl')!.value!),
|
offset: Number(this.dailyFormGroup.get('offsetCtrl')!.value!),
|
||||||
deadlineStrategy: this.convertDeadlineStrategyCtrlToDeadlineEnum(),
|
|
||||||
endingDate: moment( this.endDateFormGroup.get('endDateCtrl')!.value!).format('YYYY-MM-DDTHH:mm:ss.SSSZ')
|
endingDate: moment( this.endDateFormGroup.get('endDateCtrl')!.value!).format('YYYY-MM-DDTHH:mm:ss.SSSZ')
|
||||||
}).subscribe({
|
}).subscribe({
|
||||||
next: resp => {
|
next: resp => {
|
||||||
@ -40,13 +37,4 @@ export class TaskSeriesCreatorComponent {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
convertDeadlineStrategyCtrlToDeadlineEnum() {
|
|
||||||
const deadlineStrategy = this.dailyFormGroup.get('deadlineStrategyCtrl')!.value;
|
|
||||||
if(deadlineStrategy === DeadlineStrategyEnum.EqualStart) {
|
|
||||||
return DeadlineStrategyEnum.EqualStart;
|
|
||||||
} else {
|
|
||||||
return DeadlineStrategyEnum.FitStart;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -24,14 +24,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</mat-accordion>
|
</mat-accordion>
|
||||||
</div>
|
</div>
|
||||||
<mat-form-field appearance="outline" class="long-form" id="deadline-strategy-form">
|
|
||||||
<mat-label>Deadline-Strategy</mat-label>
|
|
||||||
<mat-select formControlName="deadlineStrategyCtrl">
|
|
||||||
<mat-option *ngFor="let deadlineStrategy of availableDeadlineStrategys" [value]="deadlineStrategy">
|
|
||||||
{{deadlineStrategy}}
|
|
||||||
</mat-option>
|
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
<mat-form-field appearance="outline" class="long-form" id="endDate-form">
|
<mat-form-field appearance="outline" class="long-form" id="endDate-form">
|
||||||
<mat-label>Ending Date</mat-label>
|
<mat-label>Ending Date</mat-label>
|
||||||
|
@ -3,7 +3,7 @@ import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
|||||||
import {TaskEntityInfo, TaskRepeatDayInfo, TaskRepeatWeekDayInfo, TaskseriesService} from "../../../api";
|
import {TaskEntityInfo, TaskRepeatDayInfo, TaskRepeatWeekDayInfo, TaskseriesService} from "../../../api";
|
||||||
import * as moment from "moment";
|
import * as moment from "moment";
|
||||||
import {FormArray, FormBuilder, FormGroup, Validators} from "@angular/forms";
|
import {FormArray, FormBuilder, FormGroup, Validators} from "@angular/forms";
|
||||||
import DeadlineStrategyEnum = TaskRepeatDayInfo.DeadlineStrategyEnum;
|
import DayOfWeekEnum = TaskRepeatWeekDayInfo.DayOfWeekEnum;
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -14,7 +14,6 @@ import DeadlineStrategyEnum = TaskRepeatDayInfo.DeadlineStrategyEnum;
|
|||||||
export class TaskWeeklySeriesCreatorComponent implements OnInit{
|
export class TaskWeeklySeriesCreatorComponent implements OnInit{
|
||||||
|
|
||||||
repeatingOffsetForm: FormGroup | undefined
|
repeatingOffsetForm: FormGroup | undefined
|
||||||
availableDeadlineStrategys: DeadlineStrategyEnum[] = ["DEADLINE_EQUAL_START", "DEADLINE_FIT_START"]
|
|
||||||
|
|
||||||
constructor(private dialogRef: MatDialogRef<TaskWeeklySeriesCreatorComponent>,
|
constructor(private dialogRef: MatDialogRef<TaskWeeklySeriesCreatorComponent>,
|
||||||
private taskRepeatingService: TaskseriesService,
|
private taskRepeatingService: TaskseriesService,
|
||||||
@ -27,7 +26,6 @@ export class TaskWeeklySeriesCreatorComponent implements OnInit{
|
|||||||
offsets: this.formbuilder.array(this.tasks.map(task => this.formbuilder.group({
|
offsets: this.formbuilder.array(this.tasks.map(task => this.formbuilder.group({
|
||||||
offsetCtrl: ['', [Validators.required, Validators.min(1)]]
|
offsetCtrl: ['', [Validators.required, Validators.min(1)]]
|
||||||
}))),
|
}))),
|
||||||
deadlineStrategyCtrl: ['', Validators.required],
|
|
||||||
endingDateCtrl: ['', Validators.required]
|
endingDateCtrl: ['', Validators.required]
|
||||||
|
|
||||||
})
|
})
|
||||||
@ -62,12 +60,12 @@ export class TaskWeeklySeriesCreatorComponent implements OnInit{
|
|||||||
for(let i=0; i<formArrayValues.length; i++) {
|
for(let i=0; i<formArrayValues.length; i++) {
|
||||||
weekDayInfos.push({
|
weekDayInfos.push({
|
||||||
taskID: this.tasks[i].taskID,
|
taskID: this.tasks[i].taskID,
|
||||||
offset: Number(formArrayValues[i].offsetCtrl) * 7
|
offset: Number(formArrayValues[i].offsetCtrl) * 7,
|
||||||
|
dayOfWeek: this.getDayOfWeekFromTask(this.tasks[i])!
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
this.taskRepeatingService.tasksTaskseriesWeeklyPost({
|
this.taskRepeatingService.tasksTaskseriesWeeklyPost({
|
||||||
deadlineStrategy: this.repeatingOffsetForm!.controls['deadlineStrategyCtrl']!.value!,
|
|
||||||
endDate: moment(this.repeatingOffsetForm!.controls['endingDateCtrl']!.value!).format('YYYY-MM-DDTHH:mm:ss.SSSZ'),
|
endDate: moment(this.repeatingOffsetForm!.controls['endingDateCtrl']!.value!).format('YYYY-MM-DDTHH:mm:ss.SSSZ'),
|
||||||
weekDayInfos: weekDayInfos
|
weekDayInfos: weekDayInfos
|
||||||
}).subscribe({
|
}).subscribe({
|
||||||
@ -77,6 +75,21 @@ export class TaskWeeklySeriesCreatorComponent implements OnInit{
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getDayOfWeekFromTask(task: TaskEntityInfo): DayOfWeekEnum | undefined {
|
||||||
|
const weekday = moment(task.startDate).isoWeekday();
|
||||||
|
|
||||||
|
switch (weekday) {
|
||||||
|
case 1: return DayOfWeekEnum.Monday;
|
||||||
|
case 2: return DayOfWeekEnum.Tuesday;
|
||||||
|
case 3: return DayOfWeekEnum.Wednesday;
|
||||||
|
case 4: return DayOfWeekEnum.Thursday;
|
||||||
|
case 5: return DayOfWeekEnum.Friday;
|
||||||
|
case 6: return DayOfWeekEnum.Saturday;
|
||||||
|
case 7: return DayOfWeekEnum.Sunday;
|
||||||
|
default: return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cancel() {
|
cancel() {
|
||||||
this.dialogRef.close(false);
|
this.dialogRef.close(false);
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,8 @@ export class UpcomingTaskOverviewComponent implements OnInit{
|
|||||||
editTask(editedTask: TaskTaskgroupInfo) {
|
editTask(editedTask: TaskTaskgroupInfo) {
|
||||||
const taskEditorInfo: TaskEditorData = {
|
const taskEditorInfo: TaskEditorData = {
|
||||||
task: editedTask,
|
task: editedTask,
|
||||||
taskgroupID: editedTask.taskgroups[editedTask.taskgroups.length-1].taskgroupID
|
taskgroupID: editedTask.taskgroups[editedTask.taskgroups.length-1].taskgroupID,
|
||||||
|
parentTask: undefined
|
||||||
};
|
};
|
||||||
this.dialog.open(TaskEditorComponent, {data: taskEditorInfo, width: "600px"})
|
this.dialog.open(TaskEditorComponent, {data: taskEditorInfo, width: "600px"})
|
||||||
}
|
}
|
||||||
|
152
openapi.yaml
152
openapi.yaml
@ -1269,6 +1269,120 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
type: object
|
type: object
|
||||||
$ref: "#/components/schemas/SimpleStatusResponse"
|
$ref: "#/components/schemas/SimpleStatusResponse"
|
||||||
|
/tasks/{taskID}/subtasks:
|
||||||
|
put:
|
||||||
|
security:
|
||||||
|
- API_TOKEN: []
|
||||||
|
tags:
|
||||||
|
- task
|
||||||
|
description: Create Subtask
|
||||||
|
summary: Creates Subtask
|
||||||
|
parameters:
|
||||||
|
- name: taskID
|
||||||
|
in: path
|
||||||
|
description: internal id of task
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: number
|
||||||
|
example: 1
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/TaskFieldInfo'
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Operation successfull
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/TaskEntityInfo'
|
||||||
|
403:
|
||||||
|
description: No permission
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/SimpleStatusResponse'
|
||||||
|
404:
|
||||||
|
description: Task not found
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/SimpleStatusResponse'
|
||||||
|
400:
|
||||||
|
description: Invalid start/end date
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/SimpleStatusResponse'
|
||||||
|
get:
|
||||||
|
security:
|
||||||
|
- API_TOKEN: []
|
||||||
|
tags:
|
||||||
|
- task
|
||||||
|
parameters:
|
||||||
|
- name: taskID
|
||||||
|
in: path
|
||||||
|
description: internal id of task
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: number
|
||||||
|
example: 1
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Operation successfull
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/TaskEntityInfo'
|
||||||
|
403:
|
||||||
|
description: No permission
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/SimpleStatusResponse'
|
||||||
|
404:
|
||||||
|
description: Task not found
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/SimpleStatusResponse'
|
||||||
|
delete:
|
||||||
|
security:
|
||||||
|
- API_TOKEN: []
|
||||||
|
tags:
|
||||||
|
- task
|
||||||
|
parameters:
|
||||||
|
- name: taskID
|
||||||
|
in: path
|
||||||
|
description: internal id of task
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: number
|
||||||
|
example: 1
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Operation successfull
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/SimpleStatusResponse'
|
||||||
|
403:
|
||||||
|
description: No permission
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/SimpleStatusResponse'
|
||||||
|
404:
|
||||||
|
description: Task not found
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/SimpleStatusResponse'
|
||||||
|
|
||||||
|
|
||||||
/schedules:
|
/schedules:
|
||||||
get:
|
get:
|
||||||
security:
|
security:
|
||||||
@ -2415,6 +2529,8 @@ components:
|
|||||||
- hasActiveSchedules
|
- hasActiveSchedules
|
||||||
- hasPlannedSchedules
|
- hasPlannedSchedules
|
||||||
- hasTaskSerie
|
- hasTaskSerie
|
||||||
|
- hasSubtasks
|
||||||
|
- hasParent
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
properties:
|
properties:
|
||||||
taskID:
|
taskID:
|
||||||
@ -2463,6 +2579,14 @@ components:
|
|||||||
type: boolean
|
type: boolean
|
||||||
description: determines whether the task is associated with a taskserie
|
description: determines whether the task is associated with a taskserie
|
||||||
example: false
|
example: false
|
||||||
|
hasSubtasks:
|
||||||
|
type: boolean
|
||||||
|
description: determines whether a task has subtasks
|
||||||
|
example: true
|
||||||
|
hasParent:
|
||||||
|
type: boolean
|
||||||
|
description: determines whether a task is a top task or Not
|
||||||
|
example: false
|
||||||
TaskTaskgroupInfo:
|
TaskTaskgroupInfo:
|
||||||
required:
|
required:
|
||||||
- taskID
|
- taskID
|
||||||
@ -2714,6 +2838,7 @@ components:
|
|||||||
- overdue
|
- overdue
|
||||||
- taskgroupPath
|
- taskgroupPath
|
||||||
- finishable
|
- finishable
|
||||||
|
- hasSubtasks
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
properties:
|
properties:
|
||||||
taskID:
|
taskID:
|
||||||
@ -2748,6 +2873,9 @@ components:
|
|||||||
finishable:
|
finishable:
|
||||||
type: boolean
|
type: boolean
|
||||||
description: determines whether the task can be finished
|
description: determines whether the task can be finished
|
||||||
|
hasSubtasks:
|
||||||
|
type: boolean
|
||||||
|
description: determines whether the task has subtasks
|
||||||
ScheduleStatus:
|
ScheduleStatus:
|
||||||
required:
|
required:
|
||||||
- activeMinutes
|
- activeMinutes
|
||||||
@ -2866,7 +2994,6 @@ components:
|
|||||||
TaskRepeatDayInfo:
|
TaskRepeatDayInfo:
|
||||||
required:
|
required:
|
||||||
- offset
|
- offset
|
||||||
- deadlineStrategy
|
|
||||||
- endingDate
|
- endingDate
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
properties:
|
properties:
|
||||||
@ -2875,11 +3002,6 @@ components:
|
|||||||
description: number repeating days
|
description: number repeating days
|
||||||
example: 7
|
example: 7
|
||||||
minimum: 1
|
minimum: 1
|
||||||
deadlineStrategy:
|
|
||||||
type: string
|
|
||||||
enum:
|
|
||||||
- DEADLINE_EQUAL_START
|
|
||||||
- DEADLINE_FIT_START
|
|
||||||
endingDate:
|
endingDate:
|
||||||
type: string
|
type: string
|
||||||
format: date
|
format: date
|
||||||
@ -2888,6 +3010,7 @@ components:
|
|||||||
required:
|
required:
|
||||||
- offset
|
- offset
|
||||||
- taskID
|
- taskID
|
||||||
|
- dayOfWeek
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
properties:
|
properties:
|
||||||
offset:
|
offset:
|
||||||
@ -2899,18 +3022,23 @@ components:
|
|||||||
type: number
|
type: number
|
||||||
description: internal identifier of task
|
description: internal identifier of task
|
||||||
example: 1
|
example: 1
|
||||||
|
dayOfWeek:
|
||||||
|
type: string
|
||||||
|
description: day of week
|
||||||
|
enum:
|
||||||
|
- MONDAY
|
||||||
|
- TUESDAY
|
||||||
|
- WEDNESDAY
|
||||||
|
- THURSDAY
|
||||||
|
- FRIDAY
|
||||||
|
- SATURDAY
|
||||||
|
- SUNDAY
|
||||||
TaskRepeatWeekInfo:
|
TaskRepeatWeekInfo:
|
||||||
required:
|
required:
|
||||||
- weekDayInfos
|
- weekDayInfos
|
||||||
- deadlineStrategy
|
|
||||||
- endDate
|
- endDate
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
properties:
|
properties:
|
||||||
deadlineStrategy:
|
|
||||||
type: string
|
|
||||||
enum:
|
|
||||||
- DEADLINE_EQUAL_START
|
|
||||||
- DEADLINE_FIT_START
|
|
||||||
endDate:
|
endDate:
|
||||||
type: string
|
type: string
|
||||||
format: date
|
format: date
|
||||||
|
Loading…
Reference in New Issue
Block a user