|
|
|
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 = "<table></table>";
|
|
|
|
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 = '<thead>';
|
|
|
|
|
|
|
|
titleLines.forEach(function(title) {
|
|
|
|
innerHtml += '<tr><th>' + title + '</th></tr>';
|
|
|
|
});
|
|
|
|
innerHtml += '</thead><tbody>';
|
|
|
|
|
|
|
|
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 = '<span style="' + style + '"></span>';
|
|
|
|
innerHtml += '<tr><td>' + "<span style='color: #001eff'>Planned Progress</span> : " + values.planned[tooltipModel.dataPoints[0].index] + '%</td></tr>';
|
|
|
|
innerHtml += '<tr><td>' + "<span style='color: #f51b1b'>Actual Progress</span> : " + values.real[tooltipModel.dataPoints[0].index] + '%</td></tr>';
|
|
|
|
innerHtml += '<tr><td> </td></tr>';
|
|
|
|
innerHtml += '<tr><td>' + "BCWP :" + '</td></tr>';
|
|
|
|
// someone pls change this hard coded currency later
|
|
|
|
innerHtml += '<tr><td>Rp. ' + thousandSeparator(values.bcwp[tooltipModel.dataPoints[0].index]) + '</td></tr>';
|
|
|
|
});
|
|
|
|
innerHtml += '</tbody>';
|
|
|
|
|
|
|
|
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;
|
|
|
|
});
|