Implement drag&drop in calendar

pull/10060/head
Oliver Günther 3 years ago
parent c2da2d7ca1
commit 6170755ac4
No known key found for this signature in database
GPG Key ID: A3A8BDAD7C0C552C
  1. 16
      frontend/src/app/features/calendar/op-calendar.service.ts
  2. 28
      frontend/src/app/features/calendar/wp-calendar/wp-calendar.component.ts
  3. 11
      frontend/src/app/features/team-planner/team-planner/planner/team-planner.component.ts

@ -233,6 +233,22 @@ export class OpCalendarService extends UntilDestroyedMixin {
return this.urlParams.cview as string|undefined;
}
public eventDurationEditable(wp:WorkPackageResource):boolean {
const schema = this.schemaCache.of(wp);
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
const schemaEditable = !!schema.startDate.writable && !!schema.dueDate.writable && schema.isAttributeEditable('startDate');
return (wp.isLeaf || wp.scheduleManually) && schemaEditable && !this.isMilestone(wp);
}
/**
* The end date from fullcalendar is open, which means it targets
* the next day instead of current day 23:59:59.
* @param end
*/
public getEndDateFromTimestamp(end:Date):string {
return moment(end).subtract(1, 'd').format('YYYY-MM-DD');
}
private defaultOptions():CalendarOptions {
return {
editable: false,

@ -24,6 +24,7 @@ import { OpTitleService } from 'core-app/core/html/op-title.service';
import dayGridPlugin from '@fullcalendar/daygrid';
import {
CalendarOptions,
EventDropArg,
EventInput,
} from '@fullcalendar/core';
import { debounceTime } from 'rxjs/operators';
@ -33,6 +34,9 @@ import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service';
import { OpCalendarService } from 'core-app/features/calendar/op-calendar.service';
import { Subject } from 'rxjs';
import { CurrentProjectService } from 'core-app/core/current-project/current-project.service';
import interactionPlugin, { EventResizeDoneArg } from '@fullcalendar/interaction';
import { HalResourceEditingService } from 'core-app/shared/components/fields/edit/services/hal-resource-editing.service';
import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service';
@Component({
templateUrl: './wp-calendar.template.html',
@ -71,6 +75,8 @@ export class WorkPackagesCalendarComponent extends UntilDestroyedMixin implement
private configuration:ConfigurationService,
readonly calendar:OpCalendarService,
readonly currentProject:CurrentProjectService,
readonly halEditing:HalResourceEditingService,
readonly halNotification:HalResourceNotificationService,
) {
super();
}
@ -123,7 +129,14 @@ export class WorkPackagesCalendarComponent extends UntilDestroyedMixin implement
headerToolbar: this.buildHeader(),
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
events: this.calendarEventsFunction.bind(this),
plugins: [dayGridPlugin],
plugins: [
dayGridPlugin,
interactionPlugin,
],
// DnD configuration
editable: true,
eventResize: (resizeInfo:EventResizeDoneArg) => this.updateEvent(resizeInfo),
eventDrop: (dropInfo:EventDropArg) => this.updateEvent(dropInfo),
};
if (this.static) {
@ -171,6 +184,7 @@ export class WorkPackagesCalendarComponent extends UntilDestroyedMixin implement
return {
title: workPackage.subject,
start: startDate,
editable: this.calendar.eventDurationEditable(workPackage),
end: exclusiveEnd,
allDay: true,
className: `__hl_background_type_${workPackage.type.id}`,
@ -184,4 +198,16 @@ export class WorkPackagesCalendarComponent extends UntilDestroyedMixin implement
private get initialView():string|undefined {
return this.static ? 'dayGridWeek' : undefined;
}
private async updateEvent(info:EventResizeDoneArg|EventDropArg):Promise<void> {
const changeset = this.calendar.updateDates(info);
try {
const result = await this.halEditing.save(changeset);
this.halNotification.showSave(result.resource, result.wasNew);
} catch (e) {
this.halNotification.showError(e, changeset.projectedResource);
info.revert();
}
}
}

@ -363,7 +363,7 @@ export class TeamPlannerComponent extends UntilDestroyedMixin implements OnInit,
}
const assignee = this.wpAssignee(workPackage);
const durationEditable = this.eventDurationEditable(workPackage);
const durationEditable = this.calendar.eventDurationEditable(workPackage);
const resourceEditable = this.eventResourceEditable(workPackage);
return {
@ -442,13 +442,6 @@ export class TeamPlannerComponent extends UntilDestroyedMixin implements OnInit,
return !!schema.assignee?.writable && schema.isAttributeEditable('assignee');
}
private eventDurationEditable(wp:WorkPackageResource):boolean {
const schema = this.schemaCache.of(wp);
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
const schemaEditable = !!schema.startDate.writable && !!schema.dueDate.writable && schema.isAttributeEditable('startDate');
return (wp.isLeaf || wp.scheduleManually) && schemaEditable && !this.calendar.isMilestone(wp);
}
// Todo: Evaluate whether we really want to use that from a UI perspective ¯\_(ツ)_/¯
// When users have the right to change the assignee but cannot change the date (due to hierarchy for example),
// they are forced to drag the wp to the exact same date in the others assignee row. This might be confusing.
@ -457,7 +450,7 @@ export class TeamPlannerComponent extends UntilDestroyedMixin implements OnInit,
private eventConstaints(wp:WorkPackageResource):{ [key:string]:string|string[] } {
const constraints:{ [key:string]:string|string[] } = {};
if (!this.eventDurationEditable(wp)) {
if (!this.calendar.eventDurationEditable(wp)) {
constraints.start = this.wpStartDate(wp);
constraints.end = this.wpEndDate(wp);
}

Loading…
Cancel
Save