import { FilterDialogComponent } from './../app/filter-dialog/filter-dialog.component'; import { filteredData } from './../data'; import { ImplementerDialogComponent } from './../app/implementer-dialog/implementer-dialog.component'; import { Component, Inject, OnInit, ViewChild } from '@angular/core'; import { MatPaginator, PageEvent } from '@angular/material/paginator'; import { GanttComponent } from '@syncfusion/ej2-angular-gantt'; import { DataService } from 'src/app/data.service'; import { projectNewData } from 'src/data'; import {FormGroup, FormControl} from '@angular/forms'; import {MatDialog} from '@angular/material/dialog'; import * as $ from 'jquery'; import { StateDialogComponent } from 'src/app/state-dialog/state-dialog.component'; import { MatSnackBar, MatSnackBarHorizontalPosition, MatSnackBarVerticalPosition } from '@angular/material/snack-bar'; @Component({ selector: 'ntt-gantt', templateUrl: './ntt-gantt.component.html', styleUrls: ['./ntt-gantt.component.css'] }) export class NttGanttComponent implements OnInit { @ViewChild('ganttObject') public ganttDefault!: GanttComponent; @ViewChild(MatPaginator) paginator: MatPaginator; public range = new FormGroup({ start: new FormControl(new Date('12/01/2022')), end: new FormControl(new Date('01/31/2024')), }); public data: any[] = []; public allResources: any[] = []; public resources: any[] = []; public taskSettings: object = {}; public labelSettings: object = {}; public projectStartDate: Date = new Date('12/01/2022'); public projectEndDate: Date = new Date('01/31/2024'); public resourceFields: object ={}; public editSettings: object = {}; public columns: object[] = []; public toolbar: any[] = []; public timelineSettings: object ={}; public rendering: boolean = true; public blockedTaskIDs : number [] = [8]; public selectionSettings : object = {}; public spin:boolean = true; public sliceStart: number; public sliceEnd: number; public filterSettings: object; public renderGantt: boolean; public firstLoad: boolean; public selectedScalar: string = 'Monat-Woche'; public scalars: string[] = ['Woche-Tag', 'Monat-Woche', 'Jahr-Monat']; public showDetails: boolean = true; public panelOpenState:boolean; public attributes: any[] = [ //{value: 'vertrag', viewValue: 'WI Vertrag'}, {value: 'resourceName', viewValue: 'Name'}, {value: 'status', viewValue: 'Status'}, //{value: 'implementer', viewValue: 'Implementer'}, {value: 'changeid', viewValue: 'Change Id'}, ]; public paketTypeList: any[] = [ {name: 'Paket Typ A', checked: false}, {name: 'Paket Typ B', checked: false}, {name: 'Paket Typ C', checked: false}, ]; public stateList: any[] = []; public vertragList: any[] = [ {name: 'Vertrag A', checked: false}, {name: 'Vertrag B', checked: false}, {name: 'Vertrag C', checked: false}, ]; public supportGroupList: any[] = [ {name: 'SM ITSM', id: 'SG0001' }, {name: 'Support Group B', id: 'SG0002' }, {name: 'Support Group C', id: 'SG0003' }, ]; public selectedSupportGroup: any = this.supportGroupList[0]; public filterStartDate: Date; public filterEndDate: Date; public selectedAttribute: string; public showFilters: boolean = false; // ["=",">","<","beinhaltet"]; public selectedFilter: string; public showCriterea: boolean = false; public criteria: string = ''; public filterEnabled: boolean = false; public filters:any = null; public sortEnabled: boolean = false; public sortedData: any[] = []; private selectedRescourceIds : any[] = []; private selectedrowindex: number[] = []; private selectedrecords: any[] = []; private selRecs: any[] = []; public states : any[] = []; private approvalPending: boolean; public sort = null; public oldSort = null; /** * The constructor injects required dependencies * @param dataService injects the dataService for data management and backend communication * @param matDialog injects the matDialog */ constructor(public dataService: DataService, public matDialog : MatDialog, private _snackBar: MatSnackBar ) { } /** * The function ngOnInit sets values which are required for rendering the gantt Chart. Furthermore it trims the resources (Changes Array) and sets the paginator to the first page. */ public ngOnInit(): void { var WebFont = require('webfontloader'); WebFont.load({ google: { families: [ 'Material Icons', ], }, }); this.renderGantt = false; this.firstLoad = true; this.sliceStart = 0; this.sliceEnd = 20; // this.dataService.fetchChanges().then((res: any[])=>{ // console.log(res); // this.allResources = res; // this.mapTasksToResources(0,20); // this.range = new FormGroup({ // start: new FormControl(new Date('12/01/'+(new Date().getFullYear()-1))), // end: new FormControl(new Date('01/31/'+(new Date().getFullYear()+1))), // }); // this.renderGantt = true; // }); this.refreshData() this.states = this.dataService.getStates(); this.dataService.fetchStates().then((res: any [])=>{ this.states = res; this.stateList = res; console.log(this.states); }); this.filterSettings = { ignoreAccent: true // columns: [ // { field: 'vertrag', matchCase: false, operator: 'startswith', predicate: 'and', value: 'WV' }, // ] }; this.taskSettings = { id: 'TaskID', name: 'TaskName', startDate: 'StartDate', endDate: 'EndDate', duration: 'Duration', progress: 'Progress', dependency: 'Predecessor', resourceInfo: 'resources', work: 'work', expandState: 'isExpand', child: 'subtasks', isFixed: 'isFixed', vertrag: 'vertrag' }; this.resourceFields = { id: 'resourceId', name: 'resourceName', unit: 'Unit', group: 'resourceGroup', state: 'state', stateName: 'stateName', isRes: 'isRes', vertrag: 'vertrag', vertragName: 'vertragName', supportGroup: 'supportGroup', supportGroupId: 'supportGroupId', implementerEdit: 'implementerEdit' }; this.editSettings = { allowAdding: true, allowEditing: true, allowDeleting: true, allowTaskbarEditing: true, showDeleteConfirmDialog: true }; this.selectionSettings = { mode: 'Row', type: 'Multiple', }; this.columns = [ { field: 'TaskName', headerText: 'Kurzbeschreibung', width: 250 }, { field: 'stateName', headerText: 'Status', width: 250}, { field: 'vertragName', headerText: 'Vertrag / Provider Cluster', width: 150 }, { field: 'supportGroup', headerText: 'Gewählte Support Gruppe' }, { field: 'StartDate', headerText: 'Geplantes Start-Datum'}, // { field: 'approvalStatus', headerText: 'Active Approval', width: 100 }, // { field: 'supportGroupId' }, //{ field: 'Duration' }, //{ field: 'TaskID', headerText: 'Taks ID', width: 250 }, //{ field: 'work', headerText: 'Work' }, //{ field: 'Progress' }, //{ field: 'resourceGroup', headerText: 'Group' }, ]; this.toolbar = ['Cancel']; this.labelSettings = { rightLabel: 'resources', taskLabel: 'TaskName' }; this.timelineSettings= { topTier: { //format: 'YYYY', unit: 'Month' }, bottomTier: { format: 'WW', unit: 'Week', count: 1 } }; this.projectStartDate = this.range.controls.start.value; this.projectEndDate = this.range.controls.end.value; } /** * The function taskbarEditing catches the corresponding syncfsuions event and cancels the user action if the isFixed flag of a task (Date) is set true * @param args event arguments from the syncfusion gantt chart */ public taskbarEditing(args: any) { if(args.data.taskData.isFixed !== false ) { args.cancel = true; } } public renderDetails(){ // console.log(this.showDetails); if(this.showDetails == false){ this.refreshData(); }else{ this.refreshData(); } } public changeScalar(){ if(this.selectedScalar == "Woche-Tag"){ this.timelineSettings= { topTier: { //format: 'WW', unit: 'Week' }, bottomTier: { format: 'dd', unit: 'Day', count: 1 } }; } if(this.selectedScalar == "Monat-Woche"){ this.timelineSettings= { topTier: { //format: 'YYYY', unit: 'Month' }, bottomTier: { format: 'WW', unit: 'Week', count: 1 } }; } if(this.selectedScalar == "Jahr-Monat"){ this.timelineSettings= { topTier: { //format: 'YYYY', unit: 'Year' }, bottomTier: { format: 'MM', unit: 'Month', count: 1 } }; } } /** * The function taskbarEdited catches the corresponding syncfsuions event and cancels the user action if the isFixed flag of a task (Date) is set true * If the isFixed flag is false, it triggers the dataService to update the moved date * @param args event arguments from the syncfusion gantt chart */ public taskbarEdited(args: any){ if(args.data.taskData.isFixed !== false ) { args.cancel = true; }else{ const found = this.allResources.find((resource) => { return resource.resourceId == args.data.taskData.resources[0].resourceId; }); this.dataService.updateDatePerChange(found); } } /** * The function queryTaskbarInfo catches the corresponding syncfsuions event and colors the respective diamond depending on the status * @param args event arguments from the syncfusion gantt chart */ public queryTaskbarInfo(args: any) { if(args.data.taskData.isRes == false){ if(args.data.taskData.TaskID.includes("D2")){ if (args.data.taskData.resources[0].state == 0 ) { args.milestoneColor = "red"; } if (args.data.taskData.resources[0].state == 1 ) { args.milestoneColor = "orange"; } if (args.data.taskData.resources[0].state == 3 ) { args.milestoneColor = "yellow"; } if (args.data.taskData.resources[0].state == 6 ) { args.milestoneColor = "lightgreen"; } if (args.data.taskData.resources[0].state >= 10 ) { args.milestoneColor = "lightgrey"; } }else{ if (args.data.taskData.TaskID.includes("D1")||args.data.taskData.TaskID.includes("D4")){ if(this.showDetails == false){ args.taskbarElement.innerHTML = ""; } } } } } /** * The function onExpand catches the corresponding syncfsuions event and avoids a chart row from beeing expanded by the user * @param args event arguments from the syncfusion gantt chart */ public onExpand(args: any){ args.cancel = true; } /** * The function actionBegin catches the corresponding syncfsuions event * @param event event arguments from the syncfusion gantt chart */ /** * The function mapTasksToResources slices the allResources array to the resources array according the actual page conditions maps the actual tasks (Dates) to the actual resources (Changes) * This function is triggered when a pageEvent is thrown respectively a user changes the page on the paginator * @param start begin of slice * @param end end of slice */ public mapTasksToResources(start : number, end : number){ this.data = []; this.resources = this.allResources; //.slice(start, end); for (const resource of this.resources) { for (const task of resource.tasks) { this.data.push(task); } } } /** * The function load catches the corresponding syncfsuions event and triggers the loading spinner to start while the gantt chart is refrehshing * @param args event arguments from the syncfusion gantt chart */ public load(args: any) { this.spin = true; } /** * The function dataBound catches the corresponding syncfsuions event and stops the loading spinner * @param args event arguments from the syncfusion gantt chart */ public dataBound(args: any) { this.spin = false; } /** * The function created catches the corresponding syncfsuions event and stops the internal loading spinner * @param args event arguments from the syncfusion gantt chart */ public created(args: any) { this.ganttDefault.hideSpinner(); this.ganttDefault.treeGrid.grid.hideSpinner(); } /** * The function onChange catches the corresponding syncfsuions event * @param args event arguments from the syncfusion gantt chart */ public onChange(args: any){ } /** * The function handlePageEvent catches the corresponding matpaginator event and slices the gantt chart, that it fits to the actual page * @param e pageEvent from the matpaginator */ public handlePageEvent(e: PageEvent):void { let start = e.pageSize*e.pageIndex; let end = start+ e.pageSize; this.sliceStart = start; this.sliceEnd = end; //this.mapTasksToResources(start, end); //this.quickRefresh(); this.refreshData(); // this.ganttDefault.dataSource = this.data; // this.ganttDefault.resources = this.resources; // this.ganttDefault.refresh(); } /** * The function startDateChanged catches the corresponding date-range-picker event * @param startDate new startDate from date-range-picker */ public startDateChanged(startDate: any){ } /** * The function endDateChanged catches the corresponding date-range-picker event and validates the selected start- and enddate bacause the gantt chart needs a valid Date to be displayed * @param endDate new endDate from date-range-picker */ public endDateChanged(endDate: any){ if(this.range.status == 'VALID' && this.range.controls.start.value && this.range.controls.end.value){ //event for Requesting new Records based on the Dates } } /** * The function rowSelected catches the corresponding syncfsuions event and checks if all selected resources (changes) have the same status, then it displays the corresponding buttons * @param args event arguments from the syncfusion gantt chart */ public rowSelected(args: any) { this.approvalPending = false; this.selectedrowindex = this.ganttDefault.selectionModule.getSelectedRowIndexes(); // get the selected row indexes. this.selectedrecords = this.ganttDefault.selectionModule.getSelectedRecords(); // get the selected records. this.selRecs=[]; if(this.selectedrowindex.length>this.resources.length-1){ for (const record of this.selectedrecords) { if(record.taskData.isRes == true){ this.selRecs.push(record); } } }else{ this.selRecs = this.selectedrecords; } let allStates: boolean = true; this.selectedRescourceIds = []; for (const change of this.selRecs) { if(change.taskData.approvalStatus == 1){ this.approvalPending = true; } if(change.taskData.state == this.selRecs[0].taskData.state){ if(allStates){ allStates = true; } this.selectedRescourceIds.push(change.taskData.resourceId); }else{ allStates = false; } } if(allStates){ if(this.approvalPending == true){ this.toolbar = ['Cancel',{text: "Genehmigen", id: "7"},{text: "Ablehnen", id: "8"}]; } else{ if(this.selRecs[0].taskData.state > 0 &&this.selRecs[0].taskData.state < 4){ this.toolbar = ['Cancel',{text: "Statusübergang", id: "6"} ,{text: "Implementer eintragen", id: "10"}]; }else{ this.toolbar = ['Cancel',{text: "Statusübergang", id: "6"}]; if(this.selRecs[0].taskData.state == 0){ this.toolbar = [{text: "stornieren", id: "11"},{text: "Statusübergang", id: "6"} ,{text: "Implementer eintragen", id: "10"}]; } } } }else{ this.toolbar = ['Cancel']; } this.approvalPending = false; this.selRecs = []; this.selectedrecords = []; this.selectedrowindex = []; } /** * The function rowDeselected catches the corresponding syncfsuions event * When a line is deselected, it is checked if all resource states are equal after deselecting a resource, if they are equal then the respective buttons are displayed * @param args event arguments from the syncfusion gantt chart */ public rowDeselected(args: any){ this.selectedRescourceIds.forEach((element,index)=>{ if(element == args.data.taskData.resourceId) delete this.selectedRescourceIds[index]; //TODO: auf memoryLeak prüfen }); this.selectedrowindex= this.ganttDefault.selectionModule.getSelectedRowIndexes(); // get the selected row indexes. this.selectedrecords= this.ganttDefault.selectionModule.getSelectedRecords(); // get the selected records. this.selRecs = []; if(this.selectedrowindex.length>this.resources.length-1){ for (const record of this.selectedrecords) { if(record.taskData.isRes == true){ this.selRecs.push(record); } } }else{ this.selRecs = this.selectedrecords; } let allStates: boolean = true; this.selectedRescourceIds = []; for (const change of this.selRecs) { if(change.taskData.state == this.selRecs[0].taskData.state){ if(change.taskData.approvalStatus == 1){ this.approvalPending = true; } if(allStates){ allStates = true; } this.selectedRescourceIds.push(change.taskData.resourceId); }else{ allStates = false; } } if(allStates){ if(this.approvalPending == true){ this.toolbar = ['Cancel',{text: "Genehmigen", id: "7"},{text: "Ablehnen", id: "8"}]; } else{ if(this.selRecs[0].taskData.state > 0 &&this.selRecs[0].taskData.state < 4){ this.toolbar = ['Cancel',{text: "Statusübergang", id: "6"} ,{text: "Implementer eintragen", id: "10"}]; }else{ this.toolbar = ['Cancel',{text: "Statusübergang", id: "6"}]; if(this.selRecs[0].taskData.state == 0){ this.toolbar = [{text: "stornieren", id: "11"},{text: "Statusübergang", id: "6"} ,{text: "Implementer eintragen", id: "10"}]; } } } }else{ this.toolbar = ['Cancel']; } this.selRecs = []; this.selectedrecords = []; this.selectedrowindex = []; this.approvalPending = false; } /** * The function toolbarBtnClicked catches the corresponding syncfsuions event and executes the button logic when a button is clicked * @param args */ public toolbarBtnClicked(args :any){ if(args.item.text === "Statusübergang"){ let data = {changes: [], states: this.states}; for (const selectedRescourceId of this.selectedRescourceIds) { for (const iterator of this.resources) { if(iterator.resourceId == selectedRescourceId){ data.changes.push({resourceId: iterator.resourceId, changeNr: iterator.changeNr, currentState: iterator.state}); } } } let dialogRef = StateDialogComponent; this.matDialog.open(dialogRef, { data : data, }).afterClosed().subscribe((res)=>{ if(res=="Success"){ this.refreshData(); } }); } if(args.item.text === "Implementer eintragen"){ let data = {changes: []}; for (const selectedRescourceId of this.selectedRescourceIds) { for (const change of this.resources) { if(change.resourceId == selectedRescourceId){ // console.log(change); data.changes.push({resourceId: change.resourceId, pkgId: change.changeNr, supportGroupId: change.supportGroupId}); } } } this.matDialog.open(ImplementerDialogComponent, { data : data, }); } if(args.item.text === "Genehmigen"){ for (const selectedRescourceId of this.selectedRescourceIds) { for (const change of this.resources) { if(change.resourceId == selectedRescourceId){ this.dataService.updateApproval(change, 1); } } } //this.ganttDefault.refresh(); } if(args.item.text === "ablehnen"){ for (const selectedRescourceId of this.selectedRescourceIds) { for (const change of this.resources) { if(change.resourceId == selectedRescourceId){ this.dataService.updateApproval(change, 2); } } } //this.ganttDefault.refresh(); } if(args.item.text === "stornieren"){ for (const selectedRescourceId of this.selectedRescourceIds) { for (const change of this.resources) { if(change.resourceId == selectedRescourceId){ this.dataService.updateApproval(change, 3); } } } //this.ganttDefault.refresh(); } } quickRefresh(){ this.renderGantt = false; let newData = []; for (const dat of this.data) { newData.push(dat); } this.data = newData; this.renderGantt = true; } refreshData(){ this.renderGantt = false; this.dataService.fetchChanges(this.mapRequestJSON()).then((res: any[])=>{ this.allResources = res; this.mapTasksToResources(this.sliceStart,this.sliceEnd); // console.log(this.allResources) this.renderGantt = true; }); // this.mapTasksToResources(this.sliceStart, this.sliceEnd); // this.data = []; // this.resources = this.allResources.slice(this.sliceStart, this.sliceEnd); // for (const resource of this.resources) { // for (const task of resource.tasks) { // this.data.push(task); // } // } } mapRequestJSON(){ let request = { 'sliceStart': this.sliceStart, 'sliceEnd': this.sliceEnd, filter: this.filterEnabled ? this.filters : null, sort: this.sortEnabled ? this.sort : { 'column': 'ChangeNr',         'mode': 'asc' }, } return request; } private oldFilters:{}; applyFilter(){ if(this.filterEnabled){ this.oldFilters = this.filters this.filters = null; this.refreshData(); }else{ this.filters = this.oldFilters; this.refreshData(); } } openFilterDialog(){ let dialogRef = FilterDialogComponent; this.matDialog.open(dialogRef, { data : [], height: '70%', width: '80%' }).afterClosed().subscribe((res)=>{ if(res){ // console.log(res); this.filters = res; this.filterEnabled = true; this.refreshData(); } }); } public buildFilterObject(){ this.filterEnabled = true; let filter = [] let critPaketTypeFilter = []; let critStateTypeFilter = []; let critVertragTypeFilter = []; let critSupportGroupfilter = {}; let critDateFilter = {}; let critTextFilter = {}; for (const paketType of this.paketTypeList) { if(paketType.checked == true){ critPaketTypeFilter.push(paketType.name); } } if(critPaketTypeFilter.length > 0){ filter.push({ "column": "paketType",        "filter": "equals",        "criteria": critPaketTypeFilter }); } for (const state of this.stateList) { if(state.checked == true){ critStateTypeFilter.push(state.stateNameEN); } } if(critStateTypeFilter.length > 0){ filter.push({ "column": "State",        "filter": "equals",        "criteria": critStateTypeFilter }); } for (const vertrag of this.vertragList) { if(vertrag.checked == true){ critVertragTypeFilter.push(vertrag.name); } } if(critVertragTypeFilter.length > 0){ filter.push({ "column": "Contract",       "filter": "equals",       "criteria": critVertragTypeFilter }); } // filter.push({ // "column": "supportGroup", //       "filter": "equals", //       "criteria": [this.selectedSupportGroup.id] // }); if(this.filterStartDate != null && this.filterEndDate != null){ filter.push({ "column": "D2",       "filter": "dateRange",       "criteria": [this.filterStartDate, this.filterEndDate] }); } if(this.criteria != null && this.criteria != ""){ filter.push({ "column": "ResourceName",       "filter": "contains",       "criteria": [this.criteria] }); } this.filters = filter; this.refreshData(); } public actionBegin(args: any) { // console.log(args); //custom Action if(args.requestType=='sorting'){ let colName = ""; let mode = "asc"; args.cancel = true; switch (args.columnName) { case 'TaskName': colName = 'ResourceName'; break; case 'stateName': colName = 'State'; break; case 'supportGroup': colName = 'SupportGroup'; break; case 'approvalStatus': colName = 'ApprovalStatus'; break; default: break; } if(colName != ''){ this.sortEnabled = true; if(this.oldSort != null && this.oldSort.column == this.sort.column){ mode = 'dsc' } this.oldSort = this.sort; this.sort = { 'column': colName, 'mode': mode } this.refreshData(); } } } }