Merge pull request 'issue-35' (#37) from issue-35 into master
Reviewed-on: Sebastian/TimeManager#37
This commit is contained in:
commit
2cabc09598
@ -4,8 +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="Forget single schedule">
|
<list default="true" id="3a869f59-290a-4ab2-b036-a878ce801bc4" name="Changes" comment="Fix marking finished task as overdue">
|
||||||
<change beforePath="$PROJECT_DIR$/src/main/java/core/api/controller/ScheduleController.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/core/api/controller/ScheduleController.java" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/../frontend/src/app/overdue-task-overview/overdue-task-overview.component.ts" beforeDir="false" afterPath="$PROJECT_DIR$/../frontend/src/app/overdue-task-overview/overdue-task-overview.component.ts" 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" />
|
||||||
@ -41,33 +41,34 @@
|
|||||||
<option name="hideEmptyMiddlePackages" value="true" />
|
<option name="hideEmptyMiddlePackages" value="true" />
|
||||||
<option name="showLibraryContents" value="true" />
|
<option name="showLibraryContents" value="true" />
|
||||||
</component>
|
</component>
|
||||||
<component name="PropertiesComponent">{
|
<component name="PropertiesComponent"><![CDATA[{
|
||||||
"keyToString": {
|
"keyToString": {
|
||||||
"RequestMappingsPanelOrder0": "0",
|
"RequestMappingsPanelOrder0": "0",
|
||||||
"RequestMappingsPanelOrder1": "1",
|
"RequestMappingsPanelOrder1": "1",
|
||||||
"RequestMappingsPanelWidth0": "75",
|
"RequestMappingsPanelWidth0": "75",
|
||||||
"RequestMappingsPanelWidth1": "75",
|
"RequestMappingsPanelWidth1": "75",
|
||||||
"RunOnceActivity.OpenProjectViewOnStart": "true",
|
"RunOnceActivity.OpenProjectViewOnStart": "true",
|
||||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||||
"WebServerToolWindowFactoryState": "false",
|
"WebServerToolWindowFactoryState": "false",
|
||||||
"git-widget-placeholder": "issue-11-angular-update",
|
"extract.method.default.visibility": "private",
|
||||||
"last_directory_selection": "D:/Programmierprojekte/TimeManager/backend/src/main/java/core/api/models/timemanager",
|
"git-widget-placeholder": "issue-11-angular-update",
|
||||||
"last_opened_file_path": "D:/Programmierprojekte/Dicewars/client",
|
"last_directory_selection": "D:/Programmierprojekte/TimeManager/backend/src/main/java/core/api/models/timemanager",
|
||||||
"node.js.detected.package.eslint": "true",
|
"last_opened_file_path": "D:/Programmierprojekte/Dicewars/client",
|
||||||
"node.js.detected.package.tslint": "true",
|
"node.js.detected.package.eslint": "true",
|
||||||
"node.js.selected.package.eslint": "(autodetect)",
|
"node.js.detected.package.tslint": "true",
|
||||||
"node.js.selected.package.tslint": "(autodetect)",
|
"node.js.selected.package.eslint": "(autodetect)",
|
||||||
"nodejs_package_manager_path": "npm",
|
"node.js.selected.package.tslint": "(autodetect)",
|
||||||
"settings.editor.selected.configurable": "swagger",
|
"nodejs_package_manager_path": "npm",
|
||||||
"ts.external.directory.path": "/snap/intellij-idea-ultimate/459/plugins/javascript-impl/jsLanguageServicesImpl/external",
|
"settings.editor.selected.configurable": "swagger",
|
||||||
"vue.rearranger.settings.migration": "true"
|
"ts.external.directory.path": "/snap/intellij-idea-ultimate/459/plugins/javascript-impl/jsLanguageServicesImpl/external",
|
||||||
|
"vue.rearranger.settings.migration": "true"
|
||||||
},
|
},
|
||||||
"keyToStringList": {
|
"keyToStringList": {
|
||||||
"DatabaseDriversLRU": [
|
"DatabaseDriversLRU": [
|
||||||
"mariadb"
|
"mariadb"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}</component>
|
}]]></component>
|
||||||
<component name="RunManager">
|
<component name="RunManager">
|
||||||
<configuration name="DemoApplication" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot" nameIsGenerated="true">
|
<configuration name="DemoApplication" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot" nameIsGenerated="true">
|
||||||
<module name="demo" />
|
<module name="demo" />
|
||||||
@ -258,7 +259,15 @@
|
|||||||
<option name="project" value="LOCAL" />
|
<option name="project" value="LOCAL" />
|
||||||
<updated>1698573369764</updated>
|
<updated>1698573369764</updated>
|
||||||
</task>
|
</task>
|
||||||
<option name="localTasksCounter" value="20" />
|
<task id="LOCAL-00020" summary="Fix marking finished task as overdue">
|
||||||
|
<option name="closed" value="true" />
|
||||||
|
<created>1698592265846</created>
|
||||||
|
<option name="number" value="00020" />
|
||||||
|
<option name="presentableId" value="LOCAL-00020" />
|
||||||
|
<option name="project" value="LOCAL" />
|
||||||
|
<updated>1698592265846</updated>
|
||||||
|
</task>
|
||||||
|
<option name="localTasksCounter" value="21" />
|
||||||
<servers />
|
<servers />
|
||||||
</component>
|
</component>
|
||||||
<component name="TypeScriptGeneratedFilesManager">
|
<component name="TypeScriptGeneratedFilesManager">
|
||||||
@ -285,7 +294,8 @@
|
|||||||
<MESSAGE value="Check if there is another active schedule when starting task now" />
|
<MESSAGE value="Check if there is another active schedule when starting task now" />
|
||||||
<MESSAGE value="List missed Schedules" />
|
<MESSAGE value="List missed Schedules" />
|
||||||
<MESSAGE value="Forget single schedule" />
|
<MESSAGE value="Forget single schedule" />
|
||||||
<option name="LAST_COMMIT_MESSAGE" value="Forget single schedule" />
|
<MESSAGE value="Fix marking finished task as overdue" />
|
||||||
|
<option name="LAST_COMMIT_MESSAGE" value="Fix marking finished task as overdue" />
|
||||||
</component>
|
</component>
|
||||||
<component name="XDebuggerManager">
|
<component name="XDebuggerManager">
|
||||||
<breakpoint-manager>
|
<breakpoint-manager>
|
||||||
|
@ -2,9 +2,7 @@ package core.api.controller;
|
|||||||
|
|
||||||
import core.api.models.auth.SimpleStatusResponse;
|
import core.api.models.auth.SimpleStatusResponse;
|
||||||
import core.api.models.timemanager.taskSchedule.ScheduleInfo;
|
import core.api.models.timemanager.taskSchedule.ScheduleInfo;
|
||||||
import core.api.models.timemanager.tasks.TaskEntityInfo;
|
import core.api.models.timemanager.tasks.*;
|
||||||
import core.api.models.timemanager.tasks.TaskFieldInfo;
|
|
||||||
import core.api.models.timemanager.tasks.TaskShortInfo;
|
|
||||||
import core.entities.timemanager.Task;
|
import core.entities.timemanager.Task;
|
||||||
import core.entities.timemanager.Taskgroup;
|
import core.entities.timemanager.Taskgroup;
|
||||||
import core.services.*;
|
import core.services.*;
|
||||||
@ -31,22 +29,37 @@ public class TaskController {
|
|||||||
this.taskgroupService = taskgroupService;
|
this.taskgroupService = taskgroupService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/tasks/all/{finished}")
|
@GetMapping("/tasks/all/{scope}/{detailed}")
|
||||||
public ResponseEntity<List<TaskShortInfo>> loadAllTasks(@PathVariable boolean finished) {
|
public ResponseEntity<?> loadAllTasksShort(@PathVariable TaskScope scope, @PathVariable boolean detailed) {
|
||||||
List<Task> taskList = taskService.loadAllTasks(SecurityContextHolder.getContext().getAuthentication().getName(), finished);
|
List<Task> taskList = taskService.loadAllTasks(SecurityContextHolder.getContext().getAuthentication().getName(), scope);
|
||||||
List<TaskShortInfo> taskShortInfos = new ArrayList<>();
|
List<TaskShortInfo> taskShortInfos = new ArrayList<>();
|
||||||
|
List<TaskTaskgroupInfo> taskInfos = new ArrayList<>();
|
||||||
for(Task task : taskList) {
|
for(Task task : taskList) {
|
||||||
List<Taskgroup> ancestors = Taskgroup.getAncestorList(task.getTaskgroup());
|
StringBuilder builder = loadTaskgroupPath(task);
|
||||||
ancestors.add(task.getTaskgroup());
|
if(!detailed) {
|
||||||
StringBuilder builder = new StringBuilder();
|
taskShortInfos.add(new TaskShortInfo(task.getTaskID(), builder.toString()));
|
||||||
for(Taskgroup taskgroup : ancestors) {
|
} else {
|
||||||
builder.append(taskgroup.getTaskgroupName()).append("/");
|
taskInfos.add(new TaskTaskgroupInfo(task));
|
||||||
}
|
}
|
||||||
builder.append(task.getTaskName());
|
|
||||||
taskShortInfos.add(new TaskShortInfo(task.getTaskID(), builder.toString()));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
return ResponseEntity.ok(taskShortInfos);
|
|
||||||
|
if(detailed) {
|
||||||
|
return ResponseEntity.ok(taskInfos);
|
||||||
|
} else {
|
||||||
|
return ResponseEntity.ok(taskShortInfos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private StringBuilder loadTaskgroupPath(Task task) {
|
||||||
|
List<Taskgroup> ancestors = Taskgroup.getAncestorList(task.getTaskgroup());
|
||||||
|
ancestors.add(task.getTaskgroup());
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
for(Taskgroup taskgroup : ancestors) {
|
||||||
|
builder.append(taskgroup.getTaskgroupName()).append("/");
|
||||||
|
}
|
||||||
|
builder.append(task.getTaskName());
|
||||||
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/tasks/{taskgroupID}/{status}")
|
@GetMapping("/tasks/{taskgroupID}/{status}")
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
package core.api.models.timemanager.tasks;
|
||||||
|
|
||||||
|
public enum TaskScope {
|
||||||
|
|
||||||
|
FINISHED,
|
||||||
|
UNFINISHED,
|
||||||
|
OVERDUE;
|
||||||
|
}
|
@ -0,0 +1,118 @@
|
|||||||
|
package core.api.models.timemanager.tasks;
|
||||||
|
|
||||||
|
import core.api.models.timemanager.taskgroup.TaskgroupEntityInfo;
|
||||||
|
import core.entities.timemanager.Task;
|
||||||
|
import core.entities.timemanager.Taskgroup;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class TaskTaskgroupInfo {
|
||||||
|
|
||||||
|
private long taskID;
|
||||||
|
private String taskName;
|
||||||
|
|
||||||
|
private int eta;
|
||||||
|
|
||||||
|
private LocalDate startDate;
|
||||||
|
|
||||||
|
private LocalDate deadline;
|
||||||
|
|
||||||
|
private boolean overdue;
|
||||||
|
|
||||||
|
private boolean finished;
|
||||||
|
|
||||||
|
private int workTime;
|
||||||
|
|
||||||
|
private List<TaskgroupEntityInfo> taskgroups;
|
||||||
|
|
||||||
|
public TaskTaskgroupInfo(Task task) {
|
||||||
|
this.taskID = task.getTaskID();
|
||||||
|
this.taskName = task.getTaskName();
|
||||||
|
this.eta = task.getEta();
|
||||||
|
this.startDate = task.getStartDate();
|
||||||
|
this.deadline = task.getDeadline();
|
||||||
|
if(task.getDeadline() != null) {
|
||||||
|
this.overdue = LocalDate.now().isAfter(task.getDeadline());
|
||||||
|
}
|
||||||
|
this.finished = task.isFinished();
|
||||||
|
this.workTime = task.getWorkTime();
|
||||||
|
|
||||||
|
List<Taskgroup> taskgroupList = Taskgroup.getAncestorList(task.getTaskgroup());
|
||||||
|
taskgroupList.add(task.getTaskgroup());
|
||||||
|
this.taskgroups = taskgroupList.stream().map(TaskgroupEntityInfo::new).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTaskID() {
|
||||||
|
return taskID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTaskID(long taskID) {
|
||||||
|
this.taskID = taskID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTaskName() {
|
||||||
|
return taskName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTaskName(String taskName) {
|
||||||
|
this.taskName = taskName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getEta() {
|
||||||
|
return eta;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEta(int eta) {
|
||||||
|
this.eta = eta;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDate getStartDate() {
|
||||||
|
return startDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStartDate(LocalDate startDate) {
|
||||||
|
this.startDate = startDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDate getDeadline() {
|
||||||
|
return deadline;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeadline(LocalDate deadline) {
|
||||||
|
this.deadline = deadline;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOverdue() {
|
||||||
|
return overdue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOverdue(boolean overdue) {
|
||||||
|
this.overdue = overdue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFinished() {
|
||||||
|
return finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFinished(boolean finished) {
|
||||||
|
this.finished = finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getWorkTime() {
|
||||||
|
return workTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWorkTime(int workTime) {
|
||||||
|
this.workTime = workTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<TaskgroupEntityInfo> getTaskgroups() {
|
||||||
|
return taskgroups;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTaskgroups(List<TaskgroupEntityInfo> taskgroups) {
|
||||||
|
this.taskgroups = taskgroups;
|
||||||
|
}
|
||||||
|
}
|
@ -8,6 +8,7 @@ import org.springframework.data.repository.CrudRepository;
|
|||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
import javax.transaction.Transactional;
|
import javax.transaction.Transactional;
|
||||||
|
import java.time.LocalDate;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
@ -25,4 +26,7 @@ public interface TaskRepository extends CrudRepository<Task, Long> {
|
|||||||
@Modifying
|
@Modifying
|
||||||
@Query(value = "DELETE FROM Task t WHERE t.taskID = ?1")
|
@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")
|
||||||
|
List<Task> findAllOverdue(String username, LocalDate now);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package core.services;
|
package core.services;
|
||||||
|
|
||||||
import core.api.models.timemanager.tasks.TaskFieldInfo;
|
import core.api.models.timemanager.tasks.TaskFieldInfo;
|
||||||
|
import core.api.models.timemanager.tasks.TaskScope;
|
||||||
import core.entities.timemanager.BasicTaskSchedule;
|
import core.entities.timemanager.BasicTaskSchedule;
|
||||||
import core.entities.timemanager.Task;
|
import core.entities.timemanager.Task;
|
||||||
import core.entities.timemanager.Taskgroup;
|
import core.entities.timemanager.Taskgroup;
|
||||||
@ -10,6 +11,7 @@ import core.repositories.timemanager.TaskgroupRepository;
|
|||||||
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 java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@ -99,7 +101,18 @@ public class TaskService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Task> loadAllTasks(String username, boolean finished) {
|
public List<Task> loadAllTasks(String username, TaskScope scope) {
|
||||||
return taskRepository.findAllByUser(username, finished);
|
switch (scope) {
|
||||||
|
case UNFINISHED, FINISHED -> {
|
||||||
|
return taskRepository.findAllByUser(username, scope.equals(TaskScope.FINISHED));
|
||||||
|
}
|
||||||
|
case OVERDUE -> {
|
||||||
|
return taskRepository.findAllOverdue(username, LocalDate.now());
|
||||||
|
}
|
||||||
|
default -> {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,7 @@ model/taskFieldInfo.ts
|
|||||||
model/taskOverviewInfo.ts
|
model/taskOverviewInfo.ts
|
||||||
model/taskScheduleStopResponse.ts
|
model/taskScheduleStopResponse.ts
|
||||||
model/taskShortInfo.ts
|
model/taskShortInfo.ts
|
||||||
|
model/taskTaskgroupInfo.ts
|
||||||
model/taskgroupDetailInfo.ts
|
model/taskgroupDetailInfo.ts
|
||||||
model/taskgroupEntityInfo.ts
|
model/taskgroupEntityInfo.ts
|
||||||
model/taskgroupFieldInfo.ts
|
model/taskgroupFieldInfo.ts
|
||||||
|
@ -23,6 +23,7 @@ import { SimpleStatusResponse } from '../model/models';
|
|||||||
import { TaskEntityInfo } from '../model/models';
|
import { TaskEntityInfo } from '../model/models';
|
||||||
import { TaskFieldInfo } from '../model/models';
|
import { TaskFieldInfo } from '../model/models';
|
||||||
import { TaskShortInfo } from '../model/models';
|
import { TaskShortInfo } from '../model/models';
|
||||||
|
import { TaskTaskgroupInfo } from '../model/models';
|
||||||
|
|
||||||
import { BASE_PATH, COLLECTION_FORMATS } from '../variables';
|
import { BASE_PATH, COLLECTION_FORMATS } from '../variables';
|
||||||
import { Configuration } from '../configuration';
|
import { Configuration } from '../configuration';
|
||||||
@ -92,16 +93,20 @@ export class TaskService {
|
|||||||
/**
|
/**
|
||||||
* edits an existing task
|
* edits an existing task
|
||||||
* edits an existing task
|
* edits an existing task
|
||||||
* @param finished fetches either finished or unfinished tasks
|
* @param scope defines scope of fetched tasks
|
||||||
|
* @param detailed determines whether an detailed response is required or not
|
||||||
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
|
* @param 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.
|
* @param reportProgress flag to report request and response progress.
|
||||||
*/
|
*/
|
||||||
public tasksAllFinishedGet(finished: boolean, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<Array<TaskShortInfo>>;
|
public tasksAllScopeDetailedGet(scope: 'FINISHED' | 'UNFINISHED' | 'OVERDUE', detailed: boolean, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<Array<TaskShortInfo> | Array<TaskTaskgroupInfo>>;
|
||||||
public tasksAllFinishedGet(finished: boolean, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<HttpResponse<Array<TaskShortInfo>>>;
|
public tasksAllScopeDetailedGet(scope: 'FINISHED' | 'UNFINISHED' | 'OVERDUE', detailed: boolean, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<HttpResponse<Array<TaskShortInfo> | Array<TaskTaskgroupInfo>>>;
|
||||||
public tasksAllFinishedGet(finished: boolean, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<HttpEvent<Array<TaskShortInfo>>>;
|
public tasksAllScopeDetailedGet(scope: 'FINISHED' | 'UNFINISHED' | 'OVERDUE', detailed: boolean, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<HttpEvent<Array<TaskShortInfo> | Array<TaskTaskgroupInfo>>>;
|
||||||
public tasksAllFinishedGet(finished: boolean, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<any> {
|
public tasksAllScopeDetailedGet(scope: 'FINISHED' | 'UNFINISHED' | 'OVERDUE', detailed: boolean, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<any> {
|
||||||
if (finished === null || finished === undefined) {
|
if (scope === null || scope === undefined) {
|
||||||
throw new Error('Required parameter finished was null or undefined when calling tasksAllFinishedGet.');
|
throw new Error('Required parameter scope was null or undefined when calling tasksAllScopeDetailedGet.');
|
||||||
|
}
|
||||||
|
if (detailed === null || detailed === undefined) {
|
||||||
|
throw new Error('Required parameter detailed was null or undefined when calling tasksAllScopeDetailedGet.');
|
||||||
}
|
}
|
||||||
|
|
||||||
let localVarHeaders = this.defaultHeaders;
|
let localVarHeaders = this.defaultHeaders;
|
||||||
@ -136,7 +141,7 @@ export class TaskService {
|
|||||||
responseType_ = 'text';
|
responseType_ = 'text';
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.httpClient.get<Array<TaskShortInfo>>(`${this.configuration.basePath}/tasks/all/${encodeURIComponent(String(finished))}`,
|
return this.httpClient.get<Array<TaskShortInfo> | Array<TaskTaskgroupInfo>>(`${this.configuration.basePath}/tasks/all/${encodeURIComponent(String(scope))}/${encodeURIComponent(String(detailed))}`,
|
||||||
{
|
{
|
||||||
context: localVarHttpContext,
|
context: localVarHttpContext,
|
||||||
responseType: <any>responseType_,
|
responseType: <any>responseType_,
|
||||||
|
@ -24,6 +24,7 @@ export * from './taskFieldInfo';
|
|||||||
export * from './taskOverviewInfo';
|
export * from './taskOverviewInfo';
|
||||||
export * from './taskScheduleStopResponse';
|
export * from './taskScheduleStopResponse';
|
||||||
export * from './taskShortInfo';
|
export * from './taskShortInfo';
|
||||||
|
export * from './taskTaskgroupInfo';
|
||||||
export * from './taskgroupDetailInfo';
|
export * from './taskgroupDetailInfo';
|
||||||
export * from './taskgroupEntityInfo';
|
export * from './taskgroupEntityInfo';
|
||||||
export * from './taskgroupFieldInfo';
|
export * from './taskgroupFieldInfo';
|
||||||
|
50
frontend/src/api/model/taskTaskgroupInfo.ts
Normal file
50
frontend/src/api/model/taskTaskgroupInfo.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/**
|
||||||
|
* API Title
|
||||||
|
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
|
||||||
|
*
|
||||||
|
* The version of the OpenAPI document: 1.0
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||||
|
* https://openapi-generator.tech
|
||||||
|
* Do not edit the class manually.
|
||||||
|
*/
|
||||||
|
import { TaskgroupEntityInfo } from './taskgroupEntityInfo';
|
||||||
|
|
||||||
|
|
||||||
|
export interface TaskTaskgroupInfo {
|
||||||
|
/**
|
||||||
|
* internal id of task
|
||||||
|
*/
|
||||||
|
taskID: number;
|
||||||
|
/**
|
||||||
|
* name of task
|
||||||
|
*/
|
||||||
|
taskName: string;
|
||||||
|
/**
|
||||||
|
* expected time to finish task
|
||||||
|
*/
|
||||||
|
eta: number;
|
||||||
|
/**
|
||||||
|
* date from which the task can be started
|
||||||
|
*/
|
||||||
|
startDate: string;
|
||||||
|
/**
|
||||||
|
* date until the task has to be finished
|
||||||
|
*/
|
||||||
|
deadline: string;
|
||||||
|
/**
|
||||||
|
* determines whether the task is overdue
|
||||||
|
*/
|
||||||
|
overdue: boolean;
|
||||||
|
/**
|
||||||
|
* determines whether the task is finished
|
||||||
|
*/
|
||||||
|
finished: boolean;
|
||||||
|
/**
|
||||||
|
* number in minutes that was already worked on this task
|
||||||
|
*/
|
||||||
|
workTime: number;
|
||||||
|
taskgroups: Array<TaskgroupEntityInfo>;
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,7 @@ import {TaskgroupDashboardComponent} from "./taskgroups/taskgroup-dashboard/task
|
|||||||
import {TaskDetailOverviewComponent} from "./tasks/task-detail-overview/task-detail-overview.component";
|
import {TaskDetailOverviewComponent} from "./tasks/task-detail-overview/task-detail-overview.component";
|
||||||
import {SchedulerComponent} from "./schedules/scheduler/scheduler.component";
|
import {SchedulerComponent} from "./schedules/scheduler/scheduler.component";
|
||||||
import {MissedSchedulesComponent} from "./missed-schedules/missed-schedules.component";
|
import {MissedSchedulesComponent} from "./missed-schedules/missed-schedules.component";
|
||||||
|
import {OverdueTaskOverviewComponent} from "./overdue-task-overview/overdue-task-overview.component";
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{path: '', component: MainComponent},
|
{path: '', component: MainComponent},
|
||||||
@ -18,7 +19,8 @@ const routes: Routes = [
|
|||||||
{path: 'taskgroups/:taskgroupID/tasks/:taskID', component: TaskDetailOverviewComponent},
|
{path: 'taskgroups/:taskgroupID/tasks/:taskID', component: TaskDetailOverviewComponent},
|
||||||
{path: 'taskgroups/:taskgroupID/tasks/:taskID/schedule', component: SchedulerComponent},
|
{path: 'taskgroups/:taskgroupID/tasks/:taskID/schedule', component: SchedulerComponent},
|
||||||
{path: 'taskgroups/:taskgroupID/tasks/:taskID/schedule/:scheduleID', component: SchedulerComponent},
|
{path: 'taskgroups/:taskgroupID/tasks/:taskID/schedule/:scheduleID', component: SchedulerComponent},
|
||||||
{path: 'reschedule', component: MissedSchedulesComponent}
|
{path: 'reschedule', component: MissedSchedulesComponent},
|
||||||
|
{path: 'overdue', component: OverdueTaskOverviewComponent}
|
||||||
];
|
];
|
||||||
|
|
||||||
@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="overdue/" aria-label="Overdue Tasks">Overdue Tasks</button>
|
||||||
<button mat-menu-item routerLink="reschedule/" aria-label="Missed Schedules">Missed Schedules</button>
|
<button mat-menu-item routerLink="reschedule/" aria-label="Missed Schedules">Missed Schedules</button>
|
||||||
</mat-menu>
|
</mat-menu>
|
||||||
<span class="example-spacer"></span>
|
<span class="example-spacer"></span>
|
||||||
|
@ -71,6 +71,7 @@ import { ForgottenTaskStartDialogComponent } from './dashboard/forgotten-task-st
|
|||||||
import {MatAutocompleteModule} from "@angular/material/autocomplete";
|
import {MatAutocompleteModule} from "@angular/material/autocomplete";
|
||||||
import { MissedSchedulesComponent } from './missed-schedules/missed-schedules.component';
|
import { MissedSchedulesComponent } from './missed-schedules/missed-schedules.component';
|
||||||
import { MissedScheduleForgetConfirmationDialogComponent } from './missed-schedules/missed-schedule-forget-confirmation-dialog/missed-schedule-forget-confirmation-dialog.component';
|
import { MissedScheduleForgetConfirmationDialogComponent } from './missed-schedules/missed-schedule-forget-confirmation-dialog/missed-schedule-forget-confirmation-dialog.component';
|
||||||
|
import { OverdueTaskOverviewComponent } from './overdue-task-overview/overdue-task-overview.component';
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
AppComponent,
|
AppComponent,
|
||||||
@ -104,7 +105,8 @@ import { MissedScheduleForgetConfirmationDialogComponent } from './missed-schedu
|
|||||||
TaskOverviewComponent,
|
TaskOverviewComponent,
|
||||||
ForgottenTaskStartDialogComponent,
|
ForgottenTaskStartDialogComponent,
|
||||||
MissedSchedulesComponent,
|
MissedSchedulesComponent,
|
||||||
MissedScheduleForgetConfirmationDialogComponent
|
MissedScheduleForgetConfirmationDialogComponent,
|
||||||
|
OverdueTaskOverviewComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
|
@ -30,7 +30,7 @@ export class ForgottenTaskStartDialogComponent implements OnInit{
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.taskService.tasksAllFinishedGet(false).subscribe({
|
this.taskService.tasksAllScopeDetailedGet("UNFINISHED", false).subscribe({
|
||||||
next: resp => {
|
next: resp => {
|
||||||
this.tasks = resp;
|
this.tasks = resp;
|
||||||
this.filteredOptions = this.myControl.valueChanges.pipe(
|
this.filteredOptions = this.myControl.valueChanges.pipe(
|
||||||
|
@ -0,0 +1,72 @@
|
|||||||
|
.container {
|
||||||
|
margin: 20px auto;
|
||||||
|
width: 70%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spacer {
|
||||||
|
margin-bottom: 2.5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@media screen and (max-width: 600px) {
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
margin: 20px 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.undecorated-link {
|
||||||
|
text-decoration: none;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.undecorated-link:hover {
|
||||||
|
color: #3498db;
|
||||||
|
}
|
||||||
|
|
||||||
|
.originally-planned-container {
|
||||||
|
border-style: solid;
|
||||||
|
border-color: #e1e1e1;
|
||||||
|
border-width: 1px;
|
||||||
|
margin-top: 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.reschedule-actions-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-group {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.yellowBtn {
|
||||||
|
background-color: #f39c12;
|
||||||
|
color: white;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.greenBtn {
|
||||||
|
background-color: #00bc8c;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-group-item {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-group-itemleft {
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-group-itemright {
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
<div class="container">
|
||||||
|
<app-navigation-link-list [navigationLinks]="defaultNavigationLinkPath"></app-navigation-link-list>
|
||||||
|
<mat-card *ngFor="let task of overdueTasks">
|
||||||
|
<mat-card-content>
|
||||||
|
<h3>
|
||||||
|
<span *ngFor="let taskgroup of task.taskgroups">
|
||||||
|
<a class="undecorated-link">{{taskgroup.taskgroupName}}</a>/
|
||||||
|
</span>
|
||||||
|
<a class="undecorated-link">{{task.taskName}}</a>
|
||||||
|
</h3>
|
||||||
|
<mat-progress-bar mode="determinate" [value]="calcProgress(task)"></mat-progress-bar>
|
||||||
|
<div class="originally-planned-container">
|
||||||
|
<div style="width: 100%">
|
||||||
|
<p style="display: inline-block"><i>Limit:</i> {{task.deadline}}</p>
|
||||||
|
</div>
|
||||||
|
<div style="width: 100%" class="reschedule-actions-container">
|
||||||
|
<div class="btn-group">
|
||||||
|
<button mat-raised-button color="primary" class="btn-group-itemleft">Schedule</button>
|
||||||
|
<button mat-raised-button class="btn-group-item yellowBtn" (click)="startNow(task)">Start now</button>
|
||||||
|
<button mat-raised-button class="btn-group-itemright greenBtn" (click)="finishTask(task)">Finish</button>
|
||||||
|
</div>
|
||||||
|
<div class="btn-group">
|
||||||
|
<button mat-raised-button color="warn" (click)="deleteTask(task)"><mat-icon>delete</mat-icon>Delete</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
|
</div>
|
@ -0,0 +1,21 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { OverdueTaskOverviewComponent } from './overdue-task-overview.component';
|
||||||
|
|
||||||
|
describe('OverdueTaskOverviewComponent', () => {
|
||||||
|
let component: OverdueTaskOverviewComponent;
|
||||||
|
let fixture: ComponentFixture<OverdueTaskOverviewComponent>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [OverdueTaskOverviewComponent]
|
||||||
|
});
|
||||||
|
fixture = TestBed.createComponent(OverdueTaskOverviewComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,98 @@
|
|||||||
|
import {Component, OnInit} from '@angular/core';
|
||||||
|
import {ScheduleService, TaskEntityInfo, TaskService, TaskShortInfo, TaskTaskgroupInfo} from "../../api";
|
||||||
|
import {NavigationLink} from "../navigation-link-list/navigation-link-list.component";
|
||||||
|
import {Router} from "@angular/router";
|
||||||
|
import {MatSnackBar} from "@angular/material/snack-bar";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-overdue-task-overview',
|
||||||
|
templateUrl: './overdue-task-overview.component.html',
|
||||||
|
styleUrls: ['./overdue-task-overview.component.css']
|
||||||
|
})
|
||||||
|
export class OverdueTaskOverviewComponent implements OnInit{
|
||||||
|
|
||||||
|
overdueTasks: TaskTaskgroupInfo[] = []
|
||||||
|
defaultNavigationLinkPath: NavigationLink[] = [
|
||||||
|
{
|
||||||
|
linkText: "Dashboard",
|
||||||
|
routerLink: ['/']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
linkText: "Overdue Tasks",
|
||||||
|
routerLink: ["/overdue"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
constructor(private taskService: TaskService,
|
||||||
|
private scheduleService: ScheduleService,
|
||||||
|
private router: Router,
|
||||||
|
private snackbar: MatSnackBar) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.taskService.tasksAllScopeDetailedGet("OVERDUE", true).subscribe({
|
||||||
|
next: resp => {
|
||||||
|
this.overdueTasks = resp as TaskTaskgroupInfo[];
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
calcProgress(task: TaskTaskgroupInfo): number {
|
||||||
|
console.log(task.workTime / task.eta * 100)
|
||||||
|
return task.workTime / task.eta * 100
|
||||||
|
}
|
||||||
|
startNow(task: TaskTaskgroupInfo) {
|
||||||
|
this.scheduleService.schedulesTaskIDNowPost(task.taskID).subscribe({
|
||||||
|
next: resp => {
|
||||||
|
this.router.navigateByUrl("/");
|
||||||
|
},
|
||||||
|
error: err => {
|
||||||
|
if(err.status == 403) {
|
||||||
|
this.snackbar.open("No permissions", "", {duration: 2000});
|
||||||
|
} else if(err.status == 404) {
|
||||||
|
this.snackbar.open("Not found", "", {duration: 2000});
|
||||||
|
} else if(err.status == 409) {
|
||||||
|
this.snackbar.open("Task already running", "", {duration: 2000});
|
||||||
|
} else {
|
||||||
|
this.snackbar.open("Unexpected error", "", {duration: 2000});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
finishTask(task: TaskTaskgroupInfo) {
|
||||||
|
this.taskService.tasksTaskIDFinishPost(task.taskID).subscribe({
|
||||||
|
next: resp => {
|
||||||
|
this.overdueTasks = this.overdueTasks.filter(t => t.taskID !== task.taskID)
|
||||||
|
},
|
||||||
|
error: err => {
|
||||||
|
if(err.status == 403) {
|
||||||
|
this.snackbar.open("No permissions", "", {duration: 2000});
|
||||||
|
} else if(err.status == 404) {
|
||||||
|
this.snackbar.open("Not found", "", {duration: 2000});
|
||||||
|
} else {
|
||||||
|
this.snackbar.open("Unexpected error", "", {duration: 2000});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteTask(deletedTask: TaskTaskgroupInfo) {
|
||||||
|
this.taskService.tasksTaskIDDelete(deletedTask.taskID).subscribe({
|
||||||
|
next: resp => {
|
||||||
|
this.overdueTasks = this.overdueTasks.filter(task => task.taskID !== deletedTask.taskID);
|
||||||
|
},
|
||||||
|
error: err => {
|
||||||
|
if(err.status == 403) {
|
||||||
|
this.snackbar.open("No permissions", "", {duration: 2000});
|
||||||
|
} else if(err.status == 404) {
|
||||||
|
this.snackbar.open("Not found", "", {duration: 2000});
|
||||||
|
} else {
|
||||||
|
this.snackbar.open("Unexpected error", "", {duration: 2000});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
77
openapi.yaml
77
openapi.yaml
@ -883,7 +883,7 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
type: object
|
type: object
|
||||||
$ref: "#/components/schemas/SimpleStatusResponse"
|
$ref: "#/components/schemas/SimpleStatusResponse"
|
||||||
/tasks/all/{finished}:
|
/tasks/all/{scope}/{detailed}:
|
||||||
get:
|
get:
|
||||||
security:
|
security:
|
||||||
- API_TOKEN: []
|
- API_TOKEN: []
|
||||||
@ -892,9 +892,19 @@ paths:
|
|||||||
summary: edits an existing task
|
summary: edits an existing task
|
||||||
description: edits an existing task
|
description: edits an existing task
|
||||||
parameters:
|
parameters:
|
||||||
- name: finished
|
- name: scope
|
||||||
in: path
|
in: path
|
||||||
description: fetches either finished or unfinished tasks
|
description: defines scope of fetched tasks
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- FINISHED
|
||||||
|
- UNFINISHED
|
||||||
|
- OVERDUE
|
||||||
|
- name: detailed
|
||||||
|
in: path
|
||||||
|
description: determines whether an detailed response is required or not
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
type: boolean
|
type: boolean
|
||||||
@ -905,9 +915,14 @@ paths:
|
|||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
type: array
|
oneOf:
|
||||||
items:
|
- type: array
|
||||||
$ref: '#/components/schemas/TaskShortInfo'
|
items:
|
||||||
|
$ref: '#/components/schemas/TaskShortInfo'
|
||||||
|
- type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/TaskTaskgroupInfo'
|
||||||
|
|
||||||
|
|
||||||
/tasks/{taskgroupID}/{status}:
|
/tasks/{taskgroupID}/{status}:
|
||||||
get:
|
get:
|
||||||
@ -2048,6 +2063,56 @@ 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
|
||||||
|
TaskTaskgroupInfo:
|
||||||
|
required:
|
||||||
|
- taskID
|
||||||
|
- taskName
|
||||||
|
- eta
|
||||||
|
- startDate
|
||||||
|
- deadline
|
||||||
|
- overdue
|
||||||
|
- finished
|
||||||
|
- workTime
|
||||||
|
- taskgroups
|
||||||
|
additionalProperties: false
|
||||||
|
properties:
|
||||||
|
taskID:
|
||||||
|
type: number
|
||||||
|
description: internal id of task
|
||||||
|
example: 1
|
||||||
|
taskName:
|
||||||
|
type: string
|
||||||
|
description: name of task
|
||||||
|
example: Vorlesung schauen
|
||||||
|
eta:
|
||||||
|
type: number
|
||||||
|
description: expected time to finish task
|
||||||
|
example: 10
|
||||||
|
minimum: 0
|
||||||
|
startDate:
|
||||||
|
type: string
|
||||||
|
format: date
|
||||||
|
description: date from which the task can be started
|
||||||
|
deadline:
|
||||||
|
type: string
|
||||||
|
format: date
|
||||||
|
description: date until the task has to be finished
|
||||||
|
overdue:
|
||||||
|
type: boolean
|
||||||
|
description: determines whether the task is overdue
|
||||||
|
example: True
|
||||||
|
finished:
|
||||||
|
type: boolean
|
||||||
|
description: determines whether the task is finished
|
||||||
|
example: True
|
||||||
|
workTime:
|
||||||
|
type: number
|
||||||
|
description: number in minutes that was already worked on this task
|
||||||
|
example: 10
|
||||||
|
taskgroups:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/TaskgroupEntityInfo'
|
||||||
TaskFieldInfo:
|
TaskFieldInfo:
|
||||||
required:
|
required:
|
||||||
- taskName
|
- taskName
|
||||||
|
Loading…
Reference in New Issue
Block a user