Our state of the art Gantt chart


Post by amorimluc »

I used the php demo example and figured it out. So that's fixed. Now I'm trying to deliver that data from our load endpoint, but I can't get the calendar and calendar interval data to load. Is my formatting here correct?

def load
    begin
      tasks = Task.where(schedule_id: params[:schedule_id]).order(:parentIndex)
      dependencies = Dependency.where(schedule_id: params[:schedule_id])
      calendars = Calendar.where(schedule_id: params[:schedule_id])
      calendar_intervals = CalendarInterval.where(schedule_id: params[:schedule_id])
      resources = Resource.where(schedule_id: params[:schedule_id])
      assignments = Assignment.where(schedule_id: params[:schedule_id])
      time_ranges = TimeRange.where(schedule_id: params[:schedule_id])
      options = Option.all

  # calendar_intervals.each do |r|
  #   r[:type] = r[:typ]
  #   r.delete(:typ)
  # end
  # dependencies.each do |r|
  #   r[:type] = r[:typ]
  #   r.delete(:typ)
  # end

  # Group calendar intervals by calendar id
  intervals_by_calendar = calendar_intervals.group_by(&:calendar)

  # Create calendar rows with nested intervals
  calendar_rows = calendars.map do |calendar|
    {
      id: calendar.id,
      name: calendar.name,
      intervals: intervals_by_calendar[calendar.id]&.map do |interval|
        {
          id: interval.id,
          calendar: interval.calendar,
          type: interval.typ, # Ensure the field name matches your model
          startDate: interval.startDate,
          endDate: interval.endDate,
          isWorking: interval.isWorking,
          availability: interval.availability,
          recurrentStartDate: interval.recurrentStartDate,
          recurrentEndDate: interval.recurrentEndDate
        }
      end || []
    }
  end

  render json: {
    success: true,
    tasks: {
      rows: tasks
    },
    dependencies: {
      rows: dependencies
    },
    calendars: {
      rows: calendar_rows
    },
    resources: {
      rows: resources
    },
    assignments: {
      rows: assignments
    },
    time_ranges: {
      rows: time_ranges
    },
    options: {
      rows: options
    }
  }
rescue => e
  render json: {
    success: false,
    message: e.message
  }
end
  end

Post by marcio »

Hey amorimluc,

Glad that you got the other issue solved.

The format looks correct, what value are you getting in your calendarManagerStore? You can add a load event listener and see what information is being passed there https://bryntum.com/products/gantt/docs/api/Gantt/data/CalendarManagerStore#event-load.

Best regards,
Márcio


Post by amorimluc »

Yes, I checked project.calendarManagerStore.data in the project load event listener and the data seems to be correct. I'm attaching a screenshot.

Attachments
Screenshot 2024-08-07 at 4.09.52 PM.png
Screenshot 2024-08-07 at 4.09.52 PM.png (305.8 KiB) Viewed 71 times

Post by ghulam.ghous »

The data seems correct. Can you please check the calendar on project? To see if there is some calendar is being used or not. You can do this: project.calendar'. And please try setting thedifferent calendar ids` from your calendars data on project and see if that's working.

https://bryntum.com/products/gantt/docs/api/Gantt/model/ProjectModel#config-calendar


Post by amorimluc »

Yes project.calendar does have data, and it seems to be the same as project.calendarManagerStore.data. I'm really stumped with this.

Attachments
Screenshot 2024-08-08 at 11.03.29 AM.png
Screenshot 2024-08-08 at 11.03.29 AM.png (481.35 KiB) Viewed 67 times

Post by amorimluc »

Sorry, actually 'project.calendar' does NOT have data, it is null. Only project.calendars, plural, has the calendar data. Should project.calendar have data itself? What could cause it to be null?


Post by ghulam.ghous »

The only issue for this is that you are not setting the calendar on project. Can you please cross check that you have set the calendar on project? In our examples, we usually have this in our data.json:

  "project" : {
    "calendar"     : "general",
  },

This tells the project to use the calendar with general id from calendars data. As I have mentioned above, you can try setting the calendar from console and see if it is working.

You can do this:

bryntum.query('gantt').project.calendar = 2129 // 2129 is your first calendar id

https://bryntum.com/products/gantt/docs/api/Gantt/model/ProjectModel#config-calendar

