Update to 5.10

pull/9922/head
Oliver Günther 3 years ago
parent 81957d2fa3
commit d90935cdc4
No known key found for this signature in database
GPG Key ID: A3A8BDAD7C0C552C
  1. 168
      frontend/package-lock.json
  2. 12
      frontend/package.json
  3. 131
      frontend/src/app/features/team-planner/team-planner/planner/event-view-lookup.service.ts
  4. 24
      frontend/src/app/features/team-planner/team-planner/planner/team-planner.component.html
  5. 287
      frontend/src/app/features/team-planner/team-planner/planner/team-planner.component.ts

@ -2842,87 +2842,86 @@
}
},
"@fullcalendar/angular": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/@fullcalendar/angular/-/angular-5.5.0.tgz",
"integrity": "sha512-e1KZTzMjB4/rcCMKmQl7DzQN4cmBOk34KZ2kdrX7cAq6B4yOUUt3A0PMrohU4JKdlLw/XsQL1O4TrPyOoWfMjw==",
"version": "5.10.1",
"resolved": "https://registry.npmjs.org/@fullcalendar/angular/-/angular-5.10.1.tgz",
"integrity": "sha512-aV2MejZMTBGzlgQ+HN2kyWThYrbf8nusr88bB6HLHixkzjWnfdxzC8NwMYz8JOatJzH9UmOiaPQRWdTU9S8WEg==",
"requires": {
"@fullcalendar/core": "~5.5.0",
"@fullcalendar/core": "~5.10.1",
"fast-deep-equal": "^3.1.1",
"tslib": "^2.0.0"
}
},
"@fullcalendar/common": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/@fullcalendar/common/-/common-5.5.0.tgz",
"integrity": "sha512-xHsHRI2xnC4vDPdRaOkt03SGIEsmlcSTI6RVInI2PQKi3n4otgM4ej3WL4B010uT4IzrBlUvNm8HHEo/o3g8Ug==",
"version": "5.10.1",
"resolved": "https://registry.npmjs.org/@fullcalendar/common/-/common-5.10.1.tgz",
"integrity": "sha512-EumKIJcQTvQdTs75/9dmeREFgjcRVWzqHJS1Xvlz5mNsmB+w9EINCHETRjChtAQg1WD/lTQyVj4sHsKO7vCMSw==",
"requires": {
"ical.js": "^1.4.0",
"tslib": "^2.0.3"
"tslib": "^2.1.0"
},
"dependencies": {
"tslib": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz",
"integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A=="
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
}
}
},
"@fullcalendar/core": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/@fullcalendar/core/-/core-5.5.0.tgz",
"integrity": "sha512-NHyrH6AP1zRRCWsK0NuY/bZFQes1UaGB2pukMv86A0Yq1BI7Y7m9u/yH7D0/ZCaNkCLVxrSlIwAOtMQxPPITdg==",
"version": "5.10.1",
"resolved": "https://registry.npmjs.org/@fullcalendar/core/-/core-5.10.1.tgz",
"integrity": "sha512-8sVuC6ywXV+cxqsqTZaR1hgUqeyjVed20NyZ7lGW9AY0kma1GIEwLgqPS5Q6uVhHyin68lmgecKfJCwhxENE8w==",
"requires": {
"@fullcalendar/common": "~5.5.0",
"@fullcalendar/common": "~5.10.1",
"preact": "^10.0.5",
"tslib": "^2.0.3"
"tslib": "^2.1.0"
},
"dependencies": {
"tslib": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz",
"integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A=="
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
}
}
},
"@fullcalendar/daygrid": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/@fullcalendar/daygrid/-/daygrid-5.5.0.tgz",
"integrity": "sha512-O/59C3ihElLsPnkjlD1xUgAf3v7WEARXVO+wZfASKtK14Ccux2MA9mWUA1+80m9q8on7SYL36ORhrea7GKy/qQ==",
"version": "5.10.1",
"resolved": "https://registry.npmjs.org/@fullcalendar/daygrid/-/daygrid-5.10.1.tgz",
"integrity": "sha512-sfUMP+rew0krsBffgNcWWKhBCiyytGfRKZJoc64E8ohX7VWjPcPZuB1xgO5U4wPLmNkT0rZiHoGeQGTXw1+ZKg==",
"requires": {
"@fullcalendar/common": "~5.5.0",
"tslib": "^2.0.3"
"@fullcalendar/common": "~5.10.1",
"tslib": "^2.1.0"
},
"dependencies": {
"tslib": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz",
"integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A=="
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
}
}
},
"@fullcalendar/interaction": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/@fullcalendar/interaction/-/interaction-5.5.0.tgz",
"integrity": "sha512-LKc4VeESBkBkrI/sAtV++f4qUleYyt7STBrnuc5Mpojv7jg5x5XZ1rfOarLwRbEYSvbo6CwccDrsaLTbC1Qk2A==",
"version": "5.10.1",
"resolved": "https://registry.npmjs.org/@fullcalendar/interaction/-/interaction-5.10.1.tgz",
"integrity": "sha512-H1g1QeXg7yXtUcKmVtfg7uzm5R5ElFTvYniiXU+8kJda69IDg7Lee+Y7UDv5dvLb5/HxO86RhPVxRtcOQ8XdXw==",
"requires": {
"@fullcalendar/common": "~5.5.0",
"tslib": "^2.0.3"
"@fullcalendar/common": "~5.10.1",
"tslib": "^2.1.0"
},
"dependencies": {
"tslib": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz",
"integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A=="
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
}
}
},
"@fullcalendar/premium-common": {
"version": "5.5.1",
"resolved": "https://registry.npmjs.org/@fullcalendar/premium-common/-/premium-common-5.5.1.tgz",
"integrity": "sha512-xFf4MafsRN7XCohiJzBo02Qk2n1wyO66RjGoo+GOPIADq+4+TQbloQGltBq05tkjTsOZXnE6i2LIWOw/y8pShQ==",
"version": "5.10.1",
"resolved": "https://registry.npmjs.org/@fullcalendar/premium-common/-/premium-common-5.10.1.tgz",
"integrity": "sha512-yOZgtVI/7tt1oeQABLQqTNLEv2YpxbE0ywvQ+VwtrKc5LksmUA2czO4peom1KIAwH6JsZnytpvwQ4k4cP8Ivaw==",
"requires": {
"@fullcalendar/common": "~5.5.0",
"tslib": "^2.0.3"
"@fullcalendar/common": "~5.10.1",
"tslib": "^2.1.0"
},
"dependencies": {
"tslib": {
@ -2933,13 +2932,13 @@
}
},
"@fullcalendar/resource-common": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/@fullcalendar/resource-common/-/resource-common-5.5.0.tgz",
"integrity": "sha512-Bp1oqL30p9UGJ28ippsXtaHrBxbDXdcM5z/ZLEnmjbDB2wAHhy4oMXHZITbDvv1NlCqw0HUKjNz/gjWIhhSFiA==",
"version": "5.10.1",
"resolved": "https://registry.npmjs.org/@fullcalendar/resource-common/-/resource-common-5.10.1.tgz",
"integrity": "sha512-20JR8cucAeJEXSbWVSj9USwsPGKb3dVQr8CBiXuHPbD0OLK93j7jhKjBlp/pRldtcJW9mIXC8ENQvw/aNZJ9Cw==",
"requires": {
"@fullcalendar/common": "~5.5.0",
"@fullcalendar/premium-common": "~5.5.0",
"tslib": "^2.0.3"
"@fullcalendar/common": "~5.10.1",
"@fullcalendar/premium-common": "~5.10.1",
"tslib": "^2.1.0"
},
"dependencies": {
"tslib": {
@ -2950,16 +2949,16 @@
}
},
"@fullcalendar/resource-timeline": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/@fullcalendar/resource-timeline/-/resource-timeline-5.5.0.tgz",
"integrity": "sha512-HLjs9RbidX9p3XStOuOz/8x/jPBJ4/9B2SUSU7KlhjEWnwvaxAgfeNApXSFy1c2IUh40t3rxf31Z3z+HcbEyZw==",
"requires": {
"@fullcalendar/common": "~5.5.0",
"@fullcalendar/premium-common": "~5.5.0",
"@fullcalendar/resource-common": "~5.5.0",
"@fullcalendar/scrollgrid": "~5.5.0",
"@fullcalendar/timeline": "~5.5.0",
"tslib": "^2.0.3"
"version": "5.10.1",
"resolved": "https://registry.npmjs.org/@fullcalendar/resource-timeline/-/resource-timeline-5.10.1.tgz",
"integrity": "sha512-gsqjr6Z+LQcNbQlHgaTtg/kF8l6yDRtIuMQbhFlTy71RJI//x2mHhLXgV40FJeEE+srp48xJPd89+rIhlyJ5Tw==",
"requires": {
"@fullcalendar/common": "~5.10.1",
"@fullcalendar/premium-common": "~5.10.1",
"@fullcalendar/resource-common": "~5.10.1",
"@fullcalendar/scrollgrid": "~5.10.1",
"@fullcalendar/timeline": "~5.10.1",
"tslib": "^2.1.0"
},
"dependencies": {
"tslib": {
@ -2970,13 +2969,13 @@
}
},
"@fullcalendar/scrollgrid": {
"version": "5.5.1",
"resolved": "https://registry.npmjs.org/@fullcalendar/scrollgrid/-/scrollgrid-5.5.1.tgz",
"integrity": "sha512-oPmsIlyanVGSIoWoGcOYLvFZi/GVTTN4SOqS9PpiOXZPP+4UDP8PXTe4avA+doKC/DgHVpb7lBcpTFGSyRAISw==",
"version": "5.10.1",
"resolved": "https://registry.npmjs.org/@fullcalendar/scrollgrid/-/scrollgrid-5.10.1.tgz",
"integrity": "sha512-Hj6gzj2/sUUnozIMC0GBK60/ZTeDchp/Gc2S4F+05W6V4BoUXbIwav+EdAerfNFOv7EXWa9vM9Lq3A53iVWFVg==",
"requires": {
"@fullcalendar/common": "~5.5.0",
"@fullcalendar/premium-common": "~5.5.0",
"tslib": "^2.0.3"
"@fullcalendar/common": "~5.10.1",
"@fullcalendar/premium-common": "~5.10.1",
"tslib": "^2.1.0"
},
"dependencies": {
"tslib": {
@ -2987,31 +2986,31 @@
}
},
"@fullcalendar/timegrid": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/@fullcalendar/timegrid/-/timegrid-5.5.0.tgz",
"integrity": "sha512-Y4k3/ux031pL7WQWJnvJbGcOua+zg1BGn08xeycgBICNqVMIhKm++uCnLzCvHGDV4Gp7oRjNAw/nJw4M1kMoXg==",
"version": "5.10.1",
"resolved": "https://registry.npmjs.org/@fullcalendar/timegrid/-/timegrid-5.10.1.tgz",
"integrity": "sha512-0O0m+JzFBlg8gxYr/rIjZViRlbndCtjZlDjjIylQHFBeWC32e3cpHEavKGbTIBLN8SDilUYAJnE21abSqC2G/w==",
"requires": {
"@fullcalendar/common": "~5.5.0",
"@fullcalendar/daygrid": "~5.5.0",
"tslib": "^2.0.3"
"@fullcalendar/common": "~5.10.1",
"@fullcalendar/daygrid": "~5.10.1",
"tslib": "^2.1.0"
},
"dependencies": {
"tslib": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz",
"integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A=="
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
}
}
},
"@fullcalendar/timeline": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/@fullcalendar/timeline/-/timeline-5.5.0.tgz",
"integrity": "sha512-W7Bj7X/+tKK62oT/KrSrcelpYFVt3MgEDnt2ju9BZzkjk5LX+Xa8jiH+PLFtUZasF9pJ50i86KICC9WnFZzu3g==",
"version": "5.10.1",
"resolved": "https://registry.npmjs.org/@fullcalendar/timeline/-/timeline-5.10.1.tgz",
"integrity": "sha512-pFMhK4nsCvpsA63GPJQtT1RSS2OLlT9a2+fvsf2oQregBLcottJSlCjIsIuKP7hpQLimaSdLr2kNjh5hs8jKlw==",
"requires": {
"@fullcalendar/common": "~5.5.0",
"@fullcalendar/premium-common": "~5.5.0",
"@fullcalendar/scrollgrid": "~5.5.0",
"tslib": "^2.0.3"
"@fullcalendar/common": "~5.10.1",
"@fullcalendar/premium-common": "~5.10.1",
"@fullcalendar/scrollgrid": "~5.10.1",
"tslib": "^2.1.0"
},
"dependencies": {
"tslib": {
@ -9472,11 +9471,6 @@
"ms": "^2.0.0"
}
},
"ical.js": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/ical.js/-/ical.js-1.4.0.tgz",
"integrity": "sha512-ltHZuOFNNjcyEYbzDgjemS7LWIFh2vydJeznxQHUh3dnarbxqOYsWONYteBVAq1MEOHnwXFGN2eskZReHclnrA=="
},
"iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@ -14123,9 +14117,9 @@
}
},
"preact": {
"version": "10.5.9",
"resolved": "https://registry.npmjs.org/preact/-/preact-10.5.9.tgz",
"integrity": "sha512-X4m+4VMVINl/JFQKALOCwa3p8vhMAhBvle0hJ/W44w/WWfNb2TA7RNicDV3K2dNVs57f61GviEnVLiwN+fxiIg=="
"version": "10.6.1",
"resolved": "https://registry.npmjs.org/preact/-/preact-10.6.1.tgz",
"integrity": "sha512-ydCg+ISIq70vqiThvNWStZWLRjR9U2awP/JAmGdWUKm9+Tyuy+MqVdAIyEByeIspAVtD4GWC/SJtxO8XD4knVA=="
},
"prelude-ls": {
"version": "1.2.1",

@ -72,12 +72,12 @@
"@angular/platform-browser-dynamic": "~12.2.6",
"@angular/router": "~12.2.6",
"@datorama/akita": "^6.2.0",
"@fullcalendar/angular": "5.5.0",
"@fullcalendar/core": "5.5.0",
"@fullcalendar/daygrid": "5.5.0",
"@fullcalendar/interaction": "5.5.0",
"@fullcalendar/resource-timeline": "5.5.0",
"@fullcalendar/timegrid": "5.5.0",
"@fullcalendar/angular": "5.10.1",
"@fullcalendar/core": "5.10.1",
"@fullcalendar/daygrid": "5.10.1",
"@fullcalendar/interaction": "5.10.1",
"@fullcalendar/resource-timeline": "5.10.1",
"@fullcalendar/timegrid": "5.10.1",
"@kolkov/ngx-gallery": "^1.0.11",
"@ng-select/ng-option-highlight": "0.0.5",
"@ng-select/ng-select": "^4.0.4",

@ -0,0 +1,131 @@
import { EmbeddedViewRef, Injectable, OnDestroy, TemplateRef, ViewContainerRef } from "@angular/core";
/**
* View lookup service for injecting angular templates
* as fullcalendar event content.
*
* Based on the suggestion from Daniel Goldsmith
* in https://github.com/fullcalendar/fullcalendar-angular/issues/204
*
*/
@Injectable()
export class EventViewLookupService implements OnDestroy {
private readonly views = new Map<string, EmbeddedViewRef<any>>();
constructor(private viewContainerRef:ViewContainerRef) {
}
/**
* Gets the view for the given ID, or creates one if there isn't one
* already. The template's context is set (or updated to, if the
* view has already been created) the given context values.
* @param template The template ref (get this from a @ViewChild of an
* <ng-template>)
* @param id The unique ID for this instance of the view. Use this so that
* you don't keep around views for the same event.
* @param context The available variables for the <ng-template>. For
* example, if it looks like this: <ng-template let-localVar="value"> then
* your context should be an object with a `value` key.
* @param comparator If you're re-rendering the same view and the context
* hasn't changed, then performance is a lot better if we just return the
* original view rather than destroying and re-creating the view.
* Optionally pass this function to return true when the views should be
* re-used.
*/
getView(
template:TemplateRef<any>, id:string, context:any,
comparator?:(v1:any, v2:any) => boolean
):EmbeddedViewRef<any> {
let view = this.views.get(id);
if (view) {
if (comparator && comparator(view.context, context)) {
// Nothing changed -- no need to re-render the component.
view.markForCheck();
return view;
} else {
// The performance would be better if we didn't need to destroy
// the view here... but just updating the context and checking
// changes doesn't work.
this.destroyView(id);
}
}
view = this.viewContainerRef.createEmbeddedView(template, context);
this.views.set(id, view);
view.detectChanges();
return view;
}
/**
* Generates a view for the given template and returns the root DOM node(s)
* for the view, which can be returned from an eventContent call.
* @param template The template ref (get this from a @ViewChild of an
* <ng-template>)
* @param id The unique ID for this instance of the view. Use this so that
* you don't keep around views for the same event.
* @param context The available variables for the <ng-template>. For
* example, if it looks like this: <ng-template let-localVar="value"> then
* your context should be an object with a `value` key.
* @param comparator If you're re-rendering the same view and the context
* hasn't changed, then performance is a lot better if we just return the
* original view rather than destroying and re-creating the view.
* Optionally pass this function to return true when the views should be
* re-used.
*/
getTemplateRootNodes(
template:TemplateRef<any>,
id:string,
context:any,
comparator?:(v1:any, v2:any) => boolean
) {
return this.getView(template, id, context, comparator).rootNodes;
}
hasView(id:string) {
return this.views.has(id);
}
/**
* Marks the given view (or all views) as needing change detection.
* Call `detectChanges` on your component if you need to run change
* detection synchronously; normally Angular handles that.
*/
markForCheck(id?:string) {
if (id) {
this.views.get(id)?.markForCheck();
} else {
for (const view of this.views.values()) {
view.markForCheck();
}
}
}
ngOnDestroy():void {
this.destroyAll();
}
/**
* Call this method if all views need to be cleaned up. This will happen
* when your parent component is destroyed (e.g., in ngOnDestroy),
* but it may also be needed if you are clearing just the area where the
* views have been placed.
*/
public destroyAll():void {
for (const view of this.views.values()) {
view.destroy();
}
this.views.clear();
}
public destroyView(id:string):void {
const view = this.views.get(id);
if (view) {
const index = this.viewContainerRef.indexOf(view);
if (index !== -1) {
this.viewContainerRef.remove(index);
}
view.destroy();
this.views.delete(id);
}
}
}

@ -1,5 +1,19 @@
<full-calendar
#ucCalendar
*ngIf="calendarOptions"
[options]="calendarOptions"
></full-calendar>
<!-- position: relative added in order for the loading indicator to be positioned correctly -->
<div class="wp-calendar--container loading-indicator--location"
[attr.data-indicator-name]="'table'"
style="position: relative">
<ng-container
*ngIf="(calendarOptions$ | async) as calendarOptions"
>
<full-calendar
#ucCalendar
*ngIf="calendarOptions"
[options]="calendarOptions"
></full-calendar>
</ng-container>
<ng-template #resourceContent let-resource="resource">
<op-principal
[principal]="resource.extendedProps.user"
></op-principal>
</ng-template>
</div>

@ -1,73 +1,266 @@
import {
ChangeDetectionStrategy,
Component,
ElementRef,
SecurityContext,
TemplateRef,
ViewChild,
} from '@angular/core';
import { EventInput } from '@fullcalendar/core';
import {
CalendarOptions,
EventInput,
} from '@fullcalendar/core';
import resourceTimelinePlugin from '@fullcalendar/resource-timeline';
import { I18nService } from 'core-app/core/i18n/i18n.service';
import { ConfigurationService } from 'core-app/core/config/configuration.service';
console.log(resourceTimelinePlugin);
import { FullCalendarComponent } from '@fullcalendar/angular';
import { EventViewLookupService } from 'core-app/features/team-planner/team-planner/planner/event-view-lookup.service';
import { States } from 'core-app/core/states/states.service';
import { StateService } from '@uirouter/angular';
import { DomSanitizer } from '@angular/platform-browser';
import { WorkPackageViewFiltersService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service';
import { WorkPackagesListService } from 'core-app/features/work-packages/components/wp-list/wp-list.service';
import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space';
import { WorkPackagesListChecksumService } from 'core-app/features/work-packages/components/wp-list/wp-list-checksum.service';
import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service';
import { CurrentProjectService } from 'core-app/core/current-project/current-project.service';
import { OpTitleService } from 'core-app/core/html/op-title.service';
import { Subject } from 'rxjs';
import { take } from 'rxjs/internal/operators/take';
import { WorkPackageCollectionResource } from 'core-app/features/hal/resources/wp-collection-resource';
import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource';
import { HalResource } from 'core-app/features/hal/resources/hal-resource';
import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin';
@Component({
selector: 'op-team-planner',
templateUrl: './team-planner.component.html',
styleUrls: ['./team-planner.component.sass'],
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [
EventViewLookupService,
],
})
export class TeamPlannerComponent {
calendarOptions = {
schedulerLicenseKey: 'GPL-My-Project-Is-Open-Source',
editable: false,
locale: this.I18n.locale,
fixedWeekCount: false,
firstDay: this.configuration.startOfWeek(),
events: this.calendarEventsFunction.bind(this) as unknown,
// toolbar: this.buildHeader(),
plugins: [
resourceTimelinePlugin,
],
initialView: 'resourceTimelineWeekDaysOnly',
height: 500,
views: {
resourceTimelineWeekDaysOnly: {
type: 'resourceTimeline',
duration: { weeks: 1 },
slotDuration: { days: 1 },
},
},
resources: [
{
id: '1',
title: 'User 1',
},
],
};
export class TeamPlannerComponent extends UntilDestroyedMixin {
@ViewChild(FullCalendarComponent) ucCalendar:FullCalendarComponent;
@ViewChild('resourceContent') resourceContent:TemplateRef<unknown>;
calendarOptions$ = new Subject<CalendarOptions>();
projectIdentifier:string|null = null;
constructor(
readonly I18n:I18nService,
readonly configuration:ConfigurationService,
) {}
private elementRef:ElementRef,
private states:States,
private $state:StateService,
private sanitizer:DomSanitizer,
private configuration:ConfigurationService,
private wpTableFilters:WorkPackageViewFiltersService,
private wpListService:WorkPackagesListService,
private querySpace:IsolatedQuerySpace,
private wpListChecksumService:WorkPackagesListChecksumService,
private schemaCache:SchemaCacheService,
private currentProject:CurrentProjectService,
private titleService:OpTitleService,
private viewLookup:EventViewLookupService,
private I18n:I18nService,
) {
super();
}
ngOnInit() {
this.setupWorkPackagesListener();
this.initializeCalendar();
this.projectIdentifier = this.currentProject.identifier;
}
public calendarResourcesFunction(
fetchInfo:{ start:Date, end:Date, timeZone:string },
successCallback:(events:EventInput[]) => void,
failureCallback:(error:unknown) => void,
):void|PromiseLike<EventInput[]> {
this.querySpace.results.values$().pipe(
take(1),
).subscribe((collection:WorkPackageCollectionResource) => {
const resources = this.mapToCalendarResources(collection.elements);
successCallback(resources);
});
}
public calendarEventsFunction(
fetchInfo:{ start:Date, end:Date, timeZone:string },
successCallback:(events:EventInput[]) => void,
failureCallback:(error:any) => void,
failureCallback:(error:unknown) => void,
):void|PromiseLike<EventInput[]> {
successCallback([{
title: 'Important todo',
start: '2021-11-10',
end: '2021-11-21',
resourceId: '1',
allDay: true,
}]);
this.querySpace.results.values$().pipe(
take(1),
).subscribe((collection:WorkPackageCollectionResource) => {
const events = this.mapToCalendarEvents(collection.elements);
successCallback(events);
});
this.updateTimeframe(fetchInfo);
}
private initializeCalendar() {
this.configuration.initialized
.then(() => {
this.calendarOptions$.next({
schedulerLicenseKey: 'GPL-My-Project-Is-Open-Source',
editable: false,
locale: this.I18n.locale,
fixedWeekCount: false,
firstDay: this.configuration.startOfWeek(),
// toolbar: this.buildHeader(),
plugins: [
resourceTimelinePlugin,
],
initialView: 'resourceTimelineWeekDaysOnly',
height: 500,
views: {
resourceTimelineWeekDaysOnly: {
type: 'resourceTimeline',
duration: { weeks: 1 },
slotDuration: { days: 1 },
},
},
events: this.calendarEventsFunction.bind(this) as any,
resources: this.calendarResourcesFunction.bind(this),
resourceLabelContent: (data:any) => this.renderTemplate(this.resourceContent, data.resource.id, data),
resourceLabelWillUnmount: (data:any) => this.unrenderTemplate(data),
});
});
}
renderTemplate(template:TemplateRef<any>, id:string, data:any):{ domNodes:unknown[] } {
const ref = this.viewLookup.getView(template, id, data);
return { domNodes: ref.rootNodes };
}
unrenderTemplate(arg:any):void {
this.viewLookup.destroyView(arg.event.id);
}
public buildHeader():{ right:string, center:string, left:string } {
return {
right: 'dayGridWeek',
center: 'title',
left: 'prev,next today',
public updateTimeframe(fetchInfo:{ start:Date, end:Date, timeZone:string }) {
const filtersEmpty = this.wpTableFilters.isEmpty;
if (filtersEmpty && this.querySpace.query.value) {
// nothing to do
return;
}
const startDate = moment(fetchInfo.start).format('YYYY-MM-DD');
const endDate = moment(fetchInfo.end).format('YYYY-MM-DD');
if (filtersEmpty) {
let queryProps = this.defaultQueryProps(startDate, endDate);
if (this.$state.params.query_props) {
queryProps = decodeURIComponent(this.$state.params.query_props || '');
}
this.wpListService.fromQueryParams({ query_props: queryProps }, this.projectIdentifier || undefined).toPromise();
} else {
const { params } = this.$state;
this.wpTableFilters.modify('datesInterval', (datesIntervalFilter) => {
datesIntervalFilter.values[0] = startDate;
datesIntervalFilter.values[1] = endDate;
});
}
}
private setupWorkPackagesListener() {
this.querySpace.results.values$().pipe(
this.untilDestroyed(),
).subscribe((collection:WorkPackageCollectionResource) => {
this.ucCalendar.getApi().refetchEvents();
});
}
private mapToCalendarEvents(workPackages:WorkPackageResource[]):EventInput[] {
return workPackages
.map((workPackage:WorkPackageResource) => {
if (!workPackage.assignee) {
return;
}
const startDate = this.eventDate(workPackage, 'start');
const endDate = this.eventDate(workPackage, 'due');
const exclusiveEnd = moment(endDate).add(1, 'days').format('YYYY-MM-DD');
return {
id: workPackage.href + (workPackage.assignee?.href || 'no-assignee'),
resourceId: workPackage.assignee?.href,
title: workPackage.subject,
start: startDate,
end: exclusiveEnd,
allDay: true,
className: `__hl_background_type_${workPackage.type.id}`,
workPackage,
};
})
.filter((event) => !!event) as EventInput[];
}
private mapToCalendarResources(workPackages:WorkPackageResource[]) {
const resources:{ id:string, title:string, user:HalResource }[] = [];
workPackages.forEach((workPackage:WorkPackageResource) => {
if (!workPackage.assignee) {
return;
}
resources.push({
id: workPackage.assignee.href,
title: workPackage.assignee.name,
user: workPackage.assignee,
});
});
return resources;
}
private defaultQueryProps(startDate:string, endDate:string) {
const props = {
c: ['id'],
t:
'id:asc',
f: [{ n: 'status', o: 'o', v: [] },
{ n: 'datesInterval', o: '<>d', v: [startDate, endDate] }],
pp: 100,
};
return JSON.stringify(props);
}
private eventDate(workPackage:WorkPackageResource, type:'start'|'due') {
if (this.schemaCache.of(workPackage).isMilestone) {
return workPackage.date;
}
return workPackage[`${type}Date`];
}
private calendarHeight():number {
let heightElement = jQuery(this.elementRef.nativeElement);
while (!heightElement.height() && heightElement.parent()) {
heightElement = heightElement.parent();
}
const topOfCalendar = jQuery(this.elementRef.nativeElement).position().top;
const topOfHeightElement = heightElement.position().top;
return heightElement.height()! - (topOfCalendar - topOfHeightElement);
}
private sanitizedValue(workPackage:WorkPackageResource, attribute:string, toStringMethod:string|null = 'name') {
let value = workPackage[attribute];
value = toStringMethod && value ? value[toStringMethod] : value;
value = value || this.I18n.t('js.placeholders.default');
return this.sanitizer.sanitize(SecurityContext.HTML, value);
}
}

Loading…
Cancel
Save