issue-47 #60
@ -4,10 +4,8 @@
|
|||||||
<option name="autoReloadType" value="SELECTIVE" />
|
<option name="autoReloadType" value="SELECTIVE" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ChangeListManager">
|
<component name="ChangeListManager">
|
||||||
<list default="true" id="3a869f59-290a-4ab2-b036-a878ce801bc4" name="Changes" comment="Fix wrong date">
|
<list default="true" id="3a869f59-290a-4ab2-b036-a878ce801bc4" name="Changes" comment="Edit and Delete in Draggable Scheduler">
|
||||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/src/main/java/core/entities/timemanager/AdvancedTaskSchedule.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/core/entities/timemanager/AdvancedTaskSchedule.java" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/src/main/java/core/services/TaskScheduleService.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/core/services/TaskScheduleService.java" afterDir="false" />
|
|
||||||
</list>
|
</list>
|
||||||
<option name="SHOW_DIALOG" value="false" />
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||||
@ -26,7 +24,7 @@
|
|||||||
<component name="Git.Settings">
|
<component name="Git.Settings">
|
||||||
<option name="RECENT_BRANCH_BY_REPOSITORY">
|
<option name="RECENT_BRANCH_BY_REPOSITORY">
|
||||||
<map>
|
<map>
|
||||||
<entry key="$PROJECT_DIR$/.." value="test-failing" />
|
<entry key="$PROJECT_DIR$/.." value="docker-deployment" />
|
||||||
</map>
|
</map>
|
||||||
</option>
|
</option>
|
||||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$/.." />
|
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$/.." />
|
||||||
@ -34,6 +32,9 @@
|
|||||||
<component name="JpbToolWindowState">
|
<component name="JpbToolWindowState">
|
||||||
<option name="isToolWindowVisible" value="false" />
|
<option name="isToolWindowVisible" value="false" />
|
||||||
</component>
|
</component>
|
||||||
|
<component name="MavenRunner">
|
||||||
|
<option name="skipTests" value="true" />
|
||||||
|
</component>
|
||||||
<component name="ProjectColorInfo">{
|
<component name="ProjectColorInfo">{
|
||||||
"customColor": "",
|
"customColor": "",
|
||||||
"associatedIndex": 6
|
"associatedIndex": 6
|
||||||
@ -548,18 +549,53 @@
|
|||||||
<option name="project" value="LOCAL" />
|
<option name="project" value="LOCAL" />
|
||||||
<updated>1699786723938</updated>
|
<updated>1699786723938</updated>
|
||||||
</task>
|
</task>
|
||||||
<option name="localTasksCounter" value="42" />
|
<task id="LOCAL-00042" summary="Deploy on docker (not productive yet!)">
|
||||||
|
<option name="closed" value="true" />
|
||||||
|
<created>1699803821051</created>
|
||||||
|
<option name="number" value="00042" />
|
||||||
|
<option name="presentableId" value="LOCAL-00042" />
|
||||||
|
<option name="project" value="LOCAL" />
|
||||||
|
<updated>1699803821051</updated>
|
||||||
|
</task>
|
||||||
|
<task id="LOCAL-00043" summary="Fix deleting tasks">
|
||||||
|
<option name="closed" value="true" />
|
||||||
|
<created>1699805283751</created>
|
||||||
|
<option name="number" value="00043" />
|
||||||
|
<option name="presentableId" value="LOCAL-00043" />
|
||||||
|
<option name="project" value="LOCAL" />
|
||||||
|
<updated>1699805283751</updated>
|
||||||
|
</task>
|
||||||
|
<task id="LOCAL-00044" summary="Fix parsing datetime">
|
||||||
|
<option name="closed" value="true" />
|
||||||
|
<created>1699806194258</created>
|
||||||
|
<option name="number" value="00044" />
|
||||||
|
<option name="presentableId" value="LOCAL-00044" />
|
||||||
|
<option name="project" value="LOCAL" />
|
||||||
|
<updated>1699806194258</updated>
|
||||||
|
</task>
|
||||||
|
<task id="LOCAL-00045" summary="Fix parsing datetime (diesmal hoffentlich wirklich)">
|
||||||
|
<option name="closed" value="true" />
|
||||||
|
<created>1699809089060</created>
|
||||||
|
<option name="number" value="00045" />
|
||||||
|
<option name="presentableId" value="LOCAL-00045" />
|
||||||
|
<option name="project" value="LOCAL" />
|
||||||
|
<updated>1699809089060</updated>
|
||||||
|
</task>
|
||||||
|
<task id="LOCAL-00046" summary="Edit and Delete in Draggable Scheduler">
|
||||||
|
<option name="closed" value="true" />
|
||||||
|
<created>1699824923378</created>
|
||||||
|
<option name="number" value="00046" />
|
||||||
|
<option name="presentableId" value="LOCAL-00046" />
|
||||||
|
<option name="project" value="LOCAL" />
|
||||||
|
<updated>1699824923378</updated>
|
||||||
|
</task>
|
||||||
|
<option name="localTasksCounter" value="47" />
|
||||||
<servers />
|
<servers />
|
||||||
</component>
|
</component>
|
||||||
<component name="TypeScriptGeneratedFilesManager">
|
<component name="TypeScriptGeneratedFilesManager">
|
||||||
<option name="version" value="3" />
|
<option name="version" value="3" />
|
||||||
</component>
|
</component>
|
||||||
<component name="VcsManagerConfiguration">
|
<component name="VcsManagerConfiguration">
|
||||||
<MESSAGE value="List missed Schedules" />
|
|
||||||
<MESSAGE value="Forget single schedule" />
|
|
||||||
<MESSAGE value="Fix marking finished task as overdue" />
|
|
||||||
<MESSAGE value="Write simple Testcase for ci/cd" />
|
|
||||||
<MESSAGE value="Deactivate Overall System test (temporarly)" />
|
|
||||||
<MESSAGE value="Fix failing test case" />
|
<MESSAGE value="Fix failing test case" />
|
||||||
<MESSAGE value="TaskgroupRepsitoryTest" />
|
<MESSAGE value="TaskgroupRepsitoryTest" />
|
||||||
<MESSAGE value="TaskgroupRepsitoryTest (+Delete)" />
|
<MESSAGE value="TaskgroupRepsitoryTest (+Delete)" />
|
||||||
@ -580,7 +616,12 @@
|
|||||||
<MESSAGE value="Update values of datetime picker automatically for editing" />
|
<MESSAGE value="Update values of datetime picker automatically for editing" />
|
||||||
<MESSAGE value="Adapt datetime-picker" />
|
<MESSAGE value="Adapt datetime-picker" />
|
||||||
<MESSAGE value="Fix wrong date" />
|
<MESSAGE value="Fix wrong date" />
|
||||||
<option name="LAST_COMMIT_MESSAGE" value="Fix wrong date" />
|
<MESSAGE value="Deploy on docker (not productive yet!)" />
|
||||||
|
<MESSAGE value="Fix deleting tasks" />
|
||||||
|
<MESSAGE value="Fix parsing datetime" />
|
||||||
|
<MESSAGE value="Fix parsing datetime (diesmal hoffentlich wirklich)" />
|
||||||
|
<MESSAGE value="Edit and Delete in Draggable Scheduler" />
|
||||||
|
<option name="LAST_COMMIT_MESSAGE" value="Edit and Delete in Draggable Scheduler" />
|
||||||
</component>
|
</component>
|
||||||
<component name="XDebuggerManager">
|
<component name="XDebuggerManager">
|
||||||
<breakpoint-manager>
|
<breakpoint-manager>
|
||||||
@ -592,13 +633,18 @@
|
|||||||
</line-breakpoint>
|
</line-breakpoint>
|
||||||
<line-breakpoint enabled="true" type="java-line">
|
<line-breakpoint enabled="true" type="java-line">
|
||||||
<url>file://$PROJECT_DIR$/src/main/java/core/api/controller/ScheduleController.java</url>
|
<url>file://$PROJECT_DIR$/src/main/java/core/api/controller/ScheduleController.java</url>
|
||||||
<line>68</line>
|
<line>98</line>
|
||||||
<option name="timeStamp" value="14" />
|
<option name="timeStamp" value="16" />
|
||||||
|
</line-breakpoint>
|
||||||
|
<line-breakpoint enabled="true" type="java-line">
|
||||||
|
<url>file://$PROJECT_DIR$/src/main/java/core/services/TaskService.java</url>
|
||||||
|
<line>93</line>
|
||||||
|
<option name="timeStamp" value="35" />
|
||||||
</line-breakpoint>
|
</line-breakpoint>
|
||||||
<line-breakpoint enabled="true" type="java-line">
|
<line-breakpoint enabled="true" type="java-line">
|
||||||
<url>file://$PROJECT_DIR$/src/main/java/core/api/controller/ScheduleController.java</url>
|
<url>file://$PROJECT_DIR$/src/main/java/core/api/controller/ScheduleController.java</url>
|
||||||
<line>93</line>
|
<line>83</line>
|
||||||
<option name="timeStamp" value="16" />
|
<option name="timeStamp" value="36" />
|
||||||
</line-breakpoint>
|
</line-breakpoint>
|
||||||
</breakpoints>
|
</breakpoints>
|
||||||
</breakpoint-manager>
|
</breakpoint-manager>
|
||||||
|
@ -1,9 +1,15 @@
|
|||||||
|
FROM maven:3.8.1-openjdk-17-slim AS build
|
||||||
|
COPY src /home/app/src
|
||||||
|
COPY pom.xml /home/app
|
||||||
|
RUN mvn -f /home/app/pom.xml clean test package
|
||||||
|
|
||||||
|
|
||||||
FROM openjdk:17-jdk-alpine
|
FROM openjdk:17-jdk-alpine
|
||||||
RUN addgroup -S spring && adduser -S spring -G spring
|
RUN addgroup -S spring && adduser -S spring -G spring
|
||||||
USER spring:spring
|
USER spring:spring
|
||||||
|
|
||||||
ARG JAR_FILE=target/*.jar
|
#ARG JAR_FILE=target/*.jar
|
||||||
COPY ${JAR_FILE} app.jar
|
#COPY ${JAR_FILE} app.jar
|
||||||
|
COPY --from=build /home/app/target/*.jar app.jar
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
ENTRYPOINT ["java","-jar","/app.jar"]
|
ENTRYPOINT ["java","-jar","/app.jar"]
|
@ -78,7 +78,12 @@ public class ScheduleController {
|
|||||||
return ResponseEntity.ok(scheduleResult.getResult().toScheduleInfo());
|
return ResponseEntity.ok(scheduleResult.getResult().toScheduleInfo());
|
||||||
} else if(scheduleFieldInfo instanceof AdvancedScheduleFieldInfo) {
|
} else if(scheduleFieldInfo instanceof AdvancedScheduleFieldInfo) {
|
||||||
ServiceResult<AbstractSchedule> scheduleResult = taskScheduleService.scheduleAdvanced(permissionResult.getResult(), (AdvancedScheduleFieldInfo) scheduleFieldInfo);
|
ServiceResult<AbstractSchedule> scheduleResult = taskScheduleService.scheduleAdvanced(permissionResult.getResult(), (AdvancedScheduleFieldInfo) scheduleFieldInfo);
|
||||||
return ResponseEntity.ok(scheduleResult.getResult().toScheduleInfo());
|
if(scheduleResult.getResult() != null) {
|
||||||
|
return ResponseEntity.ok(scheduleResult.getResult().toScheduleInfo());
|
||||||
|
} else {
|
||||||
|
return ResponseEntity.status(400).body(new SimpleStatusResponse("failed"));
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return ResponseEntity.status(400).body(new SimpleStatusResponse("failed"));
|
return ResponseEntity.status(400).body(new SimpleStatusResponse("failed"));
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package core.api.models.timemanager.taskSchedule.scheduleInfos;
|
|||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
@ -10,9 +11,11 @@ public class AdvancedScheduleFieldInfo extends ScheduleFieldInfo {
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX")
|
||||||
private LocalDateTime scheduleStartTime;
|
private LocalDateTime scheduleStartTime;
|
||||||
@NotNull
|
@NotNull
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX")
|
||||||
private LocalDateTime scheduleStopTime;
|
private LocalDateTime scheduleStopTime;
|
||||||
|
|
||||||
public AdvancedScheduleFieldInfo(LocalDateTime scheduleStartTime, LocalDateTime scheduleStopTime) {
|
public AdvancedScheduleFieldInfo(LocalDateTime scheduleStartTime, LocalDateTime scheduleStopTime) {
|
||||||
|
@ -8,6 +8,7 @@ import java.time.LocalDate;
|
|||||||
public class BasicScheduleFieldInfo extends ScheduleFieldInfo{
|
public class BasicScheduleFieldInfo extends ScheduleFieldInfo{
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX")
|
||||||
private LocalDate scheduleDate;
|
private LocalDate scheduleDate;
|
||||||
|
|
||||||
public BasicScheduleFieldInfo(LocalDate localDate) {
|
public BasicScheduleFieldInfo(LocalDate localDate) {
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
package core.api.models.timemanager.tasks;
|
package core.api.models.timemanager.tasks;
|
||||||
|
|
||||||
|
import core.api.models.timemanager.taskgroup.TaskgroupEntityInfo;
|
||||||
import core.entities.timemanager.Task;
|
import core.entities.timemanager.Task;
|
||||||
|
import core.entities.timemanager.Taskgroup;
|
||||||
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class TaskOverviewInfo {
|
public class TaskOverviewInfo {
|
||||||
|
|
||||||
@ -13,6 +16,7 @@ public class TaskOverviewInfo {
|
|||||||
private LocalDate limit;
|
private LocalDate limit;
|
||||||
private boolean overdue;
|
private boolean overdue;
|
||||||
|
|
||||||
|
|
||||||
public TaskOverviewInfo(Task task) {
|
public TaskOverviewInfo(Task task) {
|
||||||
this.taskID = task.getTaskID();
|
this.taskID = task.getTaskID();
|
||||||
this.taskName = task.getTaskName();
|
this.taskName = task.getTaskName();
|
||||||
|
@ -13,8 +13,6 @@ import java.util.List;
|
|||||||
@Repository
|
@Repository
|
||||||
public interface TaskgroupRepository extends CrudRepository<Taskgroup, Long> {
|
public interface TaskgroupRepository extends CrudRepository<Taskgroup, Long> {
|
||||||
|
|
||||||
boolean existsByTaskgroupNameAndUser(String name, User user);
|
|
||||||
|
|
||||||
@Query("SELECT tg FROM Taskgroup tg WHERE tg.user.username = ?1")
|
@Query("SELECT tg FROM Taskgroup tg WHERE tg.user.username = ?1")
|
||||||
List<Taskgroup> findAllByUser(String username);
|
List<Taskgroup> findAllByUser(String username);
|
||||||
|
|
||||||
|
@ -96,6 +96,9 @@ public class TaskScheduleService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void deleteSchedule(AbstractSchedule schedule) {
|
public void deleteSchedule(AbstractSchedule schedule) {
|
||||||
|
schedule.getTask().getBasicTaskSchedules().remove(schedule);
|
||||||
|
schedule.setTask(null);
|
||||||
|
scheduleRepository.save(schedule);
|
||||||
scheduleRepository.delete(schedule);
|
scheduleRepository.delete(schedule);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,11 +77,6 @@ public class TaskService {
|
|||||||
return new ServiceResult<>(ServiceExitCode.INVALID_PARAMETER);
|
return new ServiceResult<>(ServiceExitCode.INVALID_PARAMETER);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Check for invalid date (deadline before start
|
|
||||||
if(taskFieldInfo.getStartDate() != null && taskFieldInfo.getDeadline() != null &&
|
|
||||||
taskFieldInfo.getDeadline().isBefore(taskFieldInfo.getStartDate())) {
|
|
||||||
return new ServiceResult<>(ServiceExitCode.INVALID_PARAMETER);
|
|
||||||
}
|
|
||||||
task.setEta(taskFieldInfo.getEta());
|
task.setEta(taskFieldInfo.getEta());
|
||||||
task.setStartDate(taskFieldInfo.getStartDate());
|
task.setStartDate(taskFieldInfo.getStartDate());
|
||||||
task.setDeadline(taskFieldInfo.getDeadline());
|
task.setDeadline(taskFieldInfo.getDeadline());
|
||||||
@ -91,7 +86,12 @@ public class TaskService {
|
|||||||
|
|
||||||
public void deleteTask(Task task) {
|
public void deleteTask(Task task) {
|
||||||
//taskScheduleService.deleteScheduleByTask(task);
|
//taskScheduleService.deleteScheduleByTask(task);
|
||||||
taskRepository.deleteByTaskID(task.getTaskID());
|
System.err.println(task.getTaskID());
|
||||||
|
task.getTaskgroup().getTasks().remove(task);
|
||||||
|
taskgroupRepository.save(task.getTaskgroup());
|
||||||
|
task.setTaskgroup(null);
|
||||||
|
taskRepository.save(task);
|
||||||
|
taskRepository.delete(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearTasks(Taskgroup taskgroup) {
|
public void clearTasks(Taskgroup taskgroup) {
|
||||||
|
@ -39,32 +39,34 @@ public class TaskgroupService {
|
|||||||
throw new NoSuchElementException();
|
throw new NoSuchElementException();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!taskgroupRepository.existsByTaskgroupNameAndUser(taskData.getName(), user.get())) {
|
Taskgroup taskgroup;
|
||||||
Taskgroup taskgroup;
|
if(taskData.getParentID() < 0) {
|
||||||
if(taskData.getParentID() < 0) {
|
if(existTaskgroupOnTaskgroupLevel(null, taskData.getName(), username)) {
|
||||||
taskgroup = new Taskgroup(taskData.getName(), user.get());
|
return new ServiceResult<>(ServiceExitCode.ENTITY_ALREADY_EXIST);
|
||||||
} else {
|
} else {
|
||||||
Optional<Taskgroup> parentTaskgroup = taskgroupRepository.findById(taskData.getParentID());
|
taskgroup = new Taskgroup(taskData.getName(), user.get());
|
||||||
if(parentTaskgroup.isEmpty()) {
|
|
||||||
return new ServiceResult<>(ServiceExitCode.MISSING_ENTITY);
|
|
||||||
} else {
|
|
||||||
taskgroup = new Taskgroup(taskData.getName(), user.get());
|
|
||||||
taskgroup.setParent(parentTaskgroup.get());
|
|
||||||
parentTaskgroup.get().getChildren().add(taskgroup);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
taskgroupRepository.save(taskgroup);
|
} else{
|
||||||
return new ServiceResult<>(taskgroup);
|
Optional<Taskgroup> parentTaskgroup = taskgroupRepository.findById(taskData.getParentID());
|
||||||
} else {
|
if(parentTaskgroup.isEmpty()) {
|
||||||
return new ServiceResult<>(ServiceExitCode.ENTITY_ALREADY_EXIST);
|
return new ServiceResult<>(ServiceExitCode.MISSING_ENTITY);
|
||||||
|
} else if(existTaskgroupOnTaskgroupLevel(parentTaskgroup.get(), taskData.getName(), username)) {
|
||||||
|
return new ServiceResult<>(ServiceExitCode.ENTITY_ALREADY_EXIST);
|
||||||
|
} else {
|
||||||
|
taskgroup = new Taskgroup(taskData.getName(), user.get());
|
||||||
|
taskgroup.setParent(parentTaskgroup.get());
|
||||||
|
parentTaskgroup.get().getChildren().add(taskgroup);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
taskgroupRepository.save(taskgroup);
|
||||||
|
return new ServiceResult<>(taskgroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ServiceExitCode editTaskgroup(Taskgroup taskgroup, TaskgroupFieldInfo taskgroupFieldInfo) {
|
public ServiceExitCode editTaskgroup(Taskgroup taskgroup, TaskgroupFieldInfo taskgroupFieldInfo) {
|
||||||
if(!taskgroup.getTaskgroupName().equals(taskgroupFieldInfo.getName())) {
|
if(!taskgroup.getTaskgroupName().equals(taskgroupFieldInfo.getName())) {
|
||||||
//Check if another taskgroup with the new name is already existent for the user of the taskgroup
|
//Check if another taskgroup with the new name is already existent for the user of the taskgroup
|
||||||
if(!taskgroupRepository.existsByTaskgroupNameAndUser(taskgroupFieldInfo.getName(), taskgroup.getUser())) {
|
if(!existTaskgroupOnTaskgroupLevel(taskgroup.getParent(), taskgroupFieldInfo.getName(), taskgroup.getUser().getUsername())) {
|
||||||
taskgroup.setTaskgroupName(taskgroupFieldInfo.getName());
|
taskgroup.setTaskgroupName(taskgroupFieldInfo.getName());
|
||||||
taskgroupRepository.save(taskgroup);
|
taskgroupRepository.save(taskgroup);
|
||||||
return ServiceExitCode.OK;
|
return ServiceExitCode.OK;
|
||||||
@ -76,6 +78,24 @@ public class TaskgroupService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean existTaskgroupOnTaskgroupLevel(Taskgroup taskgroup, String taskgroupName, String username) {
|
||||||
|
if(taskgroup == null) {
|
||||||
|
List<Taskgroup> topTaskgroups = getTopTaskgroupsByUser(username);
|
||||||
|
for(Taskgroup taskgroup1 : topTaskgroups) {
|
||||||
|
if(taskgroup1.getTaskgroupName().equals(taskgroupName)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for(Taskgroup sibling : taskgroup.getChildren()) {
|
||||||
|
if(sibling.getTaskgroupName().equals(taskgroupName)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public void deleteTaskgroup(Taskgroup taskgroup) {
|
public void deleteTaskgroup(Taskgroup taskgroup) {
|
||||||
taskgroupRepository.delete(taskgroup);
|
taskgroupRepository.delete(taskgroup);
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ spring.jpa.open-in-view=false
|
|||||||
spring.data.rest.base-path=/api
|
spring.data.rest.base-path=/api
|
||||||
|
|
||||||
# Spring Server setup
|
# Spring Server setup
|
||||||
server.address=127.0.0.1
|
server.address=0.0.0.0
|
||||||
server.port=8080
|
server.port=8080
|
||||||
server.compression.enabled=true
|
server.compression.enabled=true
|
||||||
server.http2.enabled=true
|
server.http2.enabled=true
|
||||||
@ -42,3 +42,4 @@ spring.servlet.multipart.max-request-size=4196KB
|
|||||||
demo.webapp.jwtSecret=demoWebappSecretKey
|
demo.webapp.jwtSecret=demoWebappSecretKey
|
||||||
demo.webapp.jwtExpirationMS=86400000
|
demo.webapp.jwtExpirationMS=86400000
|
||||||
spring.jackson.time-zone=UTC
|
spring.jackson.time-zone=UTC
|
||||||
|
server.servlet.session.cookie.samesite=None
|
@ -37,25 +37,6 @@ public class TaskgroupRepsitoryTest {
|
|||||||
assertEquals(8, result_user1.size());
|
assertEquals(8, result_user1.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
@Sql("classpath:taskgroupRepositoryTestEntries.sql")
|
|
||||||
void existsByTaskgroupNameAndUser() {
|
|
||||||
User testUser1 = testEntityManager.find(User.class, 1L);
|
|
||||||
User testUser2 = testEntityManager.find(User.class, 2L);
|
|
||||||
|
|
||||||
//Situation 1: Taskgroup exists but within another user
|
|
||||||
assertFalse(taskgroupRepository.existsByTaskgroupNameAndUser("No children", testUser2));
|
|
||||||
|
|
||||||
//Situation 2: Taskgroup exists not
|
|
||||||
assertFalse(taskgroupRepository.existsByTaskgroupNameAndUser("ada", testUser1));
|
|
||||||
|
|
||||||
//Situation 3: Taskgroup (top) exists on user
|
|
||||||
assertTrue(taskgroupRepository.existsByTaskgroupNameAndUser("No children", testUser1));
|
|
||||||
|
|
||||||
//Situation 4: Taskgroup exists on user but not on top level
|
|
||||||
assertTrue(taskgroupRepository.existsByTaskgroupNameAndUser("Taskgroup 1.1", testUser1));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Sql("classpath:taskgroupRepositoryTestEntries.sql")
|
@Sql("classpath:taskgroupRepositoryTestEntries.sql")
|
||||||
void findAllTopTaskgroupsByUser() {
|
void findAllTopTaskgroupsByUser() {
|
||||||
|
@ -4,7 +4,7 @@ services:
|
|||||||
|
|
||||||
db:
|
db:
|
||||||
image: mariadb:latest
|
image: mariadb:latest
|
||||||
container_name: template_mysql
|
container_name: timemanager-db
|
||||||
environment:
|
environment:
|
||||||
MYSQL_ROOT_PASSWORD: edkvcjReDxJ9Z8hq
|
MYSQL_ROOT_PASSWORD: edkvcjReDxJ9Z8hq
|
||||||
backend:
|
backend:
|
||||||
@ -14,6 +14,7 @@ services:
|
|||||||
links:
|
links:
|
||||||
- "db:app_db"
|
- "db:app_db"
|
||||||
environment:
|
environment:
|
||||||
|
SERVER_ADDRESS: 0.0.0.0
|
||||||
SPRING_DATASOURCE_USERNAME: root
|
SPRING_DATASOURCE_USERNAME: root
|
||||||
SPRING_DATASOURCE_PASSWORD: edkvcjReDxJ9Z8hq
|
SPRING_DATASOURCE_PASSWORD: edkvcjReDxJ9Z8hq
|
||||||
SPRING_DATASOURCE_URL: jdbc:mariadb://app_db:3306/restservice?createDatabaseIfNotExist=true&autoReconnect=true
|
SPRING_DATASOURCE_URL: jdbc:mariadb://app_db:3306/restservice?createDatabaseIfNotExist=true&autoReconnect=true
|
||||||
@ -27,15 +28,9 @@ services:
|
|||||||
restart: always
|
restart: always
|
||||||
links:
|
links:
|
||||||
- backend:backend
|
- backend:backend
|
||||||
|
depends_on:
|
||||||
|
- "backend"
|
||||||
ports:
|
ports:
|
||||||
- "4200:80"
|
- "4200:80"
|
||||||
environment:
|
environment:
|
||||||
# change this env-variable in prod
|
BACKEND_URL: http://backend:8080
|
||||||
BACKEND_URL: http://backend/
|
|
||||||
# alternative way manipulate the api.json:
|
|
||||||
#{
|
|
||||||
# "api": "http://backend/"
|
|
||||||
#}
|
|
||||||
# bind-mount it read-only via:
|
|
||||||
#volumes:
|
|
||||||
# - ./api.json:/usr/share/nginx/html/assets/api.json:ro
|
|
||||||
|
@ -1,29 +1,23 @@
|
|||||||
FROM node:16-alpine as builder
|
### STAGE 1: Build ###
|
||||||
|
FROM node:18.17.1 AS build
|
||||||
|
WORKDIR /app
|
||||||
COPY package*.json ./
|
COPY package*.json ./
|
||||||
|
RUN npm install
|
||||||
RUN npm set progress=false && npm config set depth 0 && npm cache clean --force
|
|
||||||
|
|
||||||
RUN npm i && mkdir /ng-app && cp -R ./node_modules ./ng-app
|
|
||||||
|
|
||||||
WORKDIR /ng-app
|
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
RUN npm run build --prod
|
||||||
|
|
||||||
RUN $(npm bin)/ng build --configuration=production --build-optimizer
|
### STAGE 2: Deploy ###
|
||||||
|
FROM nginx:latest
|
||||||
|
RUN apt-get update
|
||||||
|
RUN apt-get upgrade -y
|
||||||
|
RUN apt-get install iputils-ping -y
|
||||||
|
COPY --from=build /app/dist/frontend /usr/share/nginx/html
|
||||||
|
COPY nginx.conf.template /etc/nginx/nginx.conf.template
|
||||||
|
COPY docker-entrypoint.sh /app-entrypoint.sh
|
||||||
|
|
||||||
|
RUN chmod +x /app-entrypoint.sh
|
||||||
|
|
||||||
FROM nginx:stable-alpine
|
ENTRYPOINT [ "/app-entrypoint.sh" ]
|
||||||
|
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
CMD ["nginx", "-g", "daemon off;"]
|
||||||
COPY nginx/nginx.conf.template /
|
|
||||||
|
|
||||||
RUN rm -rf /usr/share/nginx/html/*
|
|
||||||
|
|
||||||
COPY --from=builder /ng-app/dist/frontend/ /usr/share/nginx/html
|
|
||||||
RUN chown -R nginx:nginx /usr/share/nginx/html/
|
|
||||||
|
|
||||||
COPY nginx/run_nginx.sh /
|
|
||||||
|
|
||||||
CMD ["sh", "/run_nginx.sh"]
|
|
||||||
|
@ -38,8 +38,8 @@
|
|||||||
"budgets": [
|
"budgets": [
|
||||||
{
|
{
|
||||||
"type": "initial",
|
"type": "initial",
|
||||||
"maximumWarning": "500kb",
|
"maximumWarning": "2mb",
|
||||||
"maximumError": "1mb"
|
"maximumError": "5mb"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "anyComponentStyle",
|
"type": "anyComponentStyle",
|
||||||
|
5
frontend/docker-entrypoint.sh
Normal file
5
frontend/docker-entrypoint.sh
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
cd /etc/nginx/
|
||||||
|
envsubst '$BACKEND_URL' < nginx.conf.template > nginx.conf
|
||||||
|
exec "$@"
|
@ -1,7 +1,6 @@
|
|||||||
user nginx;
|
user nginx;
|
||||||
worker_processes 1;
|
worker_processes 1;
|
||||||
|
|
||||||
error_log /var/log/nginx/error.log warn;
|
|
||||||
pid /var/run/nginx.pid;
|
pid /var/run/nginx.pid;
|
||||||
|
|
||||||
|
|
||||||
@ -14,12 +13,6 @@ http {
|
|||||||
include /etc/nginx/mime.types;
|
include /etc/nginx/mime.types;
|
||||||
default_type application/octet-stream;
|
default_type application/octet-stream;
|
||||||
|
|
||||||
log_format main '${DOLLAR}remote_addr - ${DOLLAR}remote_user [${DOLLAR}time_local] "${DOLLAR}request" '
|
|
||||||
'${DOLLAR}status ${DOLLAR}body_bytes_sent "${DOLLAR}http_referer" '
|
|
||||||
'"${DOLLAR}http_user_agent" "${DOLLAR}http_x_forwarded_for"';
|
|
||||||
|
|
||||||
access_log /var/log/nginx/access.log main;
|
|
||||||
|
|
||||||
sendfile on;
|
sendfile on;
|
||||||
#tcp_nopush on;
|
#tcp_nopush on;
|
||||||
|
|
||||||
@ -40,12 +33,17 @@ http {
|
|||||||
add_header "Access-Control-Allow-Methods" 'GET, POST, OPTIONS, PUT, DELETE';
|
add_header "Access-Control-Allow-Methods" 'GET, POST, OPTIONS, PUT, DELETE';
|
||||||
add_header "Access-Control-Allow-Headers" 'X-Requested-With,Accept,Content-Type, Origin';
|
add_header "Access-Control-Allow-Headers" 'X-Requested-With,Accept,Content-Type, Origin';
|
||||||
|
|
||||||
location / {
|
location /api {
|
||||||
try_files ${DOLLAR}uri ${DOLLAR}uri/ /index.html;
|
proxy_pass ${BACKEND_URL};
|
||||||
|
add_header "Access-Control-Allow-Origin" "*";
|
||||||
|
add_header "Access-Control-Allow-Methods" 'GET, POST, OPTIONS, PUT, DELETE';
|
||||||
|
add_header "Access-Control-Allow-Headers" 'X-Requested-With,Accept,Content-Type, Origin';
|
||||||
}
|
}
|
||||||
|
|
||||||
location /api {
|
location / {
|
||||||
proxy_pass ${BACKEND_URL};
|
try_files $uri $uri/ /index.html =404;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,9 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# generate nginx-configuration (insert env variables)
|
|
||||||
|
|
||||||
export DOLLAR='$'
|
|
||||||
envsubst < ./nginx.conf.template > /etc/nginx/nginx.conf
|
|
||||||
|
|
||||||
# start nginx
|
|
||||||
exec nginx -g "daemon off;"
|
|
6
frontend/proxy.conf.json
Normal file
6
frontend/proxy.conf.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"/api": {
|
||||||
|
"target": "http://backend",
|
||||||
|
"secure": false
|
||||||
|
}
|
||||||
|
}
|
@ -35,7 +35,7 @@ import { Configuration } from '../configurat
|
|||||||
})
|
})
|
||||||
export class AccountService {
|
export class AccountService {
|
||||||
|
|
||||||
protected basePath = 'http://localhost:8080/api';
|
protected basePath = 'http://127.0.0.1:8080/api';
|
||||||
public defaultHeaders = new HttpHeaders();
|
public defaultHeaders = new HttpHeaders();
|
||||||
public configuration = new Configuration();
|
public configuration = new Configuration();
|
||||||
public encoder: HttpParameterCodec;
|
public encoder: HttpParameterCodec;
|
||||||
|
@ -31,7 +31,7 @@ import { Configuration } from '../configurat
|
|||||||
})
|
})
|
||||||
export class HistoryService {
|
export class HistoryService {
|
||||||
|
|
||||||
protected basePath = 'http://localhost:8080/api';
|
protected basePath = 'http://127.0.0.1:8080/api';
|
||||||
public defaultHeaders = new HttpHeaders();
|
public defaultHeaders = new HttpHeaders();
|
||||||
public configuration = new Configuration();
|
public configuration = new Configuration();
|
||||||
public encoder: HttpParameterCodec;
|
public encoder: HttpParameterCodec;
|
||||||
|
@ -36,7 +36,7 @@ import { Configuration } from '../configurat
|
|||||||
})
|
})
|
||||||
export class LoginService {
|
export class LoginService {
|
||||||
|
|
||||||
protected basePath = 'http://localhost:8080/api';
|
protected basePath = 'http://127.0.0.1:8080/api';
|
||||||
public defaultHeaders = new HttpHeaders();
|
public defaultHeaders = new HttpHeaders();
|
||||||
public configuration = new Configuration();
|
public configuration = new Configuration();
|
||||||
public encoder: HttpParameterCodec;
|
public encoder: HttpParameterCodec;
|
||||||
|
@ -34,7 +34,7 @@ import { Configuration } from '../configurat
|
|||||||
})
|
})
|
||||||
export class PropertiesService {
|
export class PropertiesService {
|
||||||
|
|
||||||
protected basePath = 'http://localhost:8080/api';
|
protected basePath = 'http://127.0.0.1:8080/api';
|
||||||
public defaultHeaders = new HttpHeaders();
|
public defaultHeaders = new HttpHeaders();
|
||||||
public configuration = new Configuration();
|
public configuration = new Configuration();
|
||||||
public encoder: HttpParameterCodec;
|
public encoder: HttpParameterCodec;
|
||||||
|
@ -36,7 +36,7 @@ import { Configuration } from '../configurat
|
|||||||
})
|
})
|
||||||
export class ScheduleService {
|
export class ScheduleService {
|
||||||
|
|
||||||
protected basePath = 'http://localhost:8080/api';
|
protected basePath = 'http://127.0.0.1:8080/api';
|
||||||
public defaultHeaders = new HttpHeaders();
|
public defaultHeaders = new HttpHeaders();
|
||||||
public configuration = new Configuration();
|
public configuration = new Configuration();
|
||||||
public encoder: HttpParameterCodec;
|
public encoder: HttpParameterCodec;
|
||||||
|
@ -35,7 +35,7 @@ import { Configuration } from '../configurat
|
|||||||
})
|
})
|
||||||
export class TaskService {
|
export class TaskService {
|
||||||
|
|
||||||
protected basePath = 'http://localhost:8080/api';
|
protected basePath = 'http://127.0.0.1:8080/api';
|
||||||
public defaultHeaders = new HttpHeaders();
|
public defaultHeaders = new HttpHeaders();
|
||||||
public configuration = new Configuration();
|
public configuration = new Configuration();
|
||||||
public encoder: HttpParameterCodec;
|
public encoder: HttpParameterCodec;
|
||||||
|
@ -36,7 +36,7 @@ import { Configuration } from '../configurat
|
|||||||
})
|
})
|
||||||
export class TaskgroupService {
|
export class TaskgroupService {
|
||||||
|
|
||||||
protected basePath = 'http://localhost:8080/api';
|
protected basePath = 'http://127.0.0.1:8080/api';
|
||||||
public defaultHeaders = new HttpHeaders();
|
public defaultHeaders = new HttpHeaders();
|
||||||
public configuration = new Configuration();
|
public configuration = new Configuration();
|
||||||
public encoder: HttpParameterCodec;
|
public encoder: HttpParameterCodec;
|
||||||
|
@ -34,7 +34,7 @@ import { Configuration } from '../configurat
|
|||||||
})
|
})
|
||||||
export class UsersService {
|
export class UsersService {
|
||||||
|
|
||||||
protected basePath = 'http://localhost:8080/api';
|
protected basePath = 'http://127.0.0.1:8080/api';
|
||||||
public defaultHeaders = new HttpHeaders();
|
public defaultHeaders = new HttpHeaders();
|
||||||
public configuration = new Configuration();
|
public configuration = new Configuration();
|
||||||
public encoder: HttpParameterCodec;
|
public encoder: HttpParameterCodec;
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
* https://openapi-generator.tech
|
* https://openapi-generator.tech
|
||||||
* Do not edit the class manually.
|
* Do not edit the class manually.
|
||||||
*/
|
*/
|
||||||
|
import { TaskgroupEntityInfo } from './taskgroupEntityInfo';
|
||||||
|
|
||||||
|
|
||||||
export interface TaskOverviewInfo {
|
export interface TaskOverviewInfo {
|
||||||
|
@ -11,6 +11,7 @@ import {MissedSchedulesComponent} from "./missed-schedules/missed-schedules.comp
|
|||||||
import {OverdueTaskOverviewComponent} from "./overdue-task-overview/overdue-task-overview.component";
|
import {OverdueTaskOverviewComponent} from "./overdue-task-overview/overdue-task-overview.component";
|
||||||
import {UpcomingTaskOverviewComponent} from "./upcoming-task-overview/upcoming-task-overview.component";
|
import {UpcomingTaskOverviewComponent} from "./upcoming-task-overview/upcoming-task-overview.component";
|
||||||
import {ActiveTaskOverviewComponent} from "./active-task-overview/active-task-overview.component";
|
import {ActiveTaskOverviewComponent} from "./active-task-overview/active-task-overview.component";
|
||||||
|
import {DraggableSchedulerComponent} from "./schedules/draggable-scheduler/draggable-scheduler.component";
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{path: '', component: MainComponent},
|
{path: '', component: MainComponent},
|
||||||
@ -24,7 +25,8 @@ const routes: Routes = [
|
|||||||
{path: 'reschedule', component: MissedSchedulesComponent},
|
{path: 'reschedule', component: MissedSchedulesComponent},
|
||||||
{path: 'overdue', component: OverdueTaskOverviewComponent},
|
{path: 'overdue', component: OverdueTaskOverviewComponent},
|
||||||
{path: 'upcoming', component: UpcomingTaskOverviewComponent},
|
{path: 'upcoming', component: UpcomingTaskOverviewComponent},
|
||||||
{path: 'active', component: ActiveTaskOverviewComponent}
|
{path: 'active', component: ActiveTaskOverviewComponent},
|
||||||
|
{path: 'scheduler', component: DraggableSchedulerComponent}
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
<button mat-button aria-label="Organize" *ngIf="authService.hasKey" [matMenuTriggerFor]="organizeMenu">Organize ▾</button>
|
<button mat-button aria-label="Organize" *ngIf="authService.hasKey" [matMenuTriggerFor]="organizeMenu">Organize ▾</button>
|
||||||
<mat-menu #organizeMenu=matMenu>
|
<mat-menu #organizeMenu=matMenu>
|
||||||
<button mat-menu-item routerLink="taskgroups/" aria-label="Task groups">Taskgroups</button>
|
<button mat-menu-item routerLink="taskgroups/" aria-label="Task groups">Taskgroups</button>
|
||||||
|
<button mat-menu-item routerLink="scheduler/" aria-label="Task groups">Scheduler</button>
|
||||||
<button mat-menu-item routerLink="active/" aria-label="Missed Schedules">Active Tasks</button>
|
<button mat-menu-item routerLink="active/" aria-label="Missed Schedules">Active Tasks</button>
|
||||||
<button mat-menu-item routerLink="upcoming/" aria-label="Upcoming Tasks">Upcoming Tasks</button>
|
<button mat-menu-item routerLink="upcoming/" aria-label="Upcoming Tasks">Upcoming Tasks</button>
|
||||||
<button mat-menu-item routerLink="overdue/" aria-label="Overdue Tasks">Overdue Tasks</button>
|
<button mat-menu-item routerLink="overdue/" aria-label="Overdue Tasks">Overdue Tasks</button>
|
||||||
|
@ -6,7 +6,7 @@ import { AppComponent } from './app.component';
|
|||||||
import {LoginComponent} from "./auth/login/login.component";
|
import {LoginComponent} from "./auth/login/login.component";
|
||||||
import {FormsModule, ReactiveFormsModule} from "@angular/forms";
|
import {FormsModule, ReactiveFormsModule} from "@angular/forms";
|
||||||
import {HttpClient, HttpClientModule, HttpHandler} from "@angular/common/http";
|
import {HttpClient, HttpClientModule, HttpHandler} from "@angular/common/http";
|
||||||
import {ApiModule, Configuration} from "../api";
|
import {ApiModule, Configuration, ConfigurationParameters} from "../api";
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import {MatToolbarModule} from "@angular/material/toolbar";
|
import {MatToolbarModule} from "@angular/material/toolbar";
|
||||||
import {MatIconModule} from "@angular/material/icon";
|
import {MatIconModule} from "@angular/material/icon";
|
||||||
@ -79,6 +79,7 @@ import {NgxMaterialTimepickerModule} from "ngx-material-timepicker";
|
|||||||
import { DateTimePickerComponent } from './date-time-picker/date-time-picker.component';
|
import { DateTimePickerComponent } from './date-time-picker/date-time-picker.component';
|
||||||
import {MatSliderModule} from "@angular/material/slider";
|
import {MatSliderModule} from "@angular/material/slider";
|
||||||
import {MatLegacySliderModule} from "@angular/material/legacy-slider";
|
import {MatLegacySliderModule} from "@angular/material/legacy-slider";
|
||||||
|
import { DraggableSchedulerComponent } from './schedules/draggable-scheduler/draggable-scheduler.component';
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
AppComponent,
|
AppComponent,
|
||||||
@ -117,7 +118,8 @@ import {MatLegacySliderModule} from "@angular/material/legacy-slider";
|
|||||||
UpcomingTaskOverviewComponent,
|
UpcomingTaskOverviewComponent,
|
||||||
ActiveTaskOverviewComponent,
|
ActiveTaskOverviewComponent,
|
||||||
AdvancedSchedulerComponent,
|
AdvancedSchedulerComponent,
|
||||||
DateTimePickerComponent
|
DateTimePickerComponent,
|
||||||
|
DraggableSchedulerComponent,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
@ -178,7 +180,7 @@ import {MatLegacySliderModule} from "@angular/material/legacy-slider";
|
|||||||
// context
|
// context
|
||||||
"API_TOKEN": authService.getAccessToken.bind(authService)
|
"API_TOKEN": authService.getAccessToken.bind(authService)
|
||||||
},
|
},
|
||||||
//basePath: environment.api
|
basePath: environment.api,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
deps: [AuthService],
|
deps: [AuthService],
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
<mat-card-content>
|
<mat-card-content>
|
||||||
<h3>
|
<h3>
|
||||||
<span *ngFor="let taskgroup of task.taskgroups">
|
<span *ngFor="let taskgroup of task.taskgroups">
|
||||||
<a class="undecorated-link">{{taskgroup.taskgroupName}}</a>/
|
<a class="undecorated-link" [routerLink]="['/taskgroups', taskgroup.taskgroupID]">{{taskgroup.taskgroupName}}</a>/
|
||||||
</span>
|
</span>
|
||||||
<a class="undecorated-link">{{task.taskName}}</a>
|
<a class="undecorated-link" [routerLink]="['/taskgroups', task.taskgroups[task.taskgroups.length-1].taskgroupID, 'tasks', task.taskID]">{{task.taskName}}</a>
|
||||||
</h3>
|
</h3>
|
||||||
<mat-progress-bar mode="determinate" [value]="calcProgress(task)"></mat-progress-bar>
|
<mat-progress-bar mode="determinate" [value]="calcProgress(task)"></mat-progress-bar>
|
||||||
<div class="originally-planned-container">
|
<div class="originally-planned-container">
|
||||||
|
@ -132,6 +132,8 @@ export class AdvancedSchedulerComponent implements OnInit, OnChanges{
|
|||||||
|
|
||||||
schedule() {
|
schedule() {
|
||||||
if(this.scheduleInfo == undefined) {
|
if(this.scheduleInfo == undefined) {
|
||||||
|
console.log("Direct Date" + this.selectedStartTime!.toString())
|
||||||
|
console.log("Moment Format" + moment(this.selectedStartTime!).format('YYYY-MM-DDTHH:mm:ss.SSSZ'))
|
||||||
this.scheduleService.schedulesTaskIDAdvancedPut(this.task!.taskID, {
|
this.scheduleService.schedulesTaskIDAdvancedPut(this.task!.taskID, {
|
||||||
scheduleStartTime: moment(this.selectedStartTime!).format('YYYY-MM-DDTHH:mm:ss.SSSZ'),
|
scheduleStartTime: moment(this.selectedStartTime!).format('YYYY-MM-DDTHH:mm:ss.SSSZ'),
|
||||||
scheduleStopTime: moment(this.selectedStopTime!).format('YYYY-MM-DDTHH:mm:ss.SSSZ')
|
scheduleStopTime: moment(this.selectedStopTime!).format('YYYY-MM-DDTHH:mm:ss.SSSZ')
|
||||||
@ -141,6 +143,8 @@ export class AdvancedSchedulerComponent implements OnInit, OnChanges{
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
console.log("Direct Date" + this.selectedStartTime!.toString())
|
||||||
|
console.log("Moment Format" + moment(this.selectedStartTime!).format('YYYY-MM-DDTHH:mm:ss.SSSZ'))
|
||||||
this.scheduleService.schedulesScheduleIDAdvancedPost(this.scheduleInfo.scheduleID!, {
|
this.scheduleService.schedulesScheduleIDAdvancedPost(this.scheduleInfo.scheduleID!, {
|
||||||
scheduleStartTime: moment(this.selectedStartTime!).format('YYYY-MM-DDTHH:mm:ss.SSSZ'),
|
scheduleStartTime: moment(this.selectedStartTime!).format('YYYY-MM-DDTHH:mm:ss.SSSZ'),
|
||||||
scheduleStopTime: moment(this.selectedStopTime!).format('YYYY-MM-DDTHH:mm:ss.SSSZ')
|
scheduleStopTime: moment(this.selectedStopTime!).format('YYYY-MM-DDTHH:mm:ss.SSSZ')
|
||||||
|
@ -0,0 +1,60 @@
|
|||||||
|
.container {
|
||||||
|
margin: 20px auto;
|
||||||
|
width: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spacer {
|
||||||
|
margin-bottom: 2.5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@media screen and (max-width: 600px) {
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
margin: 20px 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.schedule-header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.long-form {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.taskgroup-overview {
|
||||||
|
width: 90%;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
app-task-overview {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-list-base {
|
||||||
|
--mdc-list-list-item-label-text-color: black
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-list-base .taskgroup-btn, ::ng-deep .mat-mdc-list-base .taskgroup-last-btn {
|
||||||
|
--mdc-list-list-item-label-text-color: black
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-card {
|
||||||
|
background-color: #f3f3f3;
|
||||||
|
border: 0;
|
||||||
|
line-height: 4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.lightBlueBtn {
|
||||||
|
background-color: #3498db;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .cal-event-title {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<app-navigation-link-list #navLinkList [navigationLinks]="defaultNavigationLinkPath"></app-navigation-link-list>
|
||||||
|
|
||||||
|
<div class="schedule-header">
|
||||||
|
<h1>Monday, {{ viewDate | calendarDate:(view + 'ViewTitle'):'en':1 }}</h1>
|
||||||
|
<mat-form-field style="float:right;">
|
||||||
|
<mat-label>Schedule Strategy</mat-label>
|
||||||
|
<mat-select [(ngModel)]="scheduleStrategy">
|
||||||
|
<mat-option [value]="1">Basic</mat-option>
|
||||||
|
<!--<mat-option [value]="2">Moderate</mat-option>-->
|
||||||
|
<mat-option [value]="3">Advanced</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div style="display: flex; justify-content: space-between;">
|
||||||
|
<div style="width: 75%">
|
||||||
|
<mwl-calendar-week-view [viewDate]="viewDate" [daysInWeek]="daysInWeek" [dayStartHour]="7" [dayEndHour]="21" [refresh]="refresh"
|
||||||
|
[snapDraggedEvents]="false"
|
||||||
|
(eventTimesChanged)="eventDropped($event)" [events]="events" (eventClicked)="eventClicked('Click', $event.event)"
|
||||||
|
>
|
||||||
|
</mwl-calendar-week-view>
|
||||||
|
</div>
|
||||||
|
<div style="width: 23%;">
|
||||||
|
<div *ngIf="tasks.length > 0">
|
||||||
|
<mat-action-list style="padding: 0;">
|
||||||
|
<button mat-list-item class="lightBlueBtn" [routerLink]="['/scheduler']">Tasks</button>
|
||||||
|
</mat-action-list>
|
||||||
|
<div style="margin-bottom: 20px">
|
||||||
|
<div mwlDroppable
|
||||||
|
(drop)="externalDrop($event.dropData.event)"
|
||||||
|
dragOverClass="drag-over">
|
||||||
|
<div *ngFor="let event of tasks" mwlDraggable
|
||||||
|
[dropData]="{event: event}"
|
||||||
|
[touchStartLongPress]="{ delay: 300, delta: 30 }"
|
||||||
|
dragActiveClass="drag-active" class="task-card">
|
||||||
|
<div>
|
||||||
|
<a href="javascript:;" [style.color]="event.color" style="text-decoration: none; color: black; margin-left: 20px">
|
||||||
|
{{ event.title }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<app-taskgroup-overview (taskgroupSelected)="onSelectTaskgroup($event)"></app-taskgroup-overview>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
@ -0,0 +1,21 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { DraggableSchedulerComponent } from './draggable-scheduler.component';
|
||||||
|
|
||||||
|
describe('DraggableSchedulerComponent', () => {
|
||||||
|
let component: DraggableSchedulerComponent;
|
||||||
|
let fixture: ComponentFixture<DraggableSchedulerComponent>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [DraggableSchedulerComponent]
|
||||||
|
});
|
||||||
|
fixture = TestBed.createComponent(DraggableSchedulerComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,285 @@
|
|||||||
|
import {Component, ViewChild} from '@angular/core';
|
||||||
|
import {NavigationLink, NavigationLinkListComponent} from "../../navigation-link-list/navigation-link-list.component";
|
||||||
|
import {
|
||||||
|
AdvancedScheduleInfo,
|
||||||
|
BasicScheduleInfo,
|
||||||
|
ScheduleInfo,
|
||||||
|
ScheduleService,
|
||||||
|
TaskEntityInfo,
|
||||||
|
TaskgroupEntityInfo,
|
||||||
|
TaskOverviewInfo, TaskShortInfo
|
||||||
|
} from "../../../api";
|
||||||
|
import {Subject} from "rxjs";
|
||||||
|
import {CalendarEvent, CalendarEventAction, CalendarEventTimesChangedEvent, CalendarView} from "angular-calendar";
|
||||||
|
import {TaskOverviewData} from "../../dashboard/taskgroup-overview/taskgroup-overview.component";
|
||||||
|
import {EventColor} from "calendar-utils";
|
||||||
|
import * as moment from "moment";
|
||||||
|
import {Router} from "@angular/router";
|
||||||
|
|
||||||
|
const colors: Record<string, EventColor> = {
|
||||||
|
red: {
|
||||||
|
primary: '#ad2121',
|
||||||
|
secondary: '#FAE3E3',
|
||||||
|
},
|
||||||
|
blue: {
|
||||||
|
primary: '#1e90ff',
|
||||||
|
secondary: '#D1E8FF',
|
||||||
|
},
|
||||||
|
yellow: {
|
||||||
|
primary: '#e3bc08',
|
||||||
|
secondary: '#FDF1BA',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
@Component({
|
||||||
|
selector: 'app-draggable-scheduler',
|
||||||
|
templateUrl: './draggable-scheduler.component.html',
|
||||||
|
styleUrls: ['./draggable-scheduler.component.css'],
|
||||||
|
styles: [
|
||||||
|
`
|
||||||
|
h3 {
|
||||||
|
margin: 0 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class DraggableSchedulerComponent {
|
||||||
|
defaultNavigationLinkPath: NavigationLink[] = [
|
||||||
|
{
|
||||||
|
linkText: "Dashboard",
|
||||||
|
routerLink: ['/']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
linkText: "Taskgroups",
|
||||||
|
routerLink: ["/taskgroups"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
taskgroups: TaskgroupEntityInfo[] = []
|
||||||
|
taskgroup: TaskgroupEntityInfo | undefined
|
||||||
|
taskgroupPath: TaskgroupEntityInfo[] = []
|
||||||
|
taskgroupID: number = -1;
|
||||||
|
scheduleID: number = -1;
|
||||||
|
editedSchedule: ScheduleInfo | undefined
|
||||||
|
@ViewChild('navLinkList') navLinkListComponent: NavigationLinkListComponent | undefined
|
||||||
|
|
||||||
|
task: TaskEntityInfo | undefined
|
||||||
|
scheduleStrategy: number = 1
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************/
|
||||||
|
//Calendar-Stuff
|
||||||
|
/**************************************************/
|
||||||
|
view: CalendarView = CalendarView.Week;
|
||||||
|
viewDate = new Date();
|
||||||
|
daysInWeek = 7;
|
||||||
|
refresh: Subject<void> = new Subject<void>()
|
||||||
|
events: CalendarEvent[] = []
|
||||||
|
|
||||||
|
actions: CalendarEventAction[] = [
|
||||||
|
{
|
||||||
|
label: '<i class="fas fa-fw fa-pencil-alt"></i>',
|
||||||
|
a11yLabel: 'Edit',
|
||||||
|
onClick: ({ event }: { event: CalendarEvent }): void => {
|
||||||
|
this.eventClicked('Edit', event)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '<i class="fas fa-fw fa-trash-alt"></i>',
|
||||||
|
a11yLabel: 'Delete',
|
||||||
|
onClick: ({ event }: { event: CalendarEvent }): void => {
|
||||||
|
this.eventClicked('Delete', event);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
tasks: CalendarEvent[] = []
|
||||||
|
selectedTaskgroupID: number | undefined
|
||||||
|
|
||||||
|
constructor(private scheduleService: ScheduleService,
|
||||||
|
private router: Router) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.scheduleService.schedulesGet().subscribe({
|
||||||
|
next: resp => {
|
||||||
|
resp.forEach(schedule => {
|
||||||
|
if(schedule.scheduleType == 'BASIC') {
|
||||||
|
const basicSchedule = schedule as BasicScheduleInfo
|
||||||
|
this.events.push({
|
||||||
|
title: this.computeTaskPath(schedule.taskgroupPath, schedule.task),
|
||||||
|
color: colors['red'],
|
||||||
|
start: new Date(basicSchedule.scheduleDate),
|
||||||
|
actions: this.actions,
|
||||||
|
allDay: true,
|
||||||
|
draggable: true,
|
||||||
|
resizable: {
|
||||||
|
beforeStart: true,
|
||||||
|
afterEnd: true
|
||||||
|
},
|
||||||
|
meta: {
|
||||||
|
taskID: schedule.task.taskID,
|
||||||
|
scheduleID: schedule.scheduleID,
|
||||||
|
taskgroupID: schedule.taskgroupPath[0].taskgroupID
|
||||||
|
},
|
||||||
|
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
const advancedSchedule = schedule as AdvancedScheduleInfo
|
||||||
|
this.events.push({
|
||||||
|
title: this.computeTaskPath(schedule.taskgroupPath, schedule.task),
|
||||||
|
color: colors['red'],
|
||||||
|
cssClass: 'test',
|
||||||
|
start: new Date(advancedSchedule.scheduleStartTime),
|
||||||
|
end: new Date(advancedSchedule.scheduleStopTime),
|
||||||
|
actions: this.actions,
|
||||||
|
draggable: true,
|
||||||
|
resizable: {
|
||||||
|
beforeStart: true,
|
||||||
|
afterEnd: true
|
||||||
|
},
|
||||||
|
meta: {
|
||||||
|
taskID: schedule.task.taskID,
|
||||||
|
scheduleID: schedule.scheduleID,
|
||||||
|
taskgroupID: schedule.taskgroupPath[0].taskgroupID
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
console.log(this.events)
|
||||||
|
this.refresh.next();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onSelectTaskgroup(taskOverviewData: TaskOverviewData) {
|
||||||
|
this.tasks = [];
|
||||||
|
taskOverviewData.tasks.forEach(task => {
|
||||||
|
this.tasks.push({
|
||||||
|
title: task.taskName,
|
||||||
|
color: colors['yellow'],
|
||||||
|
start: new Date(),
|
||||||
|
draggable: true,
|
||||||
|
cssClass: 'test',
|
||||||
|
resizable: {
|
||||||
|
beforeStart: true,
|
||||||
|
afterEnd: true
|
||||||
|
},
|
||||||
|
meta: {
|
||||||
|
taskID: task.taskID,
|
||||||
|
scheduleID: undefined,
|
||||||
|
taskgroupID: taskOverviewData.taskgroupID
|
||||||
|
},
|
||||||
|
actions: this.actions
|
||||||
|
})
|
||||||
|
})
|
||||||
|
this.selectedTaskgroupID = taskOverviewData.taskgroupID;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
externalDrop(event: CalendarEvent) {
|
||||||
|
if (this.tasks.indexOf(event) === -1) {
|
||||||
|
this.events = this.events.filter((iEvent) => iEvent !== event);
|
||||||
|
this.tasks.push(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eventDropped({
|
||||||
|
event,
|
||||||
|
newStart,
|
||||||
|
newEnd,
|
||||||
|
allDay,
|
||||||
|
}: CalendarEventTimesChangedEvent): void {
|
||||||
|
const externalIndex = this.tasks.indexOf(event);
|
||||||
|
if (typeof allDay !== 'undefined') {
|
||||||
|
event.allDay = allDay;
|
||||||
|
}
|
||||||
|
if (externalIndex > -1) {
|
||||||
|
this.tasks.splice(externalIndex, 1);
|
||||||
|
this.events.push(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
event.start = newStart;
|
||||||
|
if (newEnd) {
|
||||||
|
event.end = newEnd;
|
||||||
|
}
|
||||||
|
this.events = [...this.events];
|
||||||
|
|
||||||
|
if(externalIndex > -1) {
|
||||||
|
//Create new schedule as a new event was dropped
|
||||||
|
if(event.allDay) {
|
||||||
|
this.scheduleService.schedulesTaskIDBasicPut(event.meta.taskID,{
|
||||||
|
scheduleDate: moment(event.start).format('YYYY-MM-DDTHH:mm:ss.SSSZ')
|
||||||
|
}).subscribe({
|
||||||
|
next: resp => {
|
||||||
|
event.meta.scheduleID = resp.scheduleID;
|
||||||
|
event.color = colors['red'];
|
||||||
|
event.title = this.computeTaskPath(resp.taskgroupPath, resp.task)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
console.log("Start: " + event.start);
|
||||||
|
console.log("End: " + event.end);
|
||||||
|
if(event.end == undefined) {
|
||||||
|
event.end = moment(event.start).add(30, 'm').toDate()
|
||||||
|
}
|
||||||
|
this.scheduleService.schedulesTaskIDAdvancedPut(event.meta.taskID, {
|
||||||
|
scheduleStartTime: moment(event.start).format('YYYY-MM-DDTHH:mm:ss.SSSZ'),
|
||||||
|
scheduleStopTime: moment(event.end).format('YYYY-MM-DDTHH:mm:ss.SSSZ'),
|
||||||
|
}).subscribe({
|
||||||
|
next: resp => {
|
||||||
|
event.meta.scheduleID = resp.scheduleID;
|
||||||
|
event.color = colors['red'];
|
||||||
|
event.title = this.computeTaskPath(resp.taskgroupPath, resp.task)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(event.allDay) {
|
||||||
|
this.scheduleService.schedulesScheduleIDBasicPost(event.meta.scheduleID,{
|
||||||
|
scheduleDate: moment(event.start).format('YYYY-MM-DDTHH:mm:ss.SSSZ')
|
||||||
|
}).subscribe({
|
||||||
|
next: resp => {
|
||||||
|
console.log("Updated")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.scheduleService.schedulesScheduleIDAdvancedPost(event.meta.scheduleID, {
|
||||||
|
scheduleStartTime: moment(event.start).format('YYYY-MM-DDTHH:mm:ss.SSSZ'),
|
||||||
|
scheduleStopTime: moment(event.end).format('YYYY-MM-DDTHH:mm:ss.SSSZ'),
|
||||||
|
}).subscribe({
|
||||||
|
next: resp => {
|
||||||
|
console.log("Updated")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
computeTaskPath(taskgroupPath: TaskgroupEntityInfo[], task: TaskShortInfo | TaskEntityInfo) {
|
||||||
|
let result = "";
|
||||||
|
taskgroupPath.forEach(taskgroupPathPart => {
|
||||||
|
result += taskgroupPathPart.taskgroupName + "/"
|
||||||
|
});
|
||||||
|
result += task!.taskName
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
eventClicked(action: string, event: CalendarEvent): void {
|
||||||
|
if(action == 'Click') {
|
||||||
|
this.router.navigateByUrl("/taskgroups/" + event.meta.taskgroupID + "/tasks/" + event.meta.taskID);
|
||||||
|
} else if(action == 'Edit') {
|
||||||
|
this.router.navigateByUrl("/taskgroups/" + event.meta.taskgroupID + "/tasks/" + event.meta.taskID + "/schedule/" + event.meta.scheduleID);
|
||||||
|
} else if(action == 'Delete') {
|
||||||
|
this.scheduleService.schedulesScheduleIDDelete(event.meta.scheduleID).subscribe({
|
||||||
|
next: resp => {
|
||||||
|
this.events = this.events.filter(calendarEvent => calendarEvent.meta.scheduleID !== event.meta.scheduleID);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -26,3 +26,6 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::ng-deep .cal-event-title {
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
export const environment = {
|
export const environment = {
|
||||||
production: true
|
production: true,
|
||||||
|
api: "/api"
|
||||||
};
|
};
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
export const environment = {
|
export const environment = {
|
||||||
production: false,
|
production: false,
|
||||||
api: "api"
|
api: "http://localhost:8080/api"
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
|
||||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
|
||||||
|
<link rel="stylesheet" href="https://pro.fontawesome.com/releases/v5.10.0/css/all.css" integrity="sha384-AYmEC3Yw5cVb3ZcuHtOA93w35dYTsvhLPVnYs9eStHfGJvOvKxVfELGroGkvsg+p" crossorigin="anonymous"/>
|
||||||
</head>
|
</head>
|
||||||
<body class="mat-typography">
|
<body class="mat-typography">
|
||||||
<app-root></app-root>
|
<app-root></app-root>
|
||||||
|
@ -3,7 +3,7 @@ info:
|
|||||||
title: API Title
|
title: API Title
|
||||||
version: '1.0'
|
version: '1.0'
|
||||||
servers:
|
servers:
|
||||||
- url: http://localhost:8080/api
|
- url: http://127.0.0.1:8080/api
|
||||||
paths:
|
paths:
|
||||||
/auth/signin:
|
/auth/signin:
|
||||||
post:
|
post:
|
||||||
@ -2421,6 +2421,7 @@ components:
|
|||||||
- eta
|
- eta
|
||||||
- limit
|
- limit
|
||||||
- overdue
|
- overdue
|
||||||
|
- taskgroupPath
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
properties:
|
properties:
|
||||||
taskID:
|
taskID:
|
||||||
@ -2448,6 +2449,10 @@ components:
|
|||||||
type: number
|
type: number
|
||||||
description: number in minutes that was already worked on this task
|
description: number in minutes that was already worked on this task
|
||||||
example: 10
|
example: 10
|
||||||
|
taskgroupPath:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/TaskgroupEntityInfo'
|
||||||
ScheduleStatus:
|
ScheduleStatus:
|
||||||
required:
|
required:
|
||||||
- activeMinutes
|
- activeMinutes
|
||||||
|
Loading…
Reference in New Issue
Block a user