Screen Recording 2024-08-08 at 4.07.43 PM.zip
(8.26 MiB) Downloaded 4 times
Screenshot 2024-08-08 at 4.08.41 PM.png
Screenshot 2024-08-08 at 4.08.41 PM.png (394.27 KiB) Viewed 65 times

Post by amorimluc »

I ran those commands but nothing happens. And when I check the calendar value after setting it like that it is still null. Would it be possible to do a quick Zoom so you can see the full context of our setup?


Post by ghulam.ghous »

Please share a runnable test case here that we should be able to run and debug. Hopping up on a call is not in the scope of the forum support.


Post by amorimluc »

Check our JS code below:

(We use the vanilla JS code for Bryntum Gantt)

var Gantt = window.bryntum.gantt.Gantt;
    var ProjectModel = window.bryntum.gantt.ProjectModel;
    var Calendar = window.bryntum.gantt.CalendarModel;
    var StateTrackingManager = window.bryntum.gantt.StateTrackingManager;
    var LocaleHelper = window.bryntum.gantt.LocaleHelper;
    var LocaleManager = window.bryntum.gantt.LocaleManager;
    var DateHelper = window.bryntum.gantt.DateHelper;
    var HasPercentDoneMixin = window.bryntum.gantt.HasPercentDoneMixin;
    class MyProjectModel extends HasPercentDoneMixin.derive(ProjectModel) {}

LocaleHelper.publishLocale(CONSLOG.bryntum_pt_br);
LocaleHelper.publishLocale(CONSLOG.bryntum_es);

//LocaleManager.throwOnMissingLocale = true;

if (CONSLOG.locale == "br")
  LocaleManager.locale = 'PtBr';

if (CONSLOG.locale == "es")
  LocaleManager.locale = 'Es';

// const calendar = new Calendar({
//   id: 'general',
//   intervals: [
//       {
//           recurrentStartDate: 'on Sat at 0:00',
//           recurrentEndDate: 'on Mon at 0:00',
//           isWorking: false
//       }
//   ]
// });

const project = new MyProjectModel({
  taskStore : {
    transformFlatData : true
  },
  // loadUrl: `/${CONSLOG.locale}/projects/${CONSLOG.project_id}/schedules/${schedule_id}/tasks/load`, // URL for data loading
  // syncUrl: `/${CONSLOG.locale}/projects/${CONSLOG.project_id}/schedules/${schedule_id}/tasks/sync`, // URL for data saving
  transport : {
    load : {
        url : `/${CONSLOG.locale}/projects/${CONSLOG.project_id}/schedules/${schedule_id}/tasks/load`, // URL for data loading
        requestConfig: {
          headers: {
            'X-CSRF-Token': csrfToken
          }
        }
    },
    sync : {
        url : `/${CONSLOG.locale}/projects/${CONSLOG.project_id}/schedules/${schedule_id}/tasks/sync`, // URL for data saving
        requestConfig: {
          headers: {
            'X-CSRF-Token': csrfToken
          }
        }
    }
  },
  autoLoad: true,
  autoSync: true,
  stm: new StateTrackingManager({
    autoRecord: true
  }),
  //calendar: 'general'
});

// project.calendarManagerStore.add(calendar);

// create the Bryntum Gantt instance and append to body
const gantt = new Gantt({
  appendTo: "gantt_here",
  dependencyIdField : 'wbsCode',
  selectionMode     : {
      cell       : true,
      dragSelect : true,
      rowNumber  : true
  },
  project,
  features : {
    excelExporter : {
        // Choose the date format for date fields
        dateFormat : 'YYYY-MM-DD HH:mm'
    },
    mspExport : true,
    //xerExport: true,
    taskMenu : {
      items : {
          // Add extra Export menu item available on all tasks
          exportToIcs : {
              icon   : 'b-fa b-fa-calendar-alt',
              text   : 'Add to Calendar (.ics)',
              weight : 900,
              onItem({ taskRecord }) {
                  taskRecord.exportToICS({
                      // Here you can add some custom ICS values (See https://tools.ietf.org/html/rfc5545 for more information)
                  });
              }
          }
      }
    },
    pdfExport : {
      exportServer: 'https://dev.bryntum.com:8082',
      // Required for font-awesome icons to display correctly
      
      // headerTpl,
      // footerTpl
    },
    rowReorder : {
      showGrip : true
    },
    nonWorkingTime : true
  },
  columns: [
    { type: 'wbs', hidden: true },
    { type: 'name', width: 250, text: CONSLOG.T.activity[CONSLOG.locale], showWbs: true},
    { type: 'startdate', width: 50 },
    { type: 'enddate', width: 50 },
    { type: 'duration', width: 50 },
    { type: 'resourceassignment', width: 100, text: CONSLOG.T.resources[CONSLOG.locale] },
    { text: CONSLOG.T.percent_done[CONSLOG.locale], field: 'percentDone', type : 'percentdone', showCircle : true, width : CONSLOG.locale != 'en' ? 75 : 50, sortable: false, group: false, editor: false},
  ],
  rowHeight: 35,
  barMargin: 5,
});

