Merge pull request 'issue-57' (#71) from issue-57 into master
All checks were successful
Java CI with Maven / test (push) Successful in 34s
Java CI with Maven / build-and-push-frontend (push) Successful in 2m0s
Java CI with Maven / build-and-push-backend (push) Successful in 1m27s

Reviewed-on: #71
This commit is contained in:
sebastian 2023-11-18 08:39:12 +01:00
commit edf19a52ba
9 changed files with 137 additions and 47 deletions

View File

@ -208,4 +208,8 @@ public class TaskScheduleService {
} }
return activatedSchedules; return activatedSchedules;
} }
public void deleteSchedules(List<AbstractSchedule> taskSchedules) {
scheduleRepository.deleteAll(taskSchedules);
}
} }

View File

@ -2,6 +2,7 @@ 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.api.models.timemanager.tasks.TaskScope;
import core.entities.timemanager.AbstractSchedule;
import core.entities.timemanager.Task; import core.entities.timemanager.Task;
import core.entities.timemanager.Taskgroup; import core.entities.timemanager.Taskgroup;
import core.repositories.timemanager.TaskRepository; import core.repositories.timemanager.TaskRepository;
@ -103,21 +104,20 @@ public class TaskService {
task.finish(); task.finish();
taskRepository.save(task); taskRepository.save(task);
/*List<BasicTaskSchedule> removedBasicTaskSchedules = new LinkedList<>();
for(BasicTaskSchedule basicTaskSchedule : task.getBasicTaskSchedules()) { List<AbstractSchedule> removedBasicTaskSchedules = new LinkedList<>();
for(AbstractSchedule basicTaskSchedule : task.getBasicTaskSchedules()) {
if(basicTaskSchedule.getStartTime() == null) { if(basicTaskSchedule.getStartTime() == null) {
removedBasicTaskSchedules.add(basicTaskSchedule); removedBasicTaskSchedules.add(basicTaskSchedule);
} else if(basicTaskSchedule.getFinishedTime() == null) { } else if(basicTaskSchedule.getStopTime() == null) {
ServiceResult<?> result = taskScheduleService.stopSchedule(basicTaskSchedule, true); ServiceResult<?> result = taskScheduleService.stopSchedule(basicTaskSchedule, true);
System.out.println(result); System.out.println(result);
} }
} }
for(BasicTaskSchedule deletedTaskSchedule: removedBasicTaskSchedules) { task.getBasicTaskSchedules().removeAll(removedBasicTaskSchedules);
task.getBasicTaskSchedules().remove(deletedTaskSchedule);
taskRepository.save(task); taskRepository.save(task);
taskScheduleService.deleteBasicSchedule(deletedTaskSchedule); taskScheduleService.deleteSchedules(removedBasicTaskSchedules);
}*/
} }
public List<Task> loadAllTasks(String username, TaskScope scope) { public List<Task> loadAllTasks(String username, TaskScope scope) {

View File

@ -2,6 +2,7 @@ package core.tasks;
import core.api.models.timemanager.tasks.TaskFieldInfo; import core.api.models.timemanager.tasks.TaskFieldInfo;
import core.api.models.timemanager.tasks.TaskScope; import core.api.models.timemanager.tasks.TaskScope;
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;
import core.repositories.timemanager.TaskRepository; import core.repositories.timemanager.TaskRepository;
@ -43,7 +44,8 @@ public class TaskServiceTest {
@Test @Test
@SqlGroup({ @SqlGroup({
@Sql("classpath:taskgroupRepositoryTestEntries.sql"), @Sql("classpath:taskgroupRepositoryTestEntries.sql"),
@Sql("classpath:taskRepositoryEntries.sql") @Sql("classpath:taskRepositoryEntries.sql"),
@Sql("classpath:basicScheduleEntries.sql")
}) })
void createTask() { void createTask() {
//Situation 1: Task with that name already exists in a taskgroup //Situation 1: Task with that name already exists in a taskgroup
@ -85,7 +87,8 @@ public class TaskServiceTest {
@Test @Test
@SqlGroup({ @SqlGroup({
@Sql("classpath:taskgroupRepositoryTestEntries.sql"), @Sql("classpath:taskgroupRepositoryTestEntries.sql"),
@Sql("classpath:taskRepositoryEntries.sql") @Sql("classpath:taskRepositoryEntries.sql"),
@Sql("classpath:basicScheduleEntries.sql")
}) })
void getTaskPermissions() { void getTaskPermissions() {
//Situation 1: correct task and username //Situation 1: correct task and username
@ -110,7 +113,8 @@ public class TaskServiceTest {
@Test @Test
@SqlGroup({ @SqlGroup({
@Sql("classpath:taskgroupRepositoryTestEntries.sql"), @Sql("classpath:taskgroupRepositoryTestEntries.sql"),
@Sql("classpath:taskRepositoryEntries.sql") @Sql("classpath:taskRepositoryEntries.sql"),
@Sql("classpath:basicScheduleEntries.sql")
}) })
void editTask() { void editTask() {
//Situation 1: Nothing is updated //Situation 1: Nothing is updated
@ -182,7 +186,8 @@ public class TaskServiceTest {
@Test @Test
@SqlGroup({ @SqlGroup({
@Sql("classpath:taskgroupRepositoryTestEntries.sql"), @Sql("classpath:taskgroupRepositoryTestEntries.sql"),
@Sql("classpath:taskRepositoryEntries.sql") @Sql("classpath:taskRepositoryEntries.sql"),
@Sql("classpath:basicScheduleEntries.sql")
}) })
void deleteTask() { void deleteTask() {
for(long i=1; i<=15; i++) { for(long i=1; i<=15; i++) {
@ -194,7 +199,8 @@ public class TaskServiceTest {
@Test @Test
@SqlGroup({ @SqlGroup({
@Sql("classpath:taskgroupRepositoryTestEntries.sql"), @Sql("classpath:taskgroupRepositoryTestEntries.sql"),
@Sql("classpath:taskRepositoryEntries.sql") @Sql("classpath:taskRepositoryEntries.sql"),
@Sql("classpath:basicScheduleEntries.sql")
}) })
void clearTasks() { void clearTasks() {
//Situation 1: Delete from taskgroup with no tasks //Situation 1: Delete from taskgroup with no tasks
@ -212,7 +218,8 @@ public class TaskServiceTest {
@Test @Test
@SqlGroup({ @SqlGroup({
@Sql("classpath:taskgroupRepositoryTestEntries.sql"), @Sql("classpath:taskgroupRepositoryTestEntries.sql"),
@Sql("classpath:taskRepositoryEntries.sql") @Sql("classpath:taskRepositoryEntries.sql"),
@Sql("classpath:basicScheduleEntries.sql")
}) })
void finishTask() { void finishTask() {
taskService.finishTask(entityManager.find(Task.class, 1L)); taskService.finishTask(entityManager.find(Task.class, 1L));
@ -220,12 +227,14 @@ public class TaskServiceTest {
taskService.finishTask(entityManager.find(Task.class, 2L)); taskService.finishTask(entityManager.find(Task.class, 2L));
assertTrue(entityManager.find(Task.class, 1L).isFinished()); assertTrue(entityManager.find(Task.class, 1L).isFinished());
assertThat(entityManager.find(BasicTaskSchedule.class, 2L)).isNull();
} }
@Test @Test
@SqlGroup({ @SqlGroup({
@Sql("classpath:taskgroupRepositoryTestEntries.sql"), @Sql("classpath:taskgroupRepositoryTestEntries.sql"),
@Sql("classpath:taskRepositoryEntries.sql") @Sql("classpath:taskRepositoryEntries.sql"),
@Sql("classpath:basicScheduleEntries.sql")
}) })
void loadAllTasks() { void loadAllTasks() {
//Situation 1: No tasks existing //Situation 1: No tasks existing

View File

@ -5,6 +5,12 @@ import {TaskOverviewComponent} from "../task-overview/task-overview.component";
import {MatDialog} from "@angular/material/dialog"; import {MatDialog} from "@angular/material/dialog";
import {ForgottenTaskStartDialogComponent} from "../forgotten-task-start-dialog/forgotten-task-start-dialog.component"; import {ForgottenTaskStartDialogComponent} from "../forgotten-task-start-dialog/forgotten-task-start-dialog.component";
export interface StopActiveScheduleEmitterInfo {
stopactiveScheduleResponse: StopActiveScheduleInfo,
finish: boolean,
taskID: number
}
@Component({ @Component({
selector: 'app-active-schedule', selector: 'app-active-schedule',
templateUrl: './active-schedule.component.html', templateUrl: './active-schedule.component.html',
@ -17,7 +23,7 @@ export class ActiveScheduleComponent implements OnInit{
currentTime: number = 0; currentTime: number = 0;
displayTime: string = "00:00:00" displayTime: string = "00:00:00"
@Output('onStopTask') scheduleStopEmitter = new EventEmitter<StopActiveScheduleInfo>; @Output('onStopTask') scheduleStopEmitter = new EventEmitter<StopActiveScheduleEmitterInfo>;
@Output('registerForgotten') registerForgottenEmitter = new EventEmitter<TaskScheduleStopResponse> @Output('registerForgotten') registerForgottenEmitter = new EventEmitter<TaskScheduleStopResponse>
constructor(private scheduleService: ScheduleService, constructor(private scheduleService: ScheduleService,
@ -60,8 +66,12 @@ export class ActiveScheduleComponent implements OnInit{
this.scheduleService.schedulesScheduleIDStopFinishPost(this.activeSchedule!.scheduleID, finish).subscribe({ this.scheduleService.schedulesScheduleIDStopFinishPost(this.activeSchedule!.scheduleID, finish).subscribe({
next: resp => { next: resp => {
this.scheduleStopEmitter.emit({ this.scheduleStopEmitter.emit({
stopactiveScheduleResponse: {
schedule: this.activeSchedule!, schedule: this.activeSchedule!,
workedMinutes: resp.workTime workedMinutes: resp.workTime
},
finish: finish,
taskID: this.activeSchedule!.task.taskID
}) })
console.log(resp.workTime) console.log(resp.workTime)
this.activeSchedule = undefined this.activeSchedule = undefined

View File

@ -39,12 +39,12 @@
</div> </div>
<div class="taskgroup-overview"> <div class="taskgroup-overview">
<app-task-overview [tasks]="tasks" (onStartNow)="onStartTaskNow($event)" [taskgroupID]="selectedTaskgroupID" (onFinished)="onFinishTask($event)"></app-task-overview> <app-task-overview [tasks]="tasks" (onStartNow)="onStartTaskNow($event)" [taskgroupID]="selectedTaskgroupID" (onFinished)="onFinishTask($event)" (onCreated)="onCreatedTask($event)"></app-task-overview>
</div> </div>
<div class="taskgroup-overview"> <div class="taskgroup-overview">
<app-taskgroup-overview (taskgroupSelected)="onSelectTaskgroup($event)"></app-taskgroup-overview> <app-taskgroup-overview #taskgroupOverview (taskgroupSelected)="onSelectTaskgroup($event)"></app-taskgroup-overview>
</div> </div>

View File

@ -6,10 +6,10 @@ import {
TaskOverviewInfo, TaskOverviewInfo,
TaskScheduleStopResponse TaskScheduleStopResponse
} from "../../api"; } from "../../api";
import {ActiveScheduleComponent} from "./active-schedule/active-schedule.component"; import {ActiveScheduleComponent, StopActiveScheduleEmitterInfo} from "./active-schedule/active-schedule.component";
import {StopActiveScheduleInfo} from "./active-schedule/StopActiveScheduleInfo"; import {StopActiveScheduleInfo} from "./active-schedule/StopActiveScheduleInfo";
import {TaskOverviewComponent} from "./task-overview/task-overview.component"; import {TaskCreationEmitterInfo, TaskOverviewComponent} from "./task-overview/task-overview.component";
import {TaskOverviewData} from "./taskgroup-overview/taskgroup-overview.component"; import {TaskgroupOverviewComponent, TaskOverviewData} from "./taskgroup-overview/taskgroup-overview.component";
@Component({ @Component({
selector: 'app-dashboard', selector: 'app-dashboard',
@ -26,6 +26,7 @@ export class DashboardComponent implements OnInit{
selectedTaskgroupID: number | undefined selectedTaskgroupID: number | undefined
@ViewChild('activeSchedule') activeScheduleComponent: ActiveScheduleComponent | undefined @ViewChild('activeSchedule') activeScheduleComponent: ActiveScheduleComponent | undefined
@ViewChild('taskgroupOverview') taskroupOverviewComponent: TaskgroupOverviewComponent | undefined
constructor(private scheduleService: ScheduleService, constructor(private scheduleService: ScheduleService,
private historyService: HistoryService) { private historyService: HistoryService) {
} }
@ -59,9 +60,13 @@ export class DashboardComponent implements OnInit{
} }
stopedTask(stopActiveScheduleInfo: StopActiveScheduleInfo) { stopedTask(stopActiveScheduleInfo: StopActiveScheduleEmitterInfo) {
this.workedMinutesToday += stopActiveScheduleInfo.workedMinutes; this.workedMinutesToday += stopActiveScheduleInfo.stopactiveScheduleResponse.workedMinutes;
this.schedules = this.schedules.filter(schedule => schedule.scheduleID !== stopActiveScheduleInfo.schedule.scheduleID); this.schedules = this.schedules.filter(schedule => schedule.scheduleID !== stopActiveScheduleInfo.stopactiveScheduleResponse.schedule.scheduleID);
if(stopActiveScheduleInfo.finish) {
this.taskroupOverviewComponent!.finishTask(stopActiveScheduleInfo.taskID);
}
} }
@ -73,16 +78,24 @@ export class DashboardComponent implements OnInit{
} }
onStartTaskNow(schedule: ScheduleInfo) { onStartTaskNow(schedule: ScheduleInfo) {
this.activeScheduleComponent?.activateSchedule(schedule) this.activeScheduleComponent?.activateSchedule(schedule);
this.selectedTaskgroupID = undefined
} }
onFinishTask(task: TaskOverviewInfo) { onFinishTask(task: TaskOverviewInfo) {
this.activeScheduleComponent?.finishTaskByOverview(task); this.activeScheduleComponent?.finishTaskByOverview(task);
this.schedules = this.schedules.filter(schedule => schedule.task.taskID !== task.taskID) this.schedules = this.schedules.filter(schedule => schedule.task.taskID !== task.taskID)
this.taskroupOverviewComponent!.finishTask(task.taskID);
this.tasks = [];
this.selectedTaskgroupID = undefined;
} }
registerForgotten(taskScheduleStopResponse: TaskScheduleStopResponse) { registerForgotten(taskScheduleStopResponse: TaskScheduleStopResponse) {
this.workedMinutesToday += taskScheduleStopResponse.workTime this.workedMinutesToday += taskScheduleStopResponse.workTime
} }
onCreatedTask(taskCreationInfo: TaskCreationEmitterInfo) {
this.taskroupOverviewComponent!.onCreateTask(taskCreationInfo);
}
} }

View File

@ -1,4 +1,5 @@
<button mat-raised-button class="greenBtn long-btn" *ngIf="taskgroupID != undefined" (click)="openTaskCreation()">Add</button> <div *ngIf="taskgroupID != undefined" >
<button mat-raised-button class="greenBtn long-btn"(click)="openTaskCreation()">Add</button>
<mat-card *ngFor="let task of tasks"> <mat-card *ngFor="let task of tasks">
<mat-card-content> <mat-card-content>
<h3><a class="task-link" [routerLink]="['/taskgroups', taskgroupID!, 'tasks', task.taskID]">{{task.taskName}}</a></h3> <h3><a class="task-link" [routerLink]="['/taskgroups', taskgroupID!, 'tasks', task.taskID]">{{task.taskName}}</a></h3>
@ -12,3 +13,5 @@
<button mat-raised-button class="greenBtn btn-without-radius" (click)="finishTask(task)">Finish</button> <button mat-raised-button class="greenBtn btn-without-radius" (click)="finishTask(task)">Finish</button>
</mat-card-actions> </mat-card-actions>
</mat-card> </mat-card>
</div>

View File

@ -1,10 +1,21 @@
import {Component, EventEmitter, Input, Output} from '@angular/core'; import {Component, EventEmitter, Input, Output} from '@angular/core';
import {BasicScheduleEntityInfo, ScheduleInfo, ScheduleService, TaskOverviewInfo, TaskService} from "../../../api"; import {
BasicScheduleEntityInfo,
ScheduleInfo,
ScheduleService,
TaskOverviewInfo,
TaskService,
TaskShortInfo
} from "../../../api";
import {MatSnackBar} from "@angular/material/snack-bar"; import {MatSnackBar} from "@angular/material/snack-bar";
import {TaskEditorData} from "../../tasks/task-editor/TaskEditorData"; import {TaskEditorData} from "../../tasks/task-editor/TaskEditorData";
import {TaskEditorComponent} from "../../tasks/task-editor/task-editor.component"; import {TaskEditorComponent} from "../../tasks/task-editor/task-editor.component";
import {MatDialog} from "@angular/material/dialog"; import {MatDialog} from "@angular/material/dialog";
export interface TaskCreationEmitterInfo {
taskgroupID: number,
task: TaskOverviewInfo
}
@Component({ @Component({
selector: 'app-task-overview', selector: 'app-task-overview',
templateUrl: './task-overview.component.html', templateUrl: './task-overview.component.html',
@ -16,6 +27,7 @@ export class TaskOverviewComponent {
@Input() taskgroupID: number | undefined @Input() taskgroupID: number | undefined
@Output('onStartNow') startNowEmitter: EventEmitter<ScheduleInfo> = new EventEmitter<ScheduleInfo>(); @Output('onStartNow') startNowEmitter: EventEmitter<ScheduleInfo> = new EventEmitter<ScheduleInfo>();
@Output('onFinished') finishedEmitter: EventEmitter<TaskOverviewInfo> = new EventEmitter<TaskOverviewInfo>(); @Output('onFinished') finishedEmitter: EventEmitter<TaskOverviewInfo> = new EventEmitter<TaskOverviewInfo>();
@Output('onCreated') creationEmitter: EventEmitter<TaskCreationEmitterInfo> = new EventEmitter<TaskCreationEmitterInfo>();
constructor(private scheduleService: ScheduleService, constructor(private scheduleService: ScheduleService,
private snackbar: MatSnackBar, private snackbar: MatSnackBar,
@ -68,14 +80,19 @@ export class TaskOverviewComponent {
const dialogRef = this.dialog.open(TaskEditorComponent, {data: editorData, width: "600px"}) const dialogRef = this.dialog.open(TaskEditorComponent, {data: editorData, width: "600px"})
dialogRef.afterClosed().subscribe(res => { dialogRef.afterClosed().subscribe(res => {
if(res != undefined) { if(res != undefined) {
this.tasks.push({ const taskOverviewInfo = {
taskID: res.taskID, taskID: res.taskID,
eta: res.eta, eta: res.eta,
limit: res.deadline, limit: res.deadline,
taskName: res.taskName, taskName: res.taskName,
activeTime: 0, activeTime: 0,
overdue: res.overdue overdue: res.overdue
}
this.creationEmitter.emit({
task: taskOverviewInfo,
taskgroupID: this.taskgroupID!
}) })
} }
}) })
} }

View File

@ -3,10 +3,8 @@ import {MatIconModule} from "@angular/material/icon";
import {MatButtonModule} from "@angular/material/button"; import {MatButtonModule} from "@angular/material/button";
import {MatTreeFlatDataSource, MatTreeFlattener, MatTreeModule} from "@angular/material/tree"; import {MatTreeFlatDataSource, MatTreeFlattener, MatTreeModule} from "@angular/material/tree";
import {FlatTreeControl} from "@angular/cdk/tree"; import {FlatTreeControl} from "@angular/cdk/tree";
import {RecursiveTaskgroupInfo, TaskgroupService, TaskOverviewInfo} from "../../../api"; import {RecursiveTaskgroupInfo, TaskgroupService, TaskOverviewInfo, TaskShortInfo} from "../../../api";
import {TaskOverviewComponent} from "../task-overview/task-overview.component"; import {TaskCreationEmitterInfo, TaskOverviewComponent} from "../task-overview/task-overview.component";
/** Flat node with expandable and level information */ /** Flat node with expandable and level information */
@ -24,6 +22,7 @@ export interface TaskOverviewData {
tasks: TaskOverviewInfo[], tasks: TaskOverviewInfo[],
taskgroupID: number taskgroupID: number
} }
@Component({ @Component({
selector: 'app-taskgroup-overview', selector: 'app-taskgroup-overview',
templateUrl: './taskgroup-overview.component.html', templateUrl: './taskgroup-overview.component.html',
@ -78,4 +77,39 @@ export class TaskgroupOverviewComponent {
taskgroupID: taskgroupID taskgroupID: taskgroupID
}) })
} }
finishTask(taskID: number) {
let taskgroupQueue : RecursiveTaskgroupInfo[] = this.dataSource.data.slice();
while(taskgroupQueue.length > 0) {
const currentTaskgroup = taskgroupQueue.pop()!;
const searchedTask = currentTaskgroup.activeTasks.find(searchedTask => searchedTask.taskID === taskID);
if(searchedTask == undefined) {
currentTaskgroup.childTaskgroups.forEach(childTask => {
taskgroupQueue.push(childTask);
})
} else {
currentTaskgroup.activeTasks = currentTaskgroup.activeTasks.filter(taskFilter => taskFilter.taskID !== searchedTask.taskID);
}
}
}
private findTaskgroup(taskgroupID: number) {
let taskgroupQueue : RecursiveTaskgroupInfo[] = this.dataSource.data.slice();
while(taskgroupQueue.length > 0) {
const currentTaskgroup = taskgroupQueue.pop()!;
if(currentTaskgroup.taskgroupID === taskgroupID) {
return currentTaskgroup;
} else {
currentTaskgroup.childTaskgroups.forEach(childgroup => taskgroupQueue.push(childgroup));
}
}
return undefined
}
onCreateTask(taskCreationInfo: TaskCreationEmitterInfo) {
const taskgroup: RecursiveTaskgroupInfo | undefined = this.findTaskgroup(taskCreationInfo.taskgroupID);
if(taskgroup != undefined) {
taskgroup.activeTasks.push(taskCreationInfo.task);
}
}
} }