var overlayControl = gantt.ext.overlay; var today = new Date(); var return_first; var sCurvePayload = { period: 'week', project_id: proyekId }; function thousandSeparator(x) { return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, "."); } function callback(response) { return_first = response; } function getSCurveData(){ $.ajax({ headers: { "Content-Type": "application/json", "Authorization": `Bearer ${token}` }, url: `${base_url}project/get-s-curve`, type: "POST", data:JSON.stringify(sCurvePayload), success: function (data) { callback(data) } }); }; function toggleOverlay() { if(overlayControl.isOverlayVisible(lineOverlay)){ gantt.config.readonly = false; overlayControl.hideOverlay(lineOverlay); gantt.$root.classList.remove("overlay_visible"); }else{ gantt.config.readonly = true; overlayControl.showOverlay(lineOverlay); gantt.$root.classList.add("overlay_visible"); } } function getChartScaleRange(){ var tasksRange = gantt.getSubtaskDates(); var cells = []; var scale = gantt.getScale(); if(!tasksRange.start_date){ return scale.trace_x; } scale.trace_x.forEach(function(date){ if(date >= tasksRange.start_date && date <= tasksRange.end_date){ cells.push(date); } }); return cells; } function getProgressLine(){ // As long as the progress data length is same with chart scale range (period) then it's fine. getSCurveData(); var cumulativePlannedDurations = return_first.data[0].data.percentagePlan; var cumulativeRealDurations = return_first.data[0].data.percentageReal; var bcwpCumulative = return_first.data[0].data.bcwp; return {planned: cumulativePlannedDurations, real: cumulativeRealDurations, bcwp: bcwpCumulative}; } function getScalePaddings(){ var scale = gantt.getScale(); var dataRange = gantt.getSubtaskDates(); var chartScale = getChartScaleRange(); var newWidth = scale.col_width; var padding = { left:0, right:0 }; if(dataRange.start_date){ var yScaleLabelsWidth = 48; // fine tune values in order to align chart with the scale range padding.left = gantt.posFromDate(dataRange.start_date) - yScaleLabelsWidth; padding.right = scale.full_width - gantt.posFromDate(dataRange.end_date) - yScaleLabelsWidth; padding.top = gantt.config.row_height - 12; padding.bottom = gantt.config.row_height - 12; } return padding; } var myChart; var lineOverlay = overlayControl.addOverlay(function(container) { var scaleLabels = []; var chartScale = getChartScaleRange(); chartScale.forEach(function(date){ scaleLabels.push(dateToStr(date)); }); var values = getProgressLine(); var canvas = document.createElement("canvas"); container.appendChild(canvas); canvas.style.height = container.offsetHeight + "px"; canvas.style.width = container.offsetWidth + "px"; var ctx = canvas.getContext("2d"); if(myChart){ myChart.destroy(); } myChart = new Chart(ctx, { type: "line", data: { datasets: [ { label: "Planned progress", backgroundColor: "#001eff", borderColor: "#001eff", data: values.planned, fill: false, cubicInterpolationMode: 'default' }, { label: "Real progress", backgroundColor: "#f51b1b", borderColor: "#f51b1b", data: values.real, fill: false, cubicInterpolationMode: 'monotone' }, ] }, options: { responsive: true, maintainAspectRatio: false, layout: { padding: getScalePaddings() }, onResize: function(chart, newSize) { var dataRange = gantt.getSubtaskDates(); if(dataRange.start_date){ // align chart with the scale range chart.options.layout.padding = getScalePaddings(); } }, legend: { display: false }, tooltips: { enabled: false, custom: function(tooltipModel, dataItem) { // Tooltip Element var tooltipEl = document.getElementById('chartjs-tooltip'); // Create element on first render if (!tooltipEl) { tooltipEl = document.createElement('div'); tooltipEl.id = 'chartjs-tooltip'; tooltipEl.innerHTML = "
"; document.body.appendChild(tooltipEl); } // Hide if no tooltip if (tooltipModel.opacity === 0) { tooltipEl.style.opacity = 0; return; } // Set caret Position tooltipEl.classList.remove('above', 'below', 'no-transform'); if (tooltipModel.yAlign) { tooltipEl.classList.add(tooltipModel.yAlign); } else { tooltipEl.classList.add('no-transform'); } function getBody(bodyItem) { return bodyItem.lines; } // Set Text if (tooltipModel.body) { var titleLines = tooltipModel.title || []; var bodyLines = tooltipModel.body.map(getBody); var innerHtml = ''; titleLines.forEach(function(title) { innerHtml += '' + title + ''; }); innerHtml += ''; bodyLines.forEach(function(body, i) { var colors = tooltipModel.labelColors[i]; var style = 'background:' + colors.backgroundColor; style += '; border-color:' + colors.borderColor; style += '; border-width: 2px'; var span = ''; innerHtml += '' + "Planned Progress : " + values.planned[tooltipModel.dataPoints[0].index] + '%'; innerHtml += '' + "Actual Progress : " + values.real[tooltipModel.dataPoints[0].index] + '%'; innerHtml += ' '; innerHtml += '' + "BCWP :" + ''; // someone pls change this hard coded currency later innerHtml += 'Rp. ' + thousandSeparator(values.bcwp[tooltipModel.dataPoints[0].index]) + ''; }); innerHtml += ''; var tableRoot = tooltipEl.querySelector('table'); tableRoot.innerHTML = innerHtml; } // `this` will be the overall tooltip var position = this._chart.canvas.getBoundingClientRect(); // Display, position, and set styles for font tooltipEl.style.opacity = 1; tooltipEl.style.zIndex= 99999; tooltipEl.style.position = 'absolute'; tooltipEl.style.left = position.left + window.pageXOffset + tooltipModel.caretX + -160 + 'px'; tooltipEl.style.top = position.top + window.pageYOffset + tooltipModel.caretY + -130 + 'px'; tooltipEl.style.fontFamily = tooltipModel._bodyFontFamily; tooltipEl.style.color= "#000000"; tooltipEl.style.fontSize = tooltipModel.bodyFontSize + 'px'; tooltipEl.style.fontStyle = tooltipModel._bodyFontStyle; tooltipEl.style.padding = tooltipModel.yPadding + 'px ' + tooltipModel.xPadding + 'px'; tooltipEl.style.pointerEvents = 'none'; tooltipEl.style.backgroundColor = 'rgba(238,238,238,0.9)'; } }, hover: { mode: "nearest", intersect: true }, scales: { xAxes: [{ labels: scaleLabels, gridLines:{ display: false }, ticks: { display: false } }, { position:"top", labels: scaleLabels, gridLines:{ display: false }, ticks: { display: false } } ], yAxes: [{ display: true, gridLines: { display:false }, ticks: { display: true, min: 0, max: 100, stepSize: 10, callback: function(current) { if (current > 100) {return "";} return current + "%"; } } }, { display: true, position: "right", gridLines: { display:false }, ticks: { display: true, min: 0, max: 100, stepSize: 10, callback: function(current) { if (current > 100) {return "";} return current + "%"; } }} ] } } }); return canvas; });