// Function to update the percentage complete
const updatePercentageComplete = () => {
  const tasks = project.taskStore;
  if (tasks && tasks.count) { // Ensure tasks are loaded
      const totalPercentageComplete = project.percentDone;
      if (!isNaN(totalPercentageComplete)) {
          document.querySelector('.list-group-item.schedules .counter').textContent = Math.floor(totalPercentageComplete) + '%';
      }
  }
};

// Enable STM after data is loaded
project.on('load', () => {
  project.stm.enable();

  // const calendarData = project.calendarManagerStore.data; // Check this to debug
  // console.log('Calendar Data:', calendarData);
  // console.log('Project Data:', project.data);
  debugger

  // Update percentage after task change or data change
  //gantt.on('taskchanged', updatePercentageComplete);
  gantt.on('taskchanged', () => {
    console.log('Task changed, attempting to sync...');
    updatePercentageComplete();
    project.sync().then(response => {
      if (response.success) {
        console.log('Task synced successfully');
      } else {
        console.error('Failed to sync task');
      }
    }).catch(error => {
      console.error('Error syncing task:', error);
    });
  });
  gantt.on('datachange', updatePercentageComplete);
});

// Update percentage complete after data import
project.on('dataReady', updatePercentageComplete);
 

And this is our Rails /load endpoint code:

def load
    begin
      tasks = Task.where(schedule_id: params[:schedule_id]).order(:parentIndex)
      dependencies = Dependency.where(schedule_id: params[:schedule_id])
      calendars = Calendar.where(schedule_id: params[:schedule_id])
      calendar_intervals = CalendarInterval.where(schedule_id: params[:schedule_id])
      resources = Resource.where(schedule_id: params[:schedule_id])
      assignments = Assignment.where(schedule_id: params[:schedule_id])
      time_ranges = TimeRange.where(schedule_id: params[:schedule_id])
      options = Option.all

  # calendar_intervals.each do |r|
  #   r[:type] = r[:typ]
  #   r.delete(:typ)
  # end
  # dependencies.each do |r|
  #   r[:type] = r[:typ]
  #   r.delete(:typ)
  # end

  # Group calendar intervals by calendar id
  intervals_by_calendar = calendar_intervals.group_by(&:calendar)

  # Create calendar rows with nested intervals
  calendar_rows = calendars.map do |calendar|
    {
      id: calendar.id,
      name: calendar.name,
      intervals: intervals_by_calendar[calendar.id]&.map do |interval|
        {
          calendar: interval.calendar,
          isWorking: interval.isWorking,
          recurrentStartDate: interval.recurrentStartDate,
          recurrentEndDate: interval.recurrentEndDate
        }
      end || []
    }
  end

  render json: {
    success: true,
    tasks: {
      rows: tasks
    },
    dependencies: {
      rows: dependencies
    },
    calendars: {
      rows: calendar_rows
    },
    resources: {
      rows: resources
    },
    assignments: {
      rows: assignments
    },
    time_ranges: {
      rows: time_ranges
    },
    options: {
      rows: options
    }
  }
rescue => e
  render json: {
    success: false,
    message: e.message
  }
end
  end

We've already confirmed that after loading, project.calendarManagerStore.data does in fact have the calendar data. But project.calendar is null, and like I said it stays null even after trying to set it with bryntum.query('gantt').project.calendar = 2129

We're at a loss and need to get this working


Post Reply