Our state of the art Gantt chart


Post by vconstruct »

Hi Team,

We are using bryntum gantt to visualize our schedule data but we found that the events where being forward scheduled, to prevent that we added manuallyScheduled: true flag to all our events but they are still being automatically forward scheduled. are we missing something in our configuration which would prevent the activities from being automatically forward scheduled.

We also tried changing the scheduling mode from normal to fixed duration to resolve this but to no avail.

Please advice.


Post by fabio.mazza »

Hello,
yes, setting manuallyScheduled: true is enough. Could you, please, provide us your config and data?

Best regards,
Fabio


Post by vconstruct »

Hi,
we can give you the config but not the original data, let me get back to you after we have sanitized the data


Post by vconstruct »

Hi,
Below is the configuration that we have been using with bryntum gantt:

Column Def:

  const columnDef: Partial<ColumnStoreConfig>[] | object[] = [

{
  type: 'activityidcolumn',
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  // @ts-ignore
  renderer({ record }) {
    const emptyString = " ";
    if(record._data.activityid){
      return record._data.activityid
    }
    else {
        return emptyString
    }
  },
  text: 'Activity Id',
},
{
  type: 'name',
  field: 'name',
  text: 'Activity Name',
  width: 350,
  editor: false,
  tooltipRenderer : false,
},
{
  type: 'statuscolumn',
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  // @ts-ignore
  renderer({ record }) {
    const emptyString = " ";
    if(record._data.status){
      return record._data.status
    }
    else {
        return emptyString
    }
}
},
{ type: 'date', field: 'startDate', text: 'Start Date', editor: false},
{ type: 'date', field: 'endDate', text: 'End Date', editor: false},
{ type: 'duration', field: 'duration', editor: false , 
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
//@ts-ignore
  renderer: ({ record, column, isExport }) => {
    // return !record.duration ? parseInt(`0`) : record.duration
    if(isExport) {
      return !record.duration ? parseInt(`0`) : record.duration
    } else {
      return !record.duration ? `0 Days` : `${record.duration} Days`
    }
  }
},
{
  type: 'percentdone',
  field: 'percentDone',
  text: 'Progress',
  editor: false, 
  tooltipRenderer : false
},
{
  type: 'criticalcolumn',
  renderer: (record: any) =>{
    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
    //@ts-ignore
    if(record.record._data.isCritical){
        return "Yes"
    }else{
        return "No"
    }
  },
},
{
  type   : 'widget',
  text : 'Trade Partners',
  editor: false,
  htmlEncode : false,
  renderer: ({record, isExport}:any) => {
   const assignments = record.getAssigned()
   if(isExport){
      let assignmentList = "";
      assignments?.forEach((assignment: { _data: { resource: { _data: { name: string; }; }; }; }) => {
        assignmentList += `${assignment._data.resource._data.name};`;
      });
      return assignmentList.slice(0,assignmentList.length-1);
    }
    if (assignments?.size === 1) {
      return  `<div class = "chipView"> ${ [...assignments][0]._data.resource._data.name} </div>`
    } else if(assignments?.size > 1) {
      return `<div class = "chipView"> Multiple </div>`
    } else {
      return ''
    }
  },
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  // @ts-ignore 
  tooltipRenderer : ({record, column,cellElement}) => {
    let assignmentList = "";
    record.getAssigned()?.forEach((assignment: { _data: { resource: { _data: { name: string; }; }; }; }) => {
      assignmentList += `${assignment._data.resource._data.name}; `;
    });
    return assignmentList.slice(0,assignmentList.length-2);
  },
  filterType: 'text',
  filterable: {
    filterField: {
     type: 'text'
    },
    filterFn: ({ value, record }:any) => {
      if(record.getAssigned()?.size && value === ""){
        return true;
      }else if(record.getAssigned()?.size && value !== ""){
        let res = false;
        record.getAssigned().forEach((assignment: { _data: { resource: { _data: { name: string; }; }; }; }) => {
          if(assignment._data.resource._data.name.toLocaleLowerCase().includes(value.toString().toLocaleLowerCase()) ){
            res = true;
          }
        });
        return res;
      }else{
        return false;
      }
    }
  },
  sortable: false
},
{
  type : "hra",
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  // @ts-ignore  
  renderer : ({ record, isExport }) => {
    if(isExport){
      let hraList = "";
      if(record._data?.hra){
        record._data.hra?.forEach((hra: { hra_id: string; hra_description: string; }) => {
          hraList += `${hra.hra_id} - ${hra.hra_description},`
        });
      }
      return hraList.slice(0,hraList.length-1);
    }
    let hra = "";
   
    if(record._data?.hra?.length > 1){
      
     return  `<div class = "chipView"> Multiple </div>`
    
    }else if(record._data?.hra?.length === 1){
      
    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
    // @ts-ignore  
        hra += `<div class = "chipView">${record._data.hra[0].hra_id} - ${record._data.hra[0].hra_description}</div>`
    }
    return `<div class="chipViewRenderer">
                ${hra}
            </div>`
  },
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  // @ts-ignore 
  tooltipRenderer : ({ record, column }) => {
    if(record._data.hra && record._data.hra.length > 0){
      let res = "";
      record._data.hra.forEach((hra: { hra_id: string; hra_description: string; }) => {
        res += `${hra.hra_id}-${hra.hra_description}; `
      });
      return res.slice(0,res.length-2);
    }
  },
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  // @ts-ignore 
  filterable: ({ value, record }) => {
    if(record._data.hra && value === ""){
      return true;
    }else if(record._data.hra && value !== ""){
      let res = false;
      record._data.hra.forEach((hra: {hra_description: string ; hra_id: string; }) => {
        if(hra.hra_description.toLocaleLowerCase().includes(value.toString().toLocaleLowerCase()) || hra.hra_id.toLocaleLowerCase().includes(value.toString().toLocaleLowerCase())){
          res = true;
        }
      });
      return res;
    }else{
      return false;
    }
  },
  sortable: false
},
  ];

