var overlayControl = gantt.ext.overlay; var today = new Date(); var return_first; var parts = []; var sCurvePayload = { period: 'week', project_id: proyekId, gantt_id: ganttId }; function callback(response) { return_first = response; } function messageListener(event) { if (event.data && event.data.action === 'sendUrl') { const parentUrl = event.data.url; const isHierarchy = event.data.isHierarchy; // Split the remaining path by "/" parts = parentUrl.split("/"); if (parts[7] && parts[7] == "1" && isHierarchy) { $.ajax({ headers: { "Content-Type": "application/json", "Authorization": `Bearer ${token}` }, url: `${base_url}project/calculate-s-curve`, type: "POST", data: JSON.stringify(sCurvePayload), success: function (data) { callback(data) window.removeEventListener('message', messageListener); } }); $.ajax({ headers: { "Content-Type": "application/json", "Authorization": `Bearer ${token}` }, url: `${base_url}project/s-curve-command`, type: "POST", data: JSON.stringify(sCurvePayload), }); } else { $.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) window.removeEventListener('message', messageListener); } }); } } } function getSCurveData() { window.parent.postMessage({ action: 'getUrl' }, '*'); window.addEventListener('message', messageListener); }; 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); // } 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(); // console.log("apa ", return_first); var cumulativePlannedDurations = return_first.data[0].data.percentagePlan; var cumulativeRealDurations = return_first.data[0].data.percentageReal; cumulativeRealDurations[cumulativeRealDurations.length - 1] = Math.ceil(cumulativeRealDurations[cumulativeRealDurations.length - 1]); var maxPlan = cumulativePlannedDurations[cumulativePlannedDurations.length - 1] var maxReal = cumulativeRealDurations[cumulativeRealDurations.length - 1] var dates = return_first.data[0].data.date; // Determine the appropriate data points based on the Gantt chart zoom level var chartScaleRange = getChartScaleRange(); var zoomLevel = gantt.ext.zoom.getCurrentLevel(); // Adjust data points based on the zoom level var plannedDurations, realDurations; if (zoomLevel === 2) { // Adjust data to monthly points plannedDurations = adjustDataToZoom(dates, cumulativePlannedDurations, chartScaleRange, 2).data; realDurations = adjustDataToZoom(dates, cumulativeRealDurations, chartScaleRange, 2).data; dates = adjustDataToZoom(dates, cumulativeRealDurations, chartScaleRange, 2).dates; } else if (zoomLevel === 1) { // Adjust data to weekly points plannedDurations = adjustDataToZoom(dates, cumulativePlannedDurations, chartScaleRange, 1).data; realDurations = adjustDataToZoom(dates, cumulativeRealDurations, chartScaleRange, 1).data; dates = adjustDataToZoom(dates, cumulativeRealDurations, chartScaleRange, 1).dates; } else { // Default: Use daily data points plannedDurations = cumulativePlannedDurations; realDurations = cumulativeRealDurations; } if (Math.floor(maxReal) > 110 || Math.floor(maxPlan) > 110) { plannedDurations = plannedDurations.map((item) => { return item / maxPlan * 100; }) realDurations = realDurations.map((item) => { if (maxPlan >= 200) { return item / maxPlan * 100; } else { return item / maxReal * 100; } }) } return { planned: plannedDurations, real: realDurations, dates: dates }; } function adjustDataToZoom(dates, data, chartScaleRange, zoomLevel) { // Implement logic to adjust data points based on the Gantt chart zoom level // For example, if zoomLevel is 'month', aggregate daily data to monthly data // Placeholder logic: This example assumes that the chartScaleRange is in days var newData = []; var newDates = []; var aggregateValue = 0; for (var i = 0; i < data.length; i++) { aggregateValue = data[i]; if (zoomLevel === 2 && i % 30 === 0) { // Aggregate data for each month (assuming 30 days in a month) newData.push(aggregateValue); newDates.push(dates[i]); aggregateValue = 0; } else if (zoomLevel === 1 && (i + 1) % 7 === 1) { // Aggregate data for each week newData.push(aggregateValue); newDates.push(dates[i]); aggregateValue = 0; } } // If there are remaining data points after the loop, add them to the result if (aggregateValue > 0) { newData.push(aggregateValue); newDates.push(dates[data.length - 1]); } return { data: newData, dates: newDates }; } function getScalePaddings(values) { let zoom = gantt.ext.zoom.getCurrentLevel(); var scale = gantt.getScale(); var dataRange = gantt.getSubtaskDates(); // let minDate = new Date(); // let maxDate = new Date(); let minValueDate = new Date(values.dates[0]); let maxValueDate = new Date(values.dates[values.dates.length - 1]); // gantt.eachTask(function(task){ // let plannedEarlier = task.planned_start < task.start_date; // let plannedLater = task.planned_end > task.end_date; // if (plannedEarlier) { // minDate = new Date(Math.min(minDate.getTime(), task.planned_start.getTime())); // } else { // minDate = new Date(Math.min(minDate.getTime(), task.start_date.getTime())); // } // if (plannedLater) { // maxDate = new Date(Math.max(maxDate.getTime(), task.planned_end.getTime())); // } else { // maxDate = new Date(Math.max(maxDate.getTime(), task.end_date.getTime())); // } // }) 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(minValueDate); padding.right = scale.full_width - gantt.posFromDate(maxValueDate); 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 values = getProgressLine(); var scaleLabels = []; // if(parts[2] && parts[2] == '1'){ // values.dates.forEach(function(date){ // var dateScale = new Date(date); // scaleLabels.push(dateToStr(dateScale)); // }) // } else { // var chartScale = getChartScaleRange(); // chartScale.forEach(function(date){ // scaleLabels.push(dateToStr(date)); // }); // } values.dates.forEach(function (date) { var dateScale = new Date(date); scaleLabels.push(dateToStr(dateScale)); }) 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: "#0000cc", borderColor: "#0000cc", data: values.planned, fill: false, cubicInterpolationMode: 'monotone' }, { label: "Real progress", backgroundColor: "#006600", borderColor: "#006600", data: values.real, fill: false, cubicInterpolationMode: 'monotone' } ] }, options: { responsive: true, maintainAspectRatio: false, layout: { padding: getScalePaddings(values) }, onResize: function (chart, newSize) { var dataRange = gantt.getSubtaskDates(); if (dataRange.start_date) { // align chart with the scale range chart.options.layout.padding = getScalePaddings(values); } }, legend: { display: false }, tooltips: { mode: "index", intersect: false, callbacks: { label: function (tooltipItem, data) { var dataset = data.datasets[tooltipItem.datasetIndex]; return dataset.label + ": " + dataset.data[tooltipItem.index] + "%"; } } }, 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, stepSize: 10, } }, ] } } }); return canvas; });