Merge pull request 'issue-36' (#39) from issue-36 into master
Reviewed-on: Sebastian/TimeManager#39
This commit is contained in:
		
						commit
						1b31a9239c
					
				@ -5,7 +5,12 @@
 | 
			
		||||
  </component>
 | 
			
		||||
  <component name="ChangeListManager">
 | 
			
		||||
    <list default="true" id="3a869f59-290a-4ab2-b036-a878ce801bc4" name="Changes" comment="Fix marking finished task as overdue">
 | 
			
		||||
      <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" />
 | 
			
		||||
      <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/repositories/timemanager/TaskRepository.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/core/repositories/timemanager/TaskRepository.java" afterDir="false" />
 | 
			
		||||
      <change beforePath="$PROJECT_DIR$/src/main/java/core/services/TaskService.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/core/services/TaskService.java" afterDir="false" />
 | 
			
		||||
      <change beforePath="$PROJECT_DIR$/../frontend/src/app/app-routing.module.ts" beforeDir="false" afterPath="$PROJECT_DIR$/../frontend/src/app/app-routing.module.ts" afterDir="false" />
 | 
			
		||||
      <change beforePath="$PROJECT_DIR$/../frontend/src/app/app.component.html" beforeDir="false" afterPath="$PROJECT_DIR$/../frontend/src/app/app.component.html" afterDir="false" />
 | 
			
		||||
      <change beforePath="$PROJECT_DIR$/../frontend/src/app/app.module.ts" beforeDir="false" afterPath="$PROJECT_DIR$/../frontend/src/app/app.module.ts" afterDir="false" />
 | 
			
		||||
    </list>
 | 
			
		||||
    <option name="SHOW_DIALOG" value="false" />
 | 
			
		||||
    <option name="HIGHLIGHT_CONFLICTS" value="true" />
 | 
			
		||||
@ -41,34 +46,34 @@
 | 
			
		||||
    <option name="hideEmptyMiddlePackages" value="true" />
 | 
			
		||||
    <option name="showLibraryContents" value="true" />
 | 
			
		||||
  </component>
 | 
			
		||||
  <component name="PropertiesComponent"><![CDATA[{
 | 
			
		||||
  "keyToString": {
 | 
			
		||||
    "RequestMappingsPanelOrder0": "0",
 | 
			
		||||
    "RequestMappingsPanelOrder1": "1",
 | 
			
		||||
    "RequestMappingsPanelWidth0": "75",
 | 
			
		||||
    "RequestMappingsPanelWidth1": "75",
 | 
			
		||||
    "RunOnceActivity.OpenProjectViewOnStart": "true",
 | 
			
		||||
    "RunOnceActivity.ShowReadmeOnStart": "true",
 | 
			
		||||
    "WebServerToolWindowFactoryState": "false",
 | 
			
		||||
    "extract.method.default.visibility": "private",
 | 
			
		||||
    "git-widget-placeholder": "issue-11-angular-update",
 | 
			
		||||
    "last_directory_selection": "D:/Programmierprojekte/TimeManager/backend/src/main/java/core/api/models/timemanager",
 | 
			
		||||
    "last_opened_file_path": "D:/Programmierprojekte/Dicewars/client",
 | 
			
		||||
    "node.js.detected.package.eslint": "true",
 | 
			
		||||
    "node.js.detected.package.tslint": "true",
 | 
			
		||||
    "node.js.selected.package.eslint": "(autodetect)",
 | 
			
		||||
    "node.js.selected.package.tslint": "(autodetect)",
 | 
			
		||||
    "nodejs_package_manager_path": "npm",
 | 
			
		||||
    "settings.editor.selected.configurable": "swagger",
 | 
			
		||||
    "ts.external.directory.path": "/snap/intellij-idea-ultimate/459/plugins/javascript-impl/jsLanguageServicesImpl/external",
 | 
			
		||||
    "vue.rearranger.settings.migration": "true"
 | 
			
		||||
  <component name="PropertiesComponent">{
 | 
			
		||||
  "keyToString": {
 | 
			
		||||
    "RequestMappingsPanelOrder0": "0",
 | 
			
		||||
    "RequestMappingsPanelOrder1": "1",
 | 
			
		||||
    "RequestMappingsPanelWidth0": "75",
 | 
			
		||||
    "RequestMappingsPanelWidth1": "75",
 | 
			
		||||
    "RunOnceActivity.OpenProjectViewOnStart": "true",
 | 
			
		||||
    "RunOnceActivity.ShowReadmeOnStart": "true",
 | 
			
		||||
    "WebServerToolWindowFactoryState": "false",
 | 
			
		||||
    "extract.method.default.visibility": "private",
 | 
			
		||||
    "git-widget-placeholder": "issue-11-angular-update",
 | 
			
		||||
    "last_directory_selection": "D:/Programmierprojekte/TimeManager/backend/src/main/java/core/api/models/timemanager",
 | 
			
		||||
    "last_opened_file_path": "D:/Programmierprojekte/Dicewars/client",
 | 
			
		||||
    "node.js.detected.package.eslint": "true",
 | 
			
		||||
    "node.js.detected.package.tslint": "true",
 | 
			
		||||
    "node.js.selected.package.eslint": "(autodetect)",
 | 
			
		||||
    "node.js.selected.package.tslint": "(autodetect)",
 | 
			
		||||
    "nodejs_package_manager_path": "npm",
 | 
			
		||||
    "settings.editor.selected.configurable": "swagger",
 | 
			
		||||
    "ts.external.directory.path": "/snap/intellij-idea-ultimate/459/plugins/javascript-impl/jsLanguageServicesImpl/external",
 | 
			
		||||
    "vue.rearranger.settings.migration": "true"
 | 
			
		||||
  },
 | 
			
		||||
  "keyToStringList": {
 | 
			
		||||
    "DatabaseDriversLRU": [
 | 
			
		||||
      "mariadb"
 | 
			
		||||
  "keyToStringList": {
 | 
			
		||||
    "DatabaseDriversLRU": [
 | 
			
		||||
      "mariadb"
 | 
			
		||||
    ]
 | 
			
		||||
  }
 | 
			
		||||
}]]></component>
 | 
			
		||||
}</component>
 | 
			
		||||
  <component name="RunManager">
 | 
			
		||||
    <configuration name="DemoApplication" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot" nameIsGenerated="true">
 | 
			
		||||
      <module name="demo" />
 | 
			
		||||
 | 
			
		||||
@ -4,5 +4,7 @@ public enum TaskScope {
 | 
			
		||||
 | 
			
		||||
    FINISHED,
 | 
			
		||||
    UNFINISHED,
 | 
			
		||||
    OVERDUE;
 | 
			
		||||
    OVERDUE,
 | 
			
		||||
    UPCOMING,
 | 
			
		||||
    ACTIVE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -29,4 +29,10 @@ public interface TaskRepository extends CrudRepository<Task, Long> {
 | 
			
		||||
 | 
			
		||||
    @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);
 | 
			
		||||
 | 
			
		||||
    @Query(value = "SELECT t FROM Task t WHERE t.taskgroup.user.username = ?1 AND t.startDate IS NOT NULL AND t.startDate > ?2 AND t.finished = FALSE")
 | 
			
		||||
    List<Task> findAllUpcoming(String username, LocalDate now);
 | 
			
		||||
 | 
			
		||||
    @Query(value = "SELECT t FROM Task t WHERE t.taskgroup.user.username = ?1 AND t.startDate IS NULL OR t.startDate <= ?2 AND t.finished = FALSE")
 | 
			
		||||
    List<Task> findAllActive(String username, LocalDate now);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -109,6 +109,12 @@ public class TaskService {
 | 
			
		||||
            case OVERDUE -> {
 | 
			
		||||
                return taskRepository.findAllOverdue(username, LocalDate.now());
 | 
			
		||||
            }
 | 
			
		||||
            case UPCOMING -> {
 | 
			
		||||
                return taskRepository.findAllUpcoming(username, LocalDate.now());
 | 
			
		||||
            }
 | 
			
		||||
            case ACTIVE -> {
 | 
			
		||||
                return taskRepository.findAllActive(username, LocalDate.now());
 | 
			
		||||
            }
 | 
			
		||||
            default -> {
 | 
			
		||||
                return new ArrayList<>();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@ -98,10 +98,10 @@ export class TaskService {
 | 
			
		||||
     * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
 | 
			
		||||
     * @param reportProgress flag to report request and response progress.
 | 
			
		||||
     */
 | 
			
		||||
    public tasksAllScopeDetailedGet(scope: 'FINISHED' | 'UNFINISHED' | 'OVERDUE', detailed: boolean, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<Array<TaskShortInfo> | Array<TaskTaskgroupInfo>>;
 | 
			
		||||
    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 tasksAllScopeDetailedGet(scope: 'FINISHED' | 'UNFINISHED' | 'OVERDUE', detailed: boolean, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<HttpEvent<Array<TaskShortInfo> | Array<TaskTaskgroupInfo>>>;
 | 
			
		||||
    public tasksAllScopeDetailedGet(scope: 'FINISHED' | 'UNFINISHED' | 'OVERDUE', detailed: boolean, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<any> {
 | 
			
		||||
    public tasksAllScopeDetailedGet(scope: 'FINISHED' | 'UNFINISHED' | 'OVERDUE' | 'UPCOMING' | 'ACTIVE', detailed: boolean, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<Array<TaskShortInfo> | Array<TaskTaskgroupInfo>>;
 | 
			
		||||
    public tasksAllScopeDetailedGet(scope: 'FINISHED' | 'UNFINISHED' | 'OVERDUE' | 'UPCOMING' | 'ACTIVE', detailed: boolean, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<HttpResponse<Array<TaskShortInfo> | Array<TaskTaskgroupInfo>>>;
 | 
			
		||||
    public tasksAllScopeDetailedGet(scope: 'FINISHED' | 'UNFINISHED' | 'OVERDUE' | 'UPCOMING' | 'ACTIVE', detailed: boolean, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<HttpEvent<Array<TaskShortInfo> | Array<TaskTaskgroupInfo>>>;
 | 
			
		||||
    public tasksAllScopeDetailedGet(scope: 'FINISHED' | 'UNFINISHED' | 'OVERDUE' | 'UPCOMING' | 'ACTIVE', detailed: boolean, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<any> {
 | 
			
		||||
        if (scope === null || scope === undefined) {
 | 
			
		||||
            throw new Error('Required parameter scope was null or undefined when calling tasksAllScopeDetailedGet.');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,89 @@
 | 
			
		||||
.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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.grayBtn {
 | 
			
		||||
  background-color: #444444;
 | 
			
		||||
  color: white;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.overdue-warning {
 | 
			
		||||
  background-color: orange;
 | 
			
		||||
  color: white;
 | 
			
		||||
  padding: 10px;
 | 
			
		||||
  border-radius: 6px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.progress-bar {
 | 
			
		||||
  margin-top: 20px;
 | 
			
		||||
  margin-bottom: 20px;
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,33 @@
 | 
			
		||||
<div class="container">
 | 
			
		||||
  <app-navigation-link-list [navigationLinks]="defaultNavigationLinkPath"></app-navigation-link-list>
 | 
			
		||||
 | 
			
		||||
  <mat-card *ngFor="let task of tasks">
 | 
			
		||||
    <mat-card-content>
 | 
			
		||||
      <h3>
 | 
			
		||||
        <span *ngFor="let taskgroup of task.taskgroups">
 | 
			
		||||
          <a class="undecorated-link" [routerLink]="['/taskgroups', taskgroup.taskgroupID]">{{taskgroup.taskgroupName}}</a> /
 | 
			
		||||
        </span>
 | 
			
		||||
        <a class="undecorated-link" [routerLink]="['/taskgroups', task.taskgroups[task.taskgroups.length-1].taskgroupID, 'tasks', task.taskID]">{{task.taskName}}</a>
 | 
			
		||||
      </h3>
 | 
			
		||||
      <div class="overdue-warning" *ngIf="task.overdue">
 | 
			
		||||
        <b>This task is overdue!</b>
 | 
			
		||||
      </div>
 | 
			
		||||
      <mat-progress-bar class="progress-bar" mode="determinate" [value]="calcProgress(task)"></mat-progress-bar>
 | 
			
		||||
      <div class="originally-planned-container">
 | 
			
		||||
        <p *ngIf="task.eta != undefined"><i>ETA: </i> {{task.eta}} Minutes</p>
 | 
			
		||||
        <p *ngIf="task.deadline != undefined"><i>Deadline: </i> {{task.deadline}}</p>
 | 
			
		||||
        <div class="reschedule-actions-container">
 | 
			
		||||
          <div class="btn-group">
 | 
			
		||||
            <button mat-raised-button color="primary" class="btn-group-itemleft" [routerLink]="['/taskgroups', task.taskgroups[task.taskgroups.length-1].taskgroupID, 'tasks', task.taskID, 'schedule']">Schedule</button>
 | 
			
		||||
            <button mat-raised-button class="yellowBtn btn-group-item" (click)="startTaskNow(task)">Start now</button>
 | 
			
		||||
            <button mat-raised-button class="btn-group-item grayBtn" (click)="editTask(task)">Edit</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 { ActiveTaskOverviewComponent } from './active-task-overview.component';
 | 
			
		||||
 | 
			
		||||
describe('ActiveTaskOverviewComponent', () => {
 | 
			
		||||
  let component: ActiveTaskOverviewComponent;
 | 
			
		||||
  let fixture: ComponentFixture<ActiveTaskOverviewComponent>;
 | 
			
		||||
 | 
			
		||||
  beforeEach(() => {
 | 
			
		||||
    TestBed.configureTestingModule({
 | 
			
		||||
      declarations: [ActiveTaskOverviewComponent]
 | 
			
		||||
    });
 | 
			
		||||
    fixture = TestBed.createComponent(ActiveTaskOverviewComponent);
 | 
			
		||||
    component = fixture.componentInstance;
 | 
			
		||||
    fixture.detectChanges();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should create', () => {
 | 
			
		||||
    expect(component).toBeTruthy();
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
@ -0,0 +1,117 @@
 | 
			
		||||
import {Component, OnInit} from '@angular/core';
 | 
			
		||||
import {NavigationLink} from "../navigation-link-list/navigation-link-list.component";
 | 
			
		||||
import {ScheduleService, TaskService, TaskTaskgroupInfo} from "../../api";
 | 
			
		||||
import {MatSnackBar} from "@angular/material/snack-bar";
 | 
			
		||||
import {Router} from "@angular/router";
 | 
			
		||||
import {TaskEditorData} from "../tasks/task-editor/TaskEditorData";
 | 
			
		||||
import {TaskEditorComponent} from "../tasks/task-editor/task-editor.component";
 | 
			
		||||
import {MatDialog} from "@angular/material/dialog";
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'app-active-task-overview',
 | 
			
		||||
  templateUrl: './active-task-overview.component.html',
 | 
			
		||||
  styleUrls: ['./active-task-overview.component.css']
 | 
			
		||||
})
 | 
			
		||||
export class ActiveTaskOverviewComponent implements OnInit{
 | 
			
		||||
  tasks: TaskTaskgroupInfo[] = []
 | 
			
		||||
  defaultNavigationLinkPath: NavigationLink[] = [
 | 
			
		||||
    {
 | 
			
		||||
      linkText: "Dashboard",
 | 
			
		||||
      routerLink: ['/']
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      linkText: "Active Tasks",
 | 
			
		||||
      routerLink: ["/active"]
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
 | 
			
		||||
  constructor(private taskService: TaskService,
 | 
			
		||||
              private snackbar: MatSnackBar,
 | 
			
		||||
              private scheduleService: ScheduleService,
 | 
			
		||||
              private router: Router,
 | 
			
		||||
              private dialog: MatDialog) {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ngOnInit(): void {
 | 
			
		||||
    this.taskService.tasksAllScopeDetailedGet("ACTIVE", true).subscribe({
 | 
			
		||||
      next: resp => {
 | 
			
		||||
        this.tasks = resp as TaskTaskgroupInfo[]
 | 
			
		||||
      },
 | 
			
		||||
      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});
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  calcProgress(task: TaskTaskgroupInfo): number {
 | 
			
		||||
    console.log(task.workTime / task.eta * 100)
 | 
			
		||||
    return task.workTime / task.eta * 100
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  startTaskNow(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});
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  deleteTask(deletedTask: TaskTaskgroupInfo) {
 | 
			
		||||
    this.taskService.tasksTaskIDDelete(deletedTask.taskID).subscribe({
 | 
			
		||||
      next: resp => {
 | 
			
		||||
        this.tasks = this.tasks.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});
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  editTask(editedTask: TaskTaskgroupInfo) {
 | 
			
		||||
    const taskEditorInfo: TaskEditorData = {
 | 
			
		||||
      task: editedTask,
 | 
			
		||||
      taskgroupID: editedTask.taskgroups[editedTask.taskgroups.length-1].taskgroupID
 | 
			
		||||
    };
 | 
			
		||||
    this.dialog.open(TaskEditorComponent, {data: taskEditorInfo, width: "600px"})
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  finishTask(task: TaskTaskgroupInfo) {
 | 
			
		||||
    this.taskService.tasksTaskIDFinishPost(task.taskID).subscribe({
 | 
			
		||||
      next: resp => {
 | 
			
		||||
        this.tasks = this.tasks.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});
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -9,6 +9,8 @@ import {TaskDetailOverviewComponent} from "./tasks/task-detail-overview/task-det
 | 
			
		||||
import {SchedulerComponent} from "./schedules/scheduler/scheduler.component";
 | 
			
		||||
import {MissedSchedulesComponent} from "./missed-schedules/missed-schedules.component";
 | 
			
		||||
import {OverdueTaskOverviewComponent} from "./overdue-task-overview/overdue-task-overview.component";
 | 
			
		||||
import {UpcomingTaskOverviewComponent} from "./upcoming-task-overview/upcoming-task-overview.component";
 | 
			
		||||
import {ActiveTaskOverviewComponent} from "./active-task-overview/active-task-overview.component";
 | 
			
		||||
 | 
			
		||||
const routes: Routes = [
 | 
			
		||||
  {path: '', component: MainComponent},
 | 
			
		||||
@ -20,7 +22,9 @@ const routes: Routes = [
 | 
			
		||||
  {path: 'taskgroups/:taskgroupID/tasks/:taskID/schedule', component: SchedulerComponent},
 | 
			
		||||
  {path: 'taskgroups/:taskgroupID/tasks/:taskID/schedule/:scheduleID', component: SchedulerComponent},
 | 
			
		||||
  {path: 'reschedule', component: MissedSchedulesComponent},
 | 
			
		||||
  {path: 'overdue', component: OverdueTaskOverviewComponent}
 | 
			
		||||
  {path: 'overdue', component: OverdueTaskOverviewComponent},
 | 
			
		||||
  {path: 'upcoming', component: UpcomingTaskOverviewComponent},
 | 
			
		||||
  {path: 'active', component: ActiveTaskOverviewComponent}
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
 | 
			
		||||
@ -4,6 +4,8 @@
 | 
			
		||||
  <button mat-button aria-label="Organize" *ngIf="authService.hasKey" [matMenuTriggerFor]="organizeMenu">Organize ▾</button>
 | 
			
		||||
  <mat-menu #organizeMenu=matMenu>
 | 
			
		||||
    <button mat-menu-item routerLink="taskgroups/" aria-label="Task groups">Taskgroups</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="overdue/" aria-label="Overdue Tasks">Overdue Tasks</button>
 | 
			
		||||
    <button mat-menu-item routerLink="reschedule/" aria-label="Missed Schedules">Missed Schedules</button>
 | 
			
		||||
  </mat-menu>
 | 
			
		||||
 | 
			
		||||
@ -72,6 +72,8 @@ import {MatAutocompleteModule} from "@angular/material/autocomplete";
 | 
			
		||||
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 { OverdueTaskOverviewComponent } from './overdue-task-overview/overdue-task-overview.component';
 | 
			
		||||
import { UpcomingTaskOverviewComponent } from './upcoming-task-overview/upcoming-task-overview.component';
 | 
			
		||||
import { ActiveTaskOverviewComponent } from './active-task-overview/active-task-overview.component';
 | 
			
		||||
@NgModule({
 | 
			
		||||
  declarations: [
 | 
			
		||||
    AppComponent,
 | 
			
		||||
@ -106,7 +108,9 @@ import { OverdueTaskOverviewComponent } from './overdue-task-overview/overdue-ta
 | 
			
		||||
    ForgottenTaskStartDialogComponent,
 | 
			
		||||
    MissedSchedulesComponent,
 | 
			
		||||
    MissedScheduleForgetConfirmationDialogComponent,
 | 
			
		||||
    OverdueTaskOverviewComponent
 | 
			
		||||
    OverdueTaskOverviewComponent,
 | 
			
		||||
    UpcomingTaskOverviewComponent,
 | 
			
		||||
    ActiveTaskOverviewComponent
 | 
			
		||||
  ],
 | 
			
		||||
  imports: [
 | 
			
		||||
    BrowserModule,
 | 
			
		||||
 | 
			
		||||
@ -48,6 +48,8 @@ export class TaskEditorComponent implements OnInit {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  createTask() {
 | 
			
		||||
    console.log(this.startDate.value)
 | 
			
		||||
    console.log(this.endDate.value)
 | 
			
		||||
    this.taskService.tasksTaskgroupIDPut(this.editorData.taskgroupID, {
 | 
			
		||||
      taskName: this.nameCtrl.value,
 | 
			
		||||
      eta: this.etaCtrl.value,
 | 
			
		||||
@ -72,8 +74,8 @@ export class TaskEditorComponent implements OnInit {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  editTask() {
 | 
			
		||||
    const startingDate = this.startDate.value.length == 0? null:this.startDate.value.length
 | 
			
		||||
    const deadline = this.endDate.value.length == 0? null:this.endDate.value.length
 | 
			
		||||
    const startingDate = this.startDate.value.length == 0? null:this.startDate.value
 | 
			
		||||
    const deadline = this.endDate.value.length == 0? null:this.endDate.value
 | 
			
		||||
    this.taskService.tasksTaskIDPost(this.editorData.task!.taskID, {
 | 
			
		||||
      taskName: this.nameCtrl.value,
 | 
			
		||||
      eta: this.etaCtrl.value,
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,77 @@
 | 
			
		||||
.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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.grayBtn {
 | 
			
		||||
  background-color: #444444;
 | 
			
		||||
  color: white;
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,24 @@
 | 
			
		||||
<div class="container">
 | 
			
		||||
  <app-navigation-link-list [navigationLinks]="defaultNavigationLinkPath"></app-navigation-link-list>
 | 
			
		||||
  <mat-card *ngFor="let task of upcomingTasks">
 | 
			
		||||
    <mat-card-content>
 | 
			
		||||
      <h3>
 | 
			
		||||
        <span *ngFor="let taskgroup of task.taskgroups">
 | 
			
		||||
          <a class="undecorated-link" [routerLink]="['/taskgroups', taskgroup.taskgroupID]">{{taskgroup.taskgroupName}}</a> /
 | 
			
		||||
        </span>
 | 
			
		||||
        <a class="undecorated-link" [routerLink]="['/taskgroups', task.taskgroups[task.taskgroups.length-1].taskgroupID, 'tasks', task.taskID]">{{task.taskName}}</a>
 | 
			
		||||
      </h3>
 | 
			
		||||
      <p><i>ETA: </i>{{task.eta}} Minutes</p>
 | 
			
		||||
      <p *ngIf="task.deadline != undefined"><i>Deadline: </i>{{task.deadline}}</p>
 | 
			
		||||
      <div class="reschedule-actions-container">
 | 
			
		||||
        <div class="btn-group">
 | 
			
		||||
          <button mat-raised-button color="primary" class="btn-group-itemleft" [routerLink]="['/taskgroups', task.taskgroups[task.taskgroups.length-1].taskgroupID, 'tasks', task.taskID, 'schedule']">Schedule</button>
 | 
			
		||||
          <button mat-raised-button class="grayBtn btn-group-itemright" (click)="editTask(task)">Edit</button>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="btn-group">
 | 
			
		||||
          <button mat-raised-button color="warn" (click)="deleteTask(task)"><mat-icon>delete</mat-icon>Delete</button>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </mat-card-content>
 | 
			
		||||
  </mat-card>
 | 
			
		||||
</div>
 | 
			
		||||
@ -0,0 +1,21 @@
 | 
			
		||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
			
		||||
 | 
			
		||||
import { UpcomingTaskOverviewComponent } from './upcoming-task-overview.component';
 | 
			
		||||
 | 
			
		||||
describe('UpcomingTaskOverviewComponent', () => {
 | 
			
		||||
  let component: UpcomingTaskOverviewComponent;
 | 
			
		||||
  let fixture: ComponentFixture<UpcomingTaskOverviewComponent>;
 | 
			
		||||
 | 
			
		||||
  beforeEach(() => {
 | 
			
		||||
    TestBed.configureTestingModule({
 | 
			
		||||
      declarations: [UpcomingTaskOverviewComponent]
 | 
			
		||||
    });
 | 
			
		||||
    fixture = TestBed.createComponent(UpcomingTaskOverviewComponent);
 | 
			
		||||
    component = fixture.componentInstance;
 | 
			
		||||
    fixture.detectChanges();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should create', () => {
 | 
			
		||||
    expect(component).toBeTruthy();
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
@ -0,0 +1,66 @@
 | 
			
		||||
import {Component, OnInit} from '@angular/core';
 | 
			
		||||
import {TaskService, TaskTaskgroupInfo} from "../../api";
 | 
			
		||||
import {NavigationLink} from "../navigation-link-list/navigation-link-list.component";
 | 
			
		||||
import {MatSnackBar} from "@angular/material/snack-bar";
 | 
			
		||||
import {TaskEditorData} from "../tasks/task-editor/TaskEditorData";
 | 
			
		||||
import {TaskEditorComponent} from "../tasks/task-editor/task-editor.component";
 | 
			
		||||
import {MatDialog} from "@angular/material/dialog";
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'app-upcoming-task-overview',
 | 
			
		||||
  templateUrl: './upcoming-task-overview.component.html',
 | 
			
		||||
  styleUrls: ['./upcoming-task-overview.component.css']
 | 
			
		||||
})
 | 
			
		||||
export class UpcomingTaskOverviewComponent implements OnInit{
 | 
			
		||||
 | 
			
		||||
  upcomingTasks: TaskTaskgroupInfo[] = []
 | 
			
		||||
  defaultNavigationLinkPath: NavigationLink[] = [
 | 
			
		||||
    {
 | 
			
		||||
      linkText: "Dashboard",
 | 
			
		||||
      routerLink: ['/']
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      linkText: "Upcoming Tasks",
 | 
			
		||||
      routerLink: ["/upcoming"]
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
 | 
			
		||||
  constructor(private taskService: TaskService,
 | 
			
		||||
              private snackbar: MatSnackBar,
 | 
			
		||||
              private dialog: MatDialog) {
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ngOnInit(): void {
 | 
			
		||||
    this.taskService.tasksAllScopeDetailedGet("UPCOMING", true).subscribe({
 | 
			
		||||
      next: resp => {
 | 
			
		||||
        this.upcomingTasks = resp as TaskTaskgroupInfo[];
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  deleteTask(deletedTask: TaskTaskgroupInfo) {
 | 
			
		||||
    this.taskService.tasksTaskIDDelete(deletedTask.taskID).subscribe({
 | 
			
		||||
      next: resp => {
 | 
			
		||||
        this.upcomingTasks = this.upcomingTasks.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});
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  editTask(editedTask: TaskTaskgroupInfo) {
 | 
			
		||||
    const taskEditorInfo: TaskEditorData = {
 | 
			
		||||
      task: editedTask,
 | 
			
		||||
      taskgroupID: editedTask.taskgroups[editedTask.taskgroups.length-1].taskgroupID
 | 
			
		||||
    };
 | 
			
		||||
    this.dialog.open(TaskEditorComponent, {data: taskEditorInfo, width: "600px"})
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -902,6 +902,8 @@ paths:
 | 
			
		||||
              - FINISHED
 | 
			
		||||
              - UNFINISHED
 | 
			
		||||
              - OVERDUE
 | 
			
		||||
              - UPCOMING
 | 
			
		||||
              - ACTIVE
 | 
			
		||||
        - name: detailed
 | 
			
		||||
          in: path
 | 
			
		||||
          description: determines whether an detailed response is required or not
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user