Selection Mode prop:

const selectionMode = {
    row : false,
    cell : false,
    rowCheckboxSelection : true,
    multiSelect : true,
    checkbox : true,
    showCheckAll : true,
    deselectFilteredOutRecords : true,
    includeChildren : true,
    preserveSelectionOnPageChange : true,
    preserveSelectionOnDatasetChange : true,
    deselectOnClick : true,
  }

Toolbar widget Options:

  const toolbarOptions: IToolbarModels = {
    ripple: true,
    zoomIn: true,
    zoomOut: true,
    zoomToFit: true,
    collapseAll: true,
    expandAll: true,
    filterActivities: true,
    hideEmptyWBS: true,
    hideNonCriticalTasks: true,
    hideGantt: true,
    hideCompletedTasks: true,
    showDateFilters: true,
    showHideDependency: true,
    export: true,
  };

Gantt Features prop:

const features: IFeatureModels = {
    // cellMenu : {
    //   disabled : true
    // },
    cellEdit: false,
    dependencyEdit : false,
    taskEdit : false,
    rowReorderFeature: false,
    taskDragFeature: false,
    taskResizeFeature: false,
    cellTooltip : {
      hoverDelay      : 0,
      textContent     : true,
      // eslint-disable-next-line @typescript-eslint/ban-ts-comment
      // @ts-ignore  
tooltipRenderer : ({ record, column }) => StringHelper.encodeHtml(record[column.field]), showOnHover : true, hideOnDelegateChange : true, hideDelay : 0 } };

React Gantt Component:

<BryntumGantt
          tbar={{}}
          onCellClick={(event?: any) =>
            props.scheduleInstance.rowSelection(event)
          }
          height={'inherit'}
          width={'inherit'}
          ref={(ganttRef: any) => {
            if (!props.scheduleInstance.getGanttInstance()) {
              props.scheduleInstance.setGanttInstance(ganttRef);
              // eslint-disable-next-line @typescript-eslint/ban-ts-comment
              //@ts-ignore
              ref.current = ganttRef;
            }
          }}
          percentBarFeature={{ allowResize: false }}
          taskDragFeature={false}
          taskResizeFeature={false}
          taskSegmentDragFeature={false}
          taskSegmentResizeFeature={false}
          rowReorderFeature={false}
          features={{
            excelExporter: {
              dateFormat: 'YYYY-MM-DD HH:mm',
              zipcelx,
            },
            filter: true,
            timeRanges: true,
            projectLines: false,
            cellTooltip: props.features.cellTooltip,
            ...props.features,
            taskMenu: false,
          }}
          dependenciesFeature={{
            allowCreate: false,
          }}
          dependencyEditFeature={false}
          taskEditFeature={getTaskEditorItems(
            emitNotesInput,
            saveTrigger,
            HRAListData,
            emitHraInputForAssign,
            addHRAsTrigger,
            TradepartnerList,
            emitTradepartnerInputForAssign,
            addTPTrigger,
            removeTPTrigger,
            emitTradepartnerInputForRemove,
            removeHRATrigger,
            emitHRAInputForRemove,
            props
          )}
          onAfterTaskEdit={(event: any) => {
            saveNotes(event);
            addTradePartners(event);
            addHRAs(event);
            removeTradePartner(event);
            removeHRA(event);
          }}
          listeners={{
            beforeTaskEditShow(event: any) {
              const { editor, taskRecord } = event;
              const widgetMap = editor.widgetMap;
              const TPCombo = widgetMap.tradePartnersTab.widgetMap.TPCombo;
              const HRACombo = widgetMap.HRATab.widgetMap.HRACombo;
              TPCombo.value = null;
              HRACombo.value = null;
              setSelectedActivity(taskRecord);
              loadCustomTabsData(editor, taskRecord, widgetMap, props);
            },
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore
            cellDblClick({ record }) {
              props?.scheduleInstance.showTaskEditDialog(record._data.guid);
            },
            beforePresetChange(event: {
              source: any;
              preset: { options: { startDate: any; endDate: any } };
            }) {
              const gantt = event.source;
              event.preset.options.startDate = gantt.project.startDate;
              event.preset.options.endDate = gantt.project.endDate;
            },
          }}
          selectionMode={props.selectionMode}
          displaySchedulingIssueResolutionPopup={false}
        />

The json data that we trying to use comes from mpxj and its larger that 30mb is there anyother way we can send you the sample data?

Please advice


Post by mats »

The json data that we trying to use comes from mpxj and its larger that 30mb is there anyother way we can send you the sample data?

Yes you could upload it to any file hosting service and post the link here then we can download it to debug.


Post by vconstruct »

https://drive.google.com/file/d/1yPsi50Yx9oYVEX0P2_h7AIUFRNwmzDmI/view?usp=sharing

this link has the file please let me know if you are facing challenges accessing the data


Post by alex.l »

Hi, JSON you posted is not valid and cannot be used in our demos. I am not sure what exactly is wrong, it contains 851k lines so it's hard to check all the file.

Screenshot 2023-05-31 at 11.22.05.png
Screenshot 2023-05-31 at 11.22.05.png (89.61 KiB) Viewed 394 times

but they are still being automatically forward scheduled.

So you see no difference if you added or not added manuallyScheduled flags for tasks?
Does it reproducible only when you use exactly attached set of data, or you see flag is ignored even if you used 2-5 tasks in a project?

All the best,
Alex


Post by vconstruct »

alex.l wrote: Wed May 31, 2023 11:32 am

Hi, JSON you posted is not valid and cannot be used in our demos. I am not sure what exactly is wrong, it contains 851k lines so it's hard to check all the file.
Screenshot 2023-05-31 at 11.22.05.png

but they are still being automatically forward scheduled.

So you see no difference if you added or not added manuallyScheduled flags for tasks?
Does it reproducible only when you use exactly attached set of data, or you see flag is ignored even if you used 2-5 tasks in a project?

My apologies i have uploaded a new dataset for you to test with,

https://drive.google.com/file/d/1S2AzfWFcnotwxph6tyBHf5d1hohgMB1q/view?usp=sharing

we use this data since we are have thoroughly tested it, please find below the tasks where we found the enddate in bryntum were not matching with what we had provided in the dataset

activity guid --> d018bebb-fbb3-7543-9d9d-e17d6b50d0db
activity name--> test124

activity guid --> 961e28fb-0dcc-6b46-955a-2a9bc7edc7ca
activity name--> test123

activity guid --> 474e2129-3c49-af49-b7be-dcead0f94f7c
activity name--> test256

activity guid --> 378bf5ac-ae1b-d540-babd-b88ce96379a6
activity name--> test789

activity guid --> c2f82f0f-e5f3-5941-b8bf-9bad298b0072
activity name--> test000


Post by alex.l »

While I am checking that, I want to clarify that manuallyScheduled allows to set manual startDate, but endDate will be calculated according to available time (calendars and other constraints). If you want to ignore project's calendar, you can set own calendar for task/resource with all time as working or any other configuration you want.

All the best,
Alex


Post by devsoftware@dpr.com »

What configurations do we need to apply at the project or task level to disable any enddate calculations and use the enddate from the data the way it comes.


Post Reply