Hello team,
Expecting your support in helping us to find a better feasible solution for the problem I am facing with different zones here, as the date fields are being converted into the local time zones, we are facing a big issue in calculating project dates, as the system is adding a few extra hours based on the UTC and the local time zone; in one single time, we are observing two different end dates at two different regions or even saving the date to our server from region is causing an issue of time difference for the users in other time zones.
Irrespective of fixing the bryntum gantt timezone, we are facing this issue, can you please help us in achive this functionality where irrespective of different timezones the users are in, the grid records should only take the project specifc timezone data, not the respective local time zone coverted data.
Please refer to our current project config and expecting your support in fixing this.
defineBryntumGanttConfig() {
// console.log("defineBryntumGanttConfig: this.hideOwnerTeam:",this.hideOwnerTeam);
this.ganttConfig = {
dependencyIdField: 'wbsCode',
// dependencyIdField: 'sequenceNumber',
selectionMode: {
row: true,
cell: true,
// dragSelect: true,
rowNumber: true,
},
preserveScrollOnDatasetChange: true,
displaySchedulingIssueResolutionPopup: false,
showTaskColorPickers: false,
showTooltip: false,
taskMenuFeature: {
items: {
cut: false,
editTask: false,
// add: {
// menu: {
// subtask: false,
// milestone: false,
// }
// },
add: false,
addTaskAbove: {
text: 'Add Task above',
icon: 'b-icon-up',
onItem: ({ taskRecord }: any) => {
console.log(taskRecord.parent, taskRecord.child, taskRecord.successor, taskRecord.predecessor)
taskRecord.parent.insertChild({
wbs: '1.1.1', name: 'NEW TASK ABOVE', duration: 1, taskstatus: this.newStatusId, eventColor: this.newTaskColor, manuallyScheduled: true, startDate: moment().format('YYYY-MM-DD'), endDate: moment().format('YYYY-MM-DD')
}, taskRecord);
// run propagation to calculate new task fields
// this.ganttInstance.project.propagate();
}
},
addTenTaskAbove: {
text: 'Add 10 Task(s) above',
icon: 'b-icon-up',
onItem: ({ taskRecord }: any) => {
console.log(taskRecord.parent, taskRecord.child, taskRecord.successor, taskRecord.predecessor)
for (var i = 0; i < 11; i++) {
taskRecord.parent.insertChild({
wbs: '1.1.1', name: 'NEW TASK ABOVE', duration: 1, taskstatus: this.newStatusId, eventColor: this.newTaskColor, manuallyScheduled: true, startDate: moment().format('YYYY-MM-DD'), endDate: moment().format('YYYY-MM-DD')
}, taskRecord);
}
// run propagation to calculate new task fields
// this.ganttInstance.project.propagate();
}
},
addTaskBelow: {
text: 'Add Task below',
icon: 'b-icon-down',
onItem: ({ taskRecord }: any) => {
console.log(taskRecord.parent, taskRecord.child, taskRecord.successor, taskRecord.predecessor)
taskRecord.parent.insertChild({
wbs: '1.1.1', name: 'NEW TASK BELOW', duration: 1, taskstatus: this.newStatusId, eventColor: this.newTaskColor, manuallyScheduled: true, startDate: moment().format('YYYY-MM-DD'), endDate: moment().format('YYYY-MM-DD')
}, taskRecord.nextSibling);
// run propagation to calculate new task fields
// this.ganttInstance.project.propagate();
}
},
addTenTaskBelow: {
text: 'Add 10 Task(s) below',
icon: 'b-icon-down',
onItem: ({ taskRecord }: any) => {
// console.log(taskRecord.parent, taskRecord.child, taskRecord.successor, taskRecord.predecessor)
for (var i = 0; i < 11; i++) {
taskRecord.parent.insertChild({
wbs: '1.1.1', name: 'NEW TASK BELOW', duration: 1, taskstatus: this.newStatusId, eventColor: this.newTaskColor, manuallyScheduled: true, startDate: moment().format('YYYY-MM-DD'), endDate: moment().format('YYYY-MM-DD')
}, taskRecord.nextSibling);
}
// run propagation to calculate new task fields
// this.ganttInstance.project.propagate();
}
},
// autoScheduleTask: {
// text: 'Auto-Schedule',
// icon: 'b-fa b-fa-calendar-alt',
// onItem: ({ taskRecord }: any) => {
// let selectedData = this.ganttInstance.selectedRecords;
// const popup = new Popup({
// title: 'Auto Schedule & Retain Current Dates',
// closable: true,
// modal: true,
// items: [
// {
// type: 'widget',
// html: '<p>Auto-scheduling will modify planned dates based on dependencies or constraints.Do you want to retain the planned dates?</p>',
// cls: 'b-popup-message'
// }
// ],
// bbar: [
// {
// text: 'Yes',
// cls: 'b-btn b-ok b-raised popup-tooltip',
// tooltip: {
// html: "This sets a 'Start No Earlier Than' constraint to keep an auto-scheduled task's manually assigned dates, unless affected by an intermediate dependency",
// align: 'b-t',
// cls: 'popup-tooltip'
// },
// onClick: () => {
// selectedData.forEach((task) => {
// if (task.manuallyScheduled === true) {
// const fixedStartDate = task.startDate !== null ? new Date(task.startDate).setHours(0, 0, 0, 0) : null;
// const fixedEndDate = task.endDate !== null ? new Date(task.endDate).setHours(0, 0, 0, 0) : null;
// task.manuallyScheduled = false;
// if (fixedStartDate !== null) {
// task.setStartDate(fixedStartDate, false);
// }
// if (fixedEndDate !== null) {
// task.setEndDate(fixedEndDate, false);
// }
// }
// });
// console.log('Retain planned dates clicked');
// popup.close();
// }
// },
// {
// text: 'No',
// cls: 'b-btn popup-tooltip',
// onClick: () => {
// selectedData.forEach((task) => { task.manuallyScheduled = false });
// console.log('OK clicked');
// popup.close();
// }
// },
// ]
// });
// popup.show();
// }
// },
delete: true,
convertToMilestone: false,
linkTasks: false,
unlinkTasks: false
}
},
projectLinesFeature: {
disabled: true
},
cellEditFeature: {
addNewAtEnd: false
},
cellTooltipFeature: {
disabled: false,
cls: 'custom-css-tooltip',
hoverDelay: 100
},
taskTooltipFeature: {
disabled: true
// template: ({ taskRecord }) => `${taskRecord.name}`,
// // Tooltip configs can be used here
// align: 'l-r' // Align left to right
},
project: {
autoCalculatePercentDoneForParentTasks: false,
skipNonWorkingTimeWhenSchedulingManually: true,
skipNonWorkingTimeInDurationWhenSchedulingManually: true,
autoSetConstraints: true,
daysPerWeek: 5,
daysPerMonth: 20,
calendar: 'general',
hoursPerDay: 8,
calendarsData: [
{
id : 'general',
name : 'General',
intervals : [
{
recurrentStartDate : 'on Sat',
recurrentEndDate : 'on Mon',
isWorking : false
},
{
recurrentStartDate : 'every weekday at 18:01',
recurrentEndDate : 'every weekday at 10:00',
isWorking : false
}
]
}
],
// calendarsData: this.simulationData?.calendars?.rows ? this.simulationData.calendars.rows : [
// {
// id: "general",
// name: "General",
// intervals: [
// {
// recurrentStartDate: "on Sat at 0:00",
// recurrentEndDate: "on Mon at 0:00",
// isWorking: false
// }
// ]
// }
// ],
// calendarsData: [
// {
// id: "general",
// name: "General",
// intervals: [
// {
// recurrentStartDate: "on Sat at 0:00",
// recurrentEndDate: "on Mon at 0:00",
// isWorking: false
// }
// ]
// }
// ],
// Let the Project know we want to use our own Task model with custom fields / methods
// transport: {
// load: {
// url: 'assets/data/launch-motiva.json'
// }
// },
autoLoad: true,
assignmentStore: {
data: this.simulationData.assignments.rows,
useRawData: true,
},
resourceStore: {
data: this.simulationData.resources.rows,
useRawData: true,
},
taskStore: {
data: this.simulationData.tasks.rows,
useRawData: true,
wbsMode: 'auto'
},
dependencyStore: {
data: this.simulationData.dependencies.rows,
useRawData: true,
},
timeRangeStore: {
data: [
{
"id": 1,
"name": moment().format('Do MMM YY'),
"startDate": moment().format('YYYY-MM-DD'),
"duration": 0,
"durationUnit": "d",
"cls": "b-fa b-fa-calendar-day"
}
]
},
listeners: {
refresh: ({ records }) => {
console.log('Calculations finished');
this.displayGantt = true;
this.message.remove(this.loadingIndicator);
this.originalStartDate = this.ganttInstance.startDate;
this.originalEndDate = this.ganttInstance.endDate;
},
},
// The State TrackingManager, which the UndoRedo widget in the toolbar uses
stm: {
// NOTE, that this option does not enable the STM itself, this is done by the `undoredo` widget, defined in the toolbar
// If you don't use `undoredo` widget in your app, you need to enable STM manually: `stm.enable()`,
// otherwise, it won't be tracking changes in the data
// It's usually best to enable STM after the initial data loading is completed.
autoRecord: true
},
// This config enables response validation and dumping of found errors to the browser console.
// It's meant to be used as a development stage helper only, so please set it to false for production systems.
validateResponse: true,
delayCalculation: true,
useRawData: true
},
// startDate: '2019-01-12',
// endDate: '2019-03-24',
startDate: this.simulationData.project.startDate,
endDate: this.simulationData.project.endDate,
columns: [
{ type: 'sequence', text: '#', width: 50, align: 'center' },
{
width: 60,
type: 'manuallyscheduled',
text: "Manually Scheduled",
align: 'center',
showCheckAll: true,
sortable: false,
filterable: false,
afterRenderCell({ record, widgets }: any) {
// Hide checkboxes in certain rows
const wbsLevel = record.wbs.split('.').length;
// Hide the checkbox if WBS level is 1 or 2
if (wbsLevel <= 2) {
widgets[0].hidden = true;
} else {
widgets[0].hidden = false;
}
},
listeners: {
// toggle: ({ record, checked }: any) => {
// const fixedStartDate = record.startDate !== null ? new Date(record.startDate).setHours(0, 0, 0, 0) : null;
// const fixedEndDate = record.endDate !== null ? new Date(record.endDate).setHours(0, 0, 0, 0) : null;
// record.manuallyScheduled = true;
// if (fixedStartDate !== null) {
// record.setStartDate(fixedStartDate, false);
// }
// if (fixedEndDate !== null) {
// record.setEndDate(fixedEndDate, false);
// }
// if (!checked) {
// const popup = new Popup({
// title: 'Auto Schedule & Retain Current Dates',
// closable: true, // Allows closing the popup
// modal: true, // Prevents interaction with other elements
// items: [
// {
// type: 'widget',
// html: `<div><p>Auto-scheduling will modify planned dates based on dependencies or constraints.
// Do you want to retain the planned dates?</p>
// <p> Current Planned Start :
// <b> ${fixedStartDate ? moment(fixedStartDate).format('MMM D, YYYY') : 'Planned start date not assigned'}</b>
// </p>
// <p>Current Planned End :
// <b> ${fixedEndDate ? moment(fixedEndDate).format('MMM D, YYYY') : ' Planned end date not assigned'}</b>
// </p></div>`,
// cls: 'b-popup-message'
// }
// ],
// bbar: [
// {
// text: 'Yes',
// icon: 'b-icon-info-circle',
// cls: 'b-btn b-ok b-raised popup-tooltip',
// tooltip: {
// html: "This sets a 'Start No Earlier Than' constraint to keep an auto-scheduled task's manually assigned dates, unless affected by an intermediate dependency",
// align: 'b-t', // Aligns tooltip to the right of the button
// cls: 'popup-tooltip'
// },
// onClick: () => {
// record.manuallyScheduled = false;
// if (fixedStartDate !== null) {
// record.setStartDate(fixedStartDate, false);
// }
// if (fixedEndDate !== null) {
// record.setEndDate(fixedEndDate, false);
// }
// console.log('Retain planned dates clicked');
// popup.close();
// }
// },
// {
// text: 'No',
// cls: 'b-btn popup-tooltip',
// onClick: () => {
// record.manuallyScheduled = false;
// console.log('OK clicked');
// popup.close();
// }
// },
// ]
// });
// popup.show();
// }
// },
}
},
{
type: 'name',
width: 450,
text: "Title",
showWbs: this.showWBS,
align: 'center',
filterable: false,
sortable: false,
// tooltipRenderer: ({ record, column }: any) => {
// return StringHelper.xss`<p style='font-size:12px;margin-bottom:0px;line-height:1rem;'>${record.name}</p>`;
// }
indentSize: 0.5
},
{
type: 'predecessor',
text: "Predecessors",
width: 112,
align: 'center',
sortable: false,
filterable: false,
hidden: true,
},
{
type: 'successor',
text: "Successors",
width: 112,
align: 'center',
sortable: false,
filterable: false,
hidden: true,
},
{
text: 'Status',
field: 'taskstatus',
width: 120,
type: 'template',
align: 'center',
sortable: false,
filterable: false,
editor: {
type: 'combo',
items: this.statusOptions,
displayField: 'status',
valueField: '_id',
listeners:{
change: ({ value, oldValue, source, userAction, valid}) => {
console.log('Status changed from', oldValue, 'to', value);
const userAccessLevel = this.currentUser.user.accessLevel;
const status = this.getStatusForBryntum(value);
// console.log("STATUS:",status);
if(status.status == "Not relevant" && userAccessLevel != "Super"){
// console.log("userAccessLevel:",userAccessLevel);
console.log("yes it is not relevant and the user is not super here");
source.value = oldValue
Toast.show({
html : 'This status option is restricted to choose, Thanks for your understanding.',
side : 'top',
showProgress:true,
maxWidth: 400,
});
}
}
}
},
template: ({ value = '' }) => {
const status = this.getStatusForBryntum(value);
if (status != undefined) {
return StringHelper.xss`
<div class="b-status b-status-${status.category.toLowerCase()}">${status.status}</div>`
}
return StringHelper.xss`
<div class="b-status b-status-na">NA</div>`
}
},
{
text: 'Indicator',
headerRenderer(context) {
return `Indicator <i class="fas fa-lock" style="color:red"></i>`;
},
field: 'varianceStatus',
width: 120,
type: 'template',
align: 'center',
sortable: false,
filterable: false,
editor: false,
template: ({ value }) => {
// console.log("variance status", value)
if (value === "overdue") {
return StringHelper.xss`<span class="circular-dot dot-overdue"></span>Overdue`
} else if (value === "ontime") {
return StringHelper.xss`<span class="circular-dot dot-ontime"></span>On Time`
} else if (value === "ontrack") {
return StringHelper.xss`<span class="circular-dot dot-ontrack"></span>On Track`
} else if (value === "earlyend") {
return StringHelper.xss`<span class="circular-dot dot-earlyend"></span>Early End`
} else if (value === "earlystart") {
return StringHelper.xss`<span class="circular-dot dot-earlystart"></span>Early Start`
} else if (value === "lateend") {
return StringHelper.xss`<span class="circular-dot dot-latestart"></span>Late End`
} else if (value === "latestart") {
return StringHelper.xss`<span class="circular-dot dot-lateend"></span>Late Start`
} else if (value === "notstarted") {
return StringHelper.xss`<span class="circular-dot dot-notstarted"></span>Not Started`
} else if (value === "delayed") {
return StringHelper.xss`<span class="circular-dot dot-delayed"></span>Delayed`
}
return StringHelper.xss`<span class="circular-dot dot-notapplicable"></span>Not Applicable`
// return value;
},
hidden: true,
},
{
width: 120,
text: "Delay Log",
filterable: false,
field: 'delayLog',
sortable: false,
tooltip: "Shows the history of delays",
renderer: ({ value }: { value: string }) => {
return value ?? "";
},
hidden: true,
},
{ type: 'constrainttype', text: "Constraint Type", hidden: true, },
{ type: 'constraintdate', text: "Constraint Date", hidden: true, format: 'MMM DD, YYYY', },
{
type: 'startdate',
format: 'MMM DD, YYYY',
text: "Planned Start",
align: 'center',
width: 100,
sortable: false,
filterable: false,
},
{
type: 'enddate',
text: "Planned End",
headerRenderer(context) {
return `Planned End <i class="fas fa-lock" style="color:red"></i>`;
},
align: 'center',
width: 110,
sortable: false,
filterable: false,
editor: false, // Disable editor for all rows (can override later)
},
{
type: 'date',
text: "Actual Start",
headerRenderer(context) {
return `Actual Start <i class="fas fa-lock" style="color:red"></i>`;
},
field: 'actualStart',
width: 110,
sortable: false,
filterable: false,
editor: false,
format: 'MMM DD, YYYY',
renderer: ({ record }: any) => {
// Render the actual start date value
console.log("-----------------------------");
console.log("rec wbs:",record.wbsValue._value);
console.log("rec star:",record.startDate);
console.log("rec end:",record.endDate);
return record.actualStart ? moment(record.actualStart).format('MMM DD, YYYY') : '';
}
},
{
type: 'date',
text: "Actual End",
headerRenderer(context) {
return `Actual End <i class="fas fa-lock" style="color:red"></i>`;
},
field: 'actualEnd',
sortable: false,
width: 110,
filterable: false,
format: 'MMM DD, YYYY',
editor: false,
renderer: ({ record }: any) => {
// Render the actual start date value
return record.actualEnd ? moment(record.actualEnd).format('MMM D, YYYY') : '';
}
},
{
field: 'calculatedStartDate',
text: "Auto Scheduled Start",
width: 130,
headerRenderer(context) {
return `Auto Scheduled Start <i class="fas fa-lock" style="color:red"></i>`;
},
align: 'center',
sortable: false,
filterable: false,
editor: false,
format: 'MMM DD, YYYY',
hidden: true,
},
{
field: 'calculatedEndDate',
text: "Auto Scheduled End",
width: 130,
headerRenderer(context) {
return `Auto Scheduled End <i class="fas fa-lock" style="color:red"></i>`;
},
align: 'center',
sortable: false,
filterable: false,
editor: false,
format: 'MMM DD, YYYY',
hidden: true,
},
{
type: 'duration', text: "Duration", align: 'center', sortable: false, filterable: false, hidden: true,
},
{
field: 'baselineStart',
text: "Baseline Start",
headerRenderer(context) {
return `Baseline Start <i class="fas fa-lock" style="color:red"></i>`;
},
align: 'center',
width: 120,
sortable: false,
filterable: false,
editor: false,
renderer: ({ record }: any) => {
// Render the actual start date value
return record.baselineStart ? moment(record.baselineStart).format('MMM D, YYYY') : '';
},
hidden: true,
},
{
field: 'baselineEnd',
text: "Baseline End",
headerRenderer(context) {
return `Baseline End <i class="fas fa-lock" style="color:red"></i>`;
},
align: 'center',
width: 110,
sortable: false,
filterable: false,
editor: false,
renderer: ({ record }: any) => {
// Render the actual start date value
return record.baselineEnd ? moment(record.baselineEnd).format('MMM D, YYYY') : '';
},
hidden: true,
},
{
field: 'baselineDuration', text: "Baseline Duration", align: 'center', sortable: false, filterable: false, hidden: true,
},
{
field: 'baselineDurationVariance',
text: "Baseline Duration Variance",
headerRenderer(context) {
return `Baseline Duration Variance <i class="fas fa-lock" style="color:red"></i>`;
},
align: 'center',
width: 140,
sortable: false,
filterable: false,
editor: false,
renderer: (cellProps: { record: any, cellElement: any, value: any }) => {
// Reset any existing styles or classes
let value = cellProps.value
if (value) {
if (value > 0) {
cellProps.cellElement.style.color = "green";
} else if (value < 0) {
cellProps.cellElement.style.color = "red";
}
return value;
} else {
cellProps.cellElement.style.color = "black";
return 0;
}
},
hidden: true,
},
{
type: 'earlystartdate',
text: "Early Start",
headerRenderer(context) {
return `Early Start <i class="fas fa-lock" style="color:red"></i>`;
},
align: 'center',
hidden: true,
format: 'MMM DD, YYYY',
},
{
type: 'earlyenddate',
text: "Early End",
headerRenderer(context) {
return `Early End <i class="fas fa-lock" style="color:red"></i>`;
},
align: 'center',
hidden: true,
format: 'MMM DD, YYYY',
},
{
type: 'latestartdate',
text: "Late Start",
headerRenderer(context) {
return `Late Start <i class="fas fa-lock" style="color:red"></i>`;
},
align: 'center',
hidden: true,
format: 'MMM DD, YYYY',
},
{
type: 'lateenddate',
text: "Late End",
headerRenderer(context) {
return `Late End <i class="fas fa-lock" style="color:red"></i>`;
},
align: 'center',
hidden: true,
format: 'MMM DD, YYYY',
},
{
type: 'totalslack',
text: "Total Slack",
headerRenderer(context) {
return `Total Slack <i class="fas fa-lock" style="color:red"></i>`;
},
// text: "Predecessors",
width: 112,
align: 'center',
sortable: false,
filterable: false,
hidden: true,
},
{
text: 'Workstream Type',
field: 'workstreamtype',
align: 'center',
width: 160,
sortable: false,
filterable: false,
editor: {
type: 'combo',
items: this.workstreamOptions,
displayField: 'name',
valueField: '_id'
},
renderer: ({ value }: { value: string }) => {
const status = this.workstreamOptions.find(option => option._id === value);
return status ? status.name : value;
}
},
{
text: 'Owner',
field: 'assignedTo',
width: 120,
align: 'center',
sortable: false,
filterable: false,
editor: {
type: 'combo',
items: this.resourceOptions,
displayField: 'name',
valueField: 'id'
},
renderer: ({ value, cellElement }) => {
const owner = this.resourceOptions.find(option => option.id === value);
return owner ? owner.name : "NA";
}
},
{
text: "Owner's Team",
field: 'team', // Key from the backend
headerRenderer(context) {
return `Owner's Team <i class="fas fa-lock" style="color:red"></i>`;
},
width: 150,
align: 'center',
hidden: this.hideOwnerTeam,
sortable: false,
filterable: false,
editor: false,
renderer: ({ value, cellElement }) => {
const team = this.teamsOptions.find(option => option._id === value);
return team ? team.title : "NA";
}
},
{
text: 'Participants',
field: 'participants',
width: 120,
sortable: false,
filterable: false,
editor: {
type: 'combo',
items: this.resourceOptions,
displayField: 'name',
valueField: 'id',
multiSelect: true
},
renderer: ({ value, cellElement }) => {
// value will be an array of IDs
if (!Array.isArray(value)) return "NA";
const participants = value.map(id =>
this.resourceOptions.find(option => option.id === id)?.name
).filter(Boolean);
// console.log("participants",participants)
return participants.length ? participants.join(', ') : "NA";
},
hidden: true,
},
{
text: 'Task Type',
field: 'tasktype',
width: 120,
align: 'center',
sortable: false,
filterable: false,
editor: {
type: 'combo',
items: this.tasktypeOptions,
displayField: 'name',
valueField: '_id'
},
renderer: ({ value }: { value: string }) => {
const status = this.tasktypeOptions.find(option => option._id === value);
return status ? status.name : value;
},
hidden: true,
},
{
text: 'Role Type',
field: 'roletype',
align: 'center',
width: 120,
sortable: false,
filterable: false,
editor: {
type: 'combo',
items: this.roletypeOptions,
displayField: 'name',
valueField: '_id'
},
renderer: ({ value }: { value: string }) => {
const status = this.roletypeOptions.find(option => option._id === value);
return status ? status.name : value;
},
hidden: true,
},
{
text: "Planned Milestone %",
field: 'plannedmilestonepercentage', // Key from the backend
headerRenderer(context) {
return `Planned Milestone % <i class="fas fa-lock" style="color:red"></i>`;
},
align: 'center',
width: 170,
sortable: false,
filterable: false,
editor: false,
hidden: true,
},
{
text: "Actual Milestone %",
field: 'actualmilestonepercentage', // Key from the backend
headerRenderer(context) {
return `Actual Milestone % <i class="fas fa-lock" style="color:red"></i>`;
},
align: 'center',
width: 150,
sortable: false,
filterable: false,
editor: false,
hidden: true,
},
{ type: 'timeAxis', text: "Time Axis" },
{
type: 'number',
text: "Overdue Days",
field: 'overdueDays', // Key from the backend
headerRenderer(context) {
return `Overdue Days <i class="fas fa-lock" style="color:red"></i>`;
},
width: 150,
align: 'center',
sortable: false,
filterable: false,
editor: false,
hidden: true,
},
{
text: 'Critical Path',
field: 'isCritical',
headerRenderer(context) {
return `Critical Path <i class="fas fa-lock" style="color:red"></i>`;
},
width: 150,
renderer: ({ record }) => {
return record.critical ? 'Yes' : 'No';
},
align: 'center',
sortable: false,
filterable: false,
editor: false,
hidden: true,
},
{
text: "Insights",
field: "insights",
headerRenderer(context) {
return `Insights <i class="fas fa-lock" style="color:red"></i>`;
},
htmlEncode: false, // Key from the backend,
align: 'center',
renderer: ({ record }) => {
let insightValue: any = [];
if (record.insights) {
insightValue = StringHelper.encodeHtml(JSON.stringify(record.insights)); // Convert object to string
}
return `<app-slicer value='${insightValue}'></app-slicer>`;
},
width: 150,
sortable: false,
filterable: false,
editor: false,
hidden: true,
},
{ type: 'percentdone', text: 'Percent Done', width: 70, hidden: true },
{
type: 'number',
text: "Wave Planned Milestone %",
field: 'waveplannedmilestonepercentage', // Key from the backend
headerRenderer(context) {
return `Wave Planned Milestone % <i class="fas fa-lock" style="color:red"></i>`;
},
align: 'center',
width: 150,
sortable: false,
filterable: false,
editor: false,
hidden: true,
},
{
type: 'number',
text: "Wave Actual Milestone %",
field: 'waveactualmilestonepercentage', // Key from the backend
headerRenderer(context) {
return `Wave Actual Milestone % <i class="fas fa-lock" style="color:red"></i>`;
},
align: 'center',
width: 150,
sortable: false,
filterable: false,
editor: false,
hidden: true,
},
],
listeners: {
},
rowReorderFeature: {
showGrip: true,
preserveSorters: true
},
fillHandleFeature: true,
cellCopyPasteFeature: {
// Enable cell copy-paste explicitly
enabled: true
},
rowCopyPasteFeature: {
enabled: true
},
taskCopyPasteFeature: {
useNativeClipboard: true
},
taskDragFeature: true,
subGridConfigs: {
locked: { width: 1000, collapsed: false },
},
columnLines: false,
timeAxis: {
useRawData: true
},
scrollButtonsFeature: {
disabled: false
},
rollupsFeature: {
disabled: true
},
dependenciesFeature: {
disabled: false,
radius: 50,
clickWidth: 50
},
dependencyEditFeature: {
disabled: false,
},
baselinesFeature: {
disabled: true,
renderer: ({ baselineRecord, taskRecord, renderData }: any) => {
// console.log("Baseline record", baselineRecord)
if (baselineRecord.isScheduled && baselineRecord.endDate.getTime() + 24 * 3600 * 1000 < taskRecord.endDate.getTime()) {
renderData.className['b-baseline-behind'] = 1;
}
else if (taskRecord.endDate < baselineRecord.endDate) {
renderData.className['b-baseline-ahead'] = 1;
}
else {
renderData.className['b-baseline-on-time'] = 1;
}
},
},
progressLineFeature: {
disabled: true,
},
headerMenuFeature: false,
filterFeature: false,
sortFeature: false,
columnReorderFeature: {
stretchedDragProxy: true
},
pdfExportFeature: {
disabled: false
},
timeRangesFeature: {
showCurrentTimeLine: false,
},
labelsFeature: {
left: {
field: 'name',
editor: {
type: 'textfield'
}
}
},
tbar: {
// @ts-ignore This is an application custom widget
type: 'gantttoolbar'
}
}
this.ganttInstance.columns.on({
toggleHidden(column) {
// Re-render only if necessary
this.ganttInstance.refresh();
}
});
this.ganttInstance.timeZone = this.selectedZone;
this.columnsToBeUnSelected = ["Auto Scheduled Start", "Auto Scheduled End", "Predecessors", "Successors", "Indicator", "Delay Log", "Constraint Type", "Constraint Date", "Duration", "Baseline Start", "Baseline End", "Baseline Duration", "Baseline Duration Variance",
"Early Start", "Early End", "Late Start", "Late End", "Total Slack", "Participants", "Task Type", "Role Type", "Planned Milestone %", "Actual Milestone %", "Percent Done", "Wave Planned Milestone %", "Wave Actual Milestone %", "Insights", "Overdue Days", "Critical Path"
]; // add the column name to be unselected by default
this.columnsOption = _.map(
_.filter(this.ganttConfig.columns, column => (column["hidden"] !== true && column["text"] != 'Time Axis') || this.columnsToBeUnSelected.includes(column['text'])),
"text"
);
// Add Column names here where we intially set the column to be hidden
// this.columnsOption.push("Auto Scheduled Start");
// this.columnsOption.push("Auto Scheduled End");
this.columnsSelected = this.columnsOption.filter(dt => !this.columnsToBeUnSelected.includes(dt));
console.log("THIS.COLUMNS SELECTED at define bryntu:", this.columnsSelected);
let value = this.waveID === null && this.versionID === null ? 2 : 3
if (this.projectLevelOptions && this.projectLevelOptions.length > 0) {
setTimeout(() => {
this.ganttInstance.features.tree.expandToLevel(this.milestoneLeveltask - value, true);
}, 500);
}
}
and this is how we are sending the task record data to our db server:
startDate: moment(task.startDate).format('YYYY-MM-DD'),
endDate: moment(task.endDate).format('YYYY-MM-DD'),