diff --git a/frontend/src/app/schedules/basic-scheduler/basic-scheduler.component.ts b/frontend/src/app/schedules/basic-scheduler/basic-scheduler.component.ts
index f3c81e7..473ff71 100644
--- a/frontend/src/app/schedules/basic-scheduler/basic-scheduler.component.ts
+++ b/frontend/src/app/schedules/basic-scheduler/basic-scheduler.component.ts
@@ -9,6 +9,7 @@ import {
} from "../../../api";
import {MatSnackBar} from "@angular/material/snack-bar";
import {Router} from "@angular/router";
+import * as moment from "moment";
@Component({
selector: 'app-basic-scheduler',
@@ -36,7 +37,7 @@ export class BasicSchedulerComponent implements OnChanges{
if(this.task != undefined) {
if(this.scheduleEntityInfo == undefined) {
this.scheduleService.schedulesTaskIDBasicPut(this.task.taskID, {
- scheduleDate: this.dateCtrl.value
+ scheduleDate: moment(this.dateCtrl.value).format('YYYY-MM-DDTHH:mm:ss.SSSZ')
}).subscribe({
next: resp => {
this.scheduleEmitter.emit(resp as BasicScheduleInfo);
@@ -44,7 +45,7 @@ export class BasicSchedulerComponent implements OnChanges{
})
} else {
this.scheduleService.schedulesScheduleIDBasicPost(this.scheduleEntityInfo!.scheduleID, {
- scheduleDate: this.dateCtrl.value
+ scheduleDate: moment(this.dateCtrl.value).format('YYYY-MM-DDTHH:mm:ss.SSSZ')
}).subscribe({
next: resp => {
this.router.navigateByUrl("/taskgroups/" + this.taskgroup!.taskgroupID + "/tasks/" + this.task!.taskID);
diff --git a/frontend/src/app/schedules/draggable-scheduler/draggable-scheduler.component.css b/frontend/src/app/schedules/draggable-scheduler/draggable-scheduler.component.css
new file mode 100644
index 0000000..bbd0712
--- /dev/null
+++ b/frontend/src/app/schedules/draggable-scheduler/draggable-scheduler.component.css
@@ -0,0 +1,60 @@
+.container {
+ margin: 20px auto;
+ width: 80%;
+}
+
+.spacer {
+ margin-bottom: 2.5%;
+}
+
+
+@media screen and (max-width: 600px) {
+ .container {
+ width: 100%;
+ margin: 20px 10px;
+ }
+}
+
+.schedule-header {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.long-form {
+ width: 100%;
+}
+
+.taskgroup-overview {
+ width: 90%;
+ display: inline-block;
+}
+
+app-task-overview {
+ margin-top: 20px;
+}
+
+::ng-deep .mat-mdc-list-base {
+ --mdc-list-list-item-label-text-color: black
+}
+
+::ng-deep .mat-mdc-list-base .taskgroup-btn, ::ng-deep .mat-mdc-list-base .taskgroup-last-btn {
+ --mdc-list-list-item-label-text-color: black
+}
+
+.task-card {
+ background-color: #f3f3f3;
+ border: 0;
+ line-height: 4em;
+}
+
+
+.lightBlueBtn {
+ background-color: #3498db;
+ color: white;
+}
+
+::ng-deep .cal-event-title {
+
+}
diff --git a/frontend/src/app/schedules/draggable-scheduler/draggable-scheduler.component.html b/frontend/src/app/schedules/draggable-scheduler/draggable-scheduler.component.html
new file mode 100644
index 0000000..3e1412a
--- /dev/null
+++ b/frontend/src/app/schedules/draggable-scheduler/draggable-scheduler.component.html
@@ -0,0 +1,54 @@
+
+
diff --git a/frontend/src/app/schedules/draggable-scheduler/draggable-scheduler.component.spec.ts b/frontend/src/app/schedules/draggable-scheduler/draggable-scheduler.component.spec.ts
new file mode 100644
index 0000000..786eca5
--- /dev/null
+++ b/frontend/src/app/schedules/draggable-scheduler/draggable-scheduler.component.spec.ts
@@ -0,0 +1,21 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { DraggableSchedulerComponent } from './draggable-scheduler.component';
+
+describe('DraggableSchedulerComponent', () => {
+ let component: DraggableSchedulerComponent;
+ let fixture: ComponentFixture
;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ declarations: [DraggableSchedulerComponent]
+ });
+ fixture = TestBed.createComponent(DraggableSchedulerComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/frontend/src/app/schedules/draggable-scheduler/draggable-scheduler.component.ts b/frontend/src/app/schedules/draggable-scheduler/draggable-scheduler.component.ts
new file mode 100644
index 0000000..23e6ac5
--- /dev/null
+++ b/frontend/src/app/schedules/draggable-scheduler/draggable-scheduler.component.ts
@@ -0,0 +1,285 @@
+import {Component, ViewChild} from '@angular/core';
+import {NavigationLink, NavigationLinkListComponent} from "../../navigation-link-list/navigation-link-list.component";
+import {
+ AdvancedScheduleInfo,
+ BasicScheduleInfo,
+ ScheduleInfo,
+ ScheduleService,
+ TaskEntityInfo,
+ TaskgroupEntityInfo,
+ TaskOverviewInfo, TaskShortInfo
+} from "../../../api";
+import {Subject} from "rxjs";
+import {CalendarEvent, CalendarEventAction, CalendarEventTimesChangedEvent, CalendarView} from "angular-calendar";
+import {TaskOverviewData} from "../../dashboard/taskgroup-overview/taskgroup-overview.component";
+import {EventColor} from "calendar-utils";
+import * as moment from "moment";
+import {Router} from "@angular/router";
+
+const colors: Record = {
+ red: {
+ primary: '#ad2121',
+ secondary: '#FAE3E3',
+ },
+ blue: {
+ primary: '#1e90ff',
+ secondary: '#D1E8FF',
+ },
+ yellow: {
+ primary: '#e3bc08',
+ secondary: '#FDF1BA',
+ },
+};
+@Component({
+ selector: 'app-draggable-scheduler',
+ templateUrl: './draggable-scheduler.component.html',
+ styleUrls: ['./draggable-scheduler.component.css'],
+ styles: [
+ `
+ h3 {
+ margin: 0 0 10px;
+ }
+
+ pre {
+ background-color: #f5f5f5;
+ padding: 15px;
+ }
+ `,
+ ],
+})
+export class DraggableSchedulerComponent {
+ defaultNavigationLinkPath: NavigationLink[] = [
+ {
+ linkText: "Dashboard",
+ routerLink: ['/']
+ },
+ {
+ linkText: "Taskgroups",
+ routerLink: ["/taskgroups"]
+ }
+ ]
+ taskgroups: TaskgroupEntityInfo[] = []
+ taskgroup: TaskgroupEntityInfo | undefined
+ taskgroupPath: TaskgroupEntityInfo[] = []
+ taskgroupID: number = -1;
+ scheduleID: number = -1;
+ editedSchedule: ScheduleInfo | undefined
+ @ViewChild('navLinkList') navLinkListComponent: NavigationLinkListComponent | undefined
+
+ task: TaskEntityInfo | undefined
+ scheduleStrategy: number = 1
+
+
+ /**************************************************/
+ //Calendar-Stuff
+ /**************************************************/
+ view: CalendarView = CalendarView.Week;
+ viewDate = new Date();
+ daysInWeek = 7;
+ refresh: Subject = new Subject()
+ events: CalendarEvent[] = []
+
+ actions: CalendarEventAction[] = [
+ {
+ label: '',
+ a11yLabel: 'Edit',
+ onClick: ({ event }: { event: CalendarEvent }): void => {
+ this.eventClicked('Edit', event)
+ },
+ },
+ {
+ label: '',
+ a11yLabel: 'Delete',
+ onClick: ({ event }: { event: CalendarEvent }): void => {
+ this.eventClicked('Delete', event);
+ },
+ },
+ ]
+
+ tasks: CalendarEvent[] = []
+ selectedTaskgroupID: number | undefined
+
+ constructor(private scheduleService: ScheduleService,
+ private router: Router) {
+ }
+
+ ngOnInit() {
+ this.scheduleService.schedulesGet().subscribe({
+ next: resp => {
+ resp.forEach(schedule => {
+ if(schedule.scheduleType == 'BASIC') {
+ const basicSchedule = schedule as BasicScheduleInfo
+ this.events.push({
+ title: this.computeTaskPath(schedule.taskgroupPath, schedule.task),
+ color: colors['red'],
+ start: new Date(basicSchedule.scheduleDate),
+ actions: this.actions,
+ allDay: true,
+ draggable: true,
+ resizable: {
+ beforeStart: true,
+ afterEnd: true
+ },
+ meta: {
+ taskID: schedule.task.taskID,
+ scheduleID: schedule.scheduleID,
+ taskgroupID: schedule.taskgroupPath[0].taskgroupID
+ },
+
+ })
+ } else {
+ const advancedSchedule = schedule as AdvancedScheduleInfo
+ this.events.push({
+ title: this.computeTaskPath(schedule.taskgroupPath, schedule.task),
+ color: colors['red'],
+ cssClass: 'test',
+ start: new Date(advancedSchedule.scheduleStartTime),
+ end: new Date(advancedSchedule.scheduleStopTime),
+ actions: this.actions,
+ draggable: true,
+ resizable: {
+ beforeStart: true,
+ afterEnd: true
+ },
+ meta: {
+ taskID: schedule.task.taskID,
+ scheduleID: schedule.scheduleID,
+ taskgroupID: schedule.taskgroupPath[0].taskgroupID
+ },
+ })
+ }
+ });
+ console.log(this.events)
+ this.refresh.next();
+ }
+ })
+ }
+
+ onSelectTaskgroup(taskOverviewData: TaskOverviewData) {
+ this.tasks = [];
+ taskOverviewData.tasks.forEach(task => {
+ this.tasks.push({
+ title: task.taskName,
+ color: colors['yellow'],
+ start: new Date(),
+ draggable: true,
+ cssClass: 'test',
+ resizable: {
+ beforeStart: true,
+ afterEnd: true
+ },
+ meta: {
+ taskID: task.taskID,
+ scheduleID: undefined,
+ taskgroupID: taskOverviewData.taskgroupID
+ },
+ actions: this.actions
+ })
+ })
+ this.selectedTaskgroupID = taskOverviewData.taskgroupID;
+ }
+
+
+ externalDrop(event: CalendarEvent) {
+ if (this.tasks.indexOf(event) === -1) {
+ this.events = this.events.filter((iEvent) => iEvent !== event);
+ this.tasks.push(event);
+ }
+ }
+
+ eventDropped({
+ event,
+ newStart,
+ newEnd,
+ allDay,
+ }: CalendarEventTimesChangedEvent): void {
+ const externalIndex = this.tasks.indexOf(event);
+ if (typeof allDay !== 'undefined') {
+ event.allDay = allDay;
+ }
+ if (externalIndex > -1) {
+ this.tasks.splice(externalIndex, 1);
+ this.events.push(event);
+ }
+
+ event.start = newStart;
+ if (newEnd) {
+ event.end = newEnd;
+ }
+ this.events = [...this.events];
+
+ if(externalIndex > -1) {
+ //Create new schedule as a new event was dropped
+ if(event.allDay) {
+ this.scheduleService.schedulesTaskIDBasicPut(event.meta.taskID,{
+ scheduleDate: moment(event.start).format('YYYY-MM-DDTHH:mm:ss.SSSZ')
+ }).subscribe({
+ next: resp => {
+ event.meta.scheduleID = resp.scheduleID;
+ event.color = colors['red'];
+ event.title = this.computeTaskPath(resp.taskgroupPath, resp.task)
+ }
+ })
+ } else {
+ console.log("Start: " + event.start);
+ console.log("End: " + event.end);
+ if(event.end == undefined) {
+ event.end = moment(event.start).add(30, 'm').toDate()
+ }
+ this.scheduleService.schedulesTaskIDAdvancedPut(event.meta.taskID, {
+ scheduleStartTime: moment(event.start).format('YYYY-MM-DDTHH:mm:ss.SSSZ'),
+ scheduleStopTime: moment(event.end).format('YYYY-MM-DDTHH:mm:ss.SSSZ'),
+ }).subscribe({
+ next: resp => {
+ event.meta.scheduleID = resp.scheduleID;
+ event.color = colors['red'];
+ event.title = this.computeTaskPath(resp.taskgroupPath, resp.task)
+ }
+ })
+ }
+ } else {
+ if(event.allDay) {
+ this.scheduleService.schedulesScheduleIDBasicPost(event.meta.scheduleID,{
+ scheduleDate: moment(event.start).format('YYYY-MM-DDTHH:mm:ss.SSSZ')
+ }).subscribe({
+ next: resp => {
+ console.log("Updated")
+ }
+ })
+ } else {
+ this.scheduleService.schedulesScheduleIDAdvancedPost(event.meta.scheduleID, {
+ scheduleStartTime: moment(event.start).format('YYYY-MM-DDTHH:mm:ss.SSSZ'),
+ scheduleStopTime: moment(event.end).format('YYYY-MM-DDTHH:mm:ss.SSSZ'),
+ }).subscribe({
+ next: resp => {
+ console.log("Updated")
+ }
+ })
+ }
+ }
+ }
+
+ computeTaskPath(taskgroupPath: TaskgroupEntityInfo[], task: TaskShortInfo | TaskEntityInfo) {
+ let result = "";
+ taskgroupPath.forEach(taskgroupPathPart => {
+ result += taskgroupPathPart.taskgroupName + "/"
+ });
+ result += task!.taskName
+ return result;
+ }
+
+ eventClicked(action: string, event: CalendarEvent): void {
+ if(action == 'Click') {
+ this.router.navigateByUrl("/taskgroups/" + event.meta.taskgroupID + "/tasks/" + event.meta.taskID);
+ } else if(action == 'Edit') {
+ this.router.navigateByUrl("/taskgroups/" + event.meta.taskgroupID + "/tasks/" + event.meta.taskID + "/schedule/" + event.meta.scheduleID);
+ } else if(action == 'Delete') {
+ this.scheduleService.schedulesScheduleIDDelete(event.meta.scheduleID).subscribe({
+ next: resp => {
+ this.events = this.events.filter(calendarEvent => calendarEvent.meta.scheduleID !== event.meta.scheduleID);
+ }
+ })
+ }
+
+ }
+}
diff --git a/frontend/src/app/schedules/scheduler/scheduler.component.css b/frontend/src/app/schedules/scheduler/scheduler.component.css
index 8793269..abdc086 100644
--- a/frontend/src/app/schedules/scheduler/scheduler.component.css
+++ b/frontend/src/app/schedules/scheduler/scheduler.component.css
@@ -26,3 +26,6 @@
width: 100%;
}
+::ng-deep .cal-event-title {
+ white-space: normal;
+}
diff --git a/frontend/src/app/taskgroups/taskgroup-dashboard/taskgroup-dashboard.component.ts b/frontend/src/app/taskgroups/taskgroup-dashboard/taskgroup-dashboard.component.ts
index 5dafbf6..79835b5 100644
--- a/frontend/src/app/taskgroups/taskgroup-dashboard/taskgroup-dashboard.component.ts
+++ b/frontend/src/app/taskgroups/taskgroup-dashboard/taskgroup-dashboard.component.ts
@@ -40,13 +40,24 @@ export class TaskgroupDashboardComponent implements OnInit {
ngOnInit(): void {
this.activatedRoute.paramMap.subscribe(params => {
if(params.has('taskgroupID')) {
+ console.log("Update of taskgroup")
this.taskgroupID = Number(params.get('taskgroupID'));
this.taskgroupService.taskgroupsTaskgroupIDGet(this.taskgroupID).subscribe({
next: resp => {
this.taskgroups = resp.children
this.taskgroupPath = resp.ancestors
this.taskgroup = resp.taskgroupInfo;
- this.navLinkListComponent!.addNavigationLink(this.taskgroup.taskgroupName, ['/taskgroups', this.taskgroup.taskgroupID.toString()])
+ this.navLinkListComponent!.resetComponent([
+ {
+ linkText: "Dashboard",
+ routerLink: ['/']
+ },
+ {
+ linkText: "Taskgroups",
+ routerLink: ["/taskgroups"]
+ }
+ ]);
+ console.log(this.taskgroups)
this.taskgroupPath.forEach(taskgroupEntity => {
this.navLinkListComponent!.addNavigationLink(taskgroupEntity.taskgroupName, ['/taskgroups', taskgroupEntity.taskgroupID.toString()]);
})
diff --git a/frontend/src/app/tasks/task-detail-overview/task-detail-overview.component.ts b/frontend/src/app/tasks/task-detail-overview/task-detail-overview.component.ts
index 70061af..bacadd5 100644
--- a/frontend/src/app/tasks/task-detail-overview/task-detail-overview.component.ts
+++ b/frontend/src/app/tasks/task-detail-overview/task-detail-overview.component.ts
@@ -49,7 +49,16 @@ export class TaskDetailOverviewComponent implements OnInit {
this.taskgroups = resp.children
this.taskgroupPath = resp.ancestors
this.taskgroup = resp.taskgroupInfo;
- this.navLinkListComponent!.addNavigationLink(this.taskgroup.taskgroupName, ['/taskgroups', this.taskgroup.taskgroupID.toString()])
+ this.navLinkListComponent!.resetComponent([
+ {
+ linkText: "Dashboard",
+ routerLink: ['/']
+ },
+ {
+ linkText: "Taskgroups",
+ routerLink: ["/taskgroups"]
+ }
+ ]);
this.taskgroupPath.forEach(taskgroupEntity => {
this.navLinkListComponent!.addNavigationLink(taskgroupEntity.taskgroupName, ['/taskgroups', taskgroupEntity.taskgroupID.toString()]);
})
diff --git a/frontend/src/app/tasks/task-editor/task-editor.component.ts b/frontend/src/app/tasks/task-editor/task-editor.component.ts
index 7752773..ab05b42 100644
--- a/frontend/src/app/tasks/task-editor/task-editor.component.ts
+++ b/frontend/src/app/tasks/task-editor/task-editor.component.ts
@@ -7,6 +7,7 @@ import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
import {TaskService} from "../../../api";
import {TaskEditorData} from "./TaskEditorData";
import {MatSnackBar} from "@angular/material/snack-bar";
+import * as moment from "moment/moment";
@Component({
@@ -53,8 +54,8 @@ export class TaskEditorComponent implements OnInit {
this.taskService.tasksTaskgroupIDPut(this.editorData.taskgroupID, {
taskName: this.nameCtrl.value,
eta: this.etaCtrl.value,
- startDate: this.startDate.value,
- deadline: this.endDate.value
+ startDate: moment(this.startDate.value).format('YYYY-MM-DDTHH:mm:ss.SSSZ'),
+ deadline: moment(this.endDate.value).format('YYYY-MM-DDTHH:mm:ss.SSSZ')
}).subscribe({
next: resp => {
this.dialog.close(resp);
diff --git a/frontend/src/index.html b/frontend/src/index.html
index 018656c..fecc517 100644
--- a/frontend/src/index.html
+++ b/frontend/src/index.html
@@ -11,6 +11,7 @@
+
diff --git a/openapi.yaml b/openapi.yaml
index a1a74d3..6cc075a 100644
--- a/openapi.yaml
+++ b/openapi.yaml
@@ -2421,6 +2421,7 @@ components:
- eta
- limit
- overdue
+ - taskgroupPath
additionalProperties: false
properties:
taskID:
@@ -2448,6 +2449,10 @@ components:
type: number
description: number in minutes that was already worked on this task
example: 10
+ taskgroupPath:
+ type: array
+ items:
+ $ref: '#/components/schemas/TaskgroupEntityInfo'
ScheduleStatus:
required:
- activeMinutes