DNorthrup
DNorthrup

Reputation: 847

Rails5 - Using Controller Helper to build Timeline Chart

I'm trying to build a timeline chart that resembles a Gantt Chart from softwares such as Jira - To do this I am trying to pass the Google Charts 'Timeline' Module 4 parameters from a helper. I was using ChartKick for this, but it could not fascilitate any of the optional parameters that the Timeline module allows.

Current Helper

  def get_all_tasks
    tasks = @project.tasks
    sorted = tasks.order(:task_start_date)
    sorted_task_array = []
    sorted.each do |s|
      sorted_task_array << [s.product, s.task_name, s.task_start_date, s.task_end_date]
    end
    sorted_task_array.to_s.gsub('"', "'")
  end

This will output an array such as

[[Product, Comment, Thu, 02 Feb 2017 00:00:00 UTC +00:00, Tue, 02 May 2017 00:00:00 UTC +00:00], ...] 

Minified for brevity, but the arrays are outputting the the Rails time format(my fault, I know).

So I modified it to be s.task_start_date.strftime("%F") and I have actually tried quite a few options on the strftime docs.

What's weird is I ALSO tried hard-coding the format that new Date() would have output.

AKA

> new Date(1789, 3, 30)
1789-04-30T04:00:00.000Z

And I still have 'invalid number' on the datetime field.

Hard-coding their example, or any example using new Date() seems to work, so what am I missing? I'm not sure how I could handle this without doubling the work in JS - When Rails can convert it.

Chart JS - Basic from the example

  google.charts.load("current", {packages:["timeline"]});
  google.charts.setOnLoadCallback(drawChart);
  function drawChart() {
    var container = document.getElementById('example4.2');
    var chart = new google.visualization.Timeline(container);
    var dataTable = new google.visualization.DataTable();

    dataTable.addColumn({ type: 'string', id: 'Role' });
    dataTable.addColumn({ type: 'string', id: 'Name' });
    dataTable.addColumn({ type: 'date', id: 'Start' });
    dataTable.addColumn({ type: 'date', id: 'End' });
    dataTable.addRows(
        <%= get_all_tasks %> 
        )
    var options = {
      timeline: { groupByRowLabel: false }
    };

    chart.draw(dataTable, options);
  }

Thoughts?

Update

I found out to get the time in the format I need, I would use s.task_end_date.iso8601 - However, I am still getting Uncaught SyntaxError: Invalid or unexpected token on the timing.

With this new error I'm thinking it may be due to my gsub that is removing the double quotes, but without removing the quotes I get an error on & that is output in the &quot; section of the JS.

Update 2 - Format

I realized my decimal was off, and after comparing the docs I saw I can append the amount of miliseconds to iso8601.

I now have s.task_start_date.iso8601(3) , s.task_end_date.iso8601(3) which is returning the same exact format as new Date(), but I am still getting a rejection due to :

Value 2017-02-02T00:00:00.000Z does not match type date in column index 2

Upvotes: 1

Views: 256

Answers (1)

DNorthrup
DNorthrup

Reputation: 847

To resolve this, I had to have JavaScript do the heavy lifting.

I used the helper to return the data in an array and used strftime to put the datetime as I wanted.

  def get_all_tasks
    tasks = @project.tasks
    sorted = tasks.order(:task_start_date)
    sorted_task_array = []
    sorted.each do |s|
      sorted_task_array << [s.product, s.task_name, s.task_start_date.strftime("%Y, %m, %d") , s.task_end_date.strftime("%Y, %m, %d") ]
    end
    sorted_task_array
  end

I then had JavaScript handle the wrapping.

var data = <%= raw get_all_tasks %>;
  var result = [];
  for(var i = 0; i < data.length; i++){
      var row = []
      for (var j = 0; j < 2; j++){
          row.push(data[i][j]);
      }
       for (j=2; j < 4; j++){
          row.push(new Date(data[i][j]));
      }
      result.push(row);
  }
  google.charts.load("current", {packages:["timeline"]});
  google.charts.setOnLoadCallback(drawChart);
  function drawChart() {
    var container = document.getElementById('example4.2');
    var chart = new google.visualization.Timeline(container);
    var dataTable = new google.visualization.DataTable();

    dataTable.addColumn({ type: 'string', id: 'Role' });
    dataTable.addColumn({ type: 'string', id: 'Name' });
    dataTable.addColumn({ type: 'date', id: 'Start' });
    dataTable.addColumn({ type: 'date', id: 'End' });
    dataTable.addRows(
        result 
        )
    var options = {
      timeline: { groupByRowLabel: false }
    };

    chart.draw(dataTable, options);
  }
</script>

The magic happens at the top, but it's working perfectly now.

Thanks to @max's comment in the question I have modified the helper to be much simpler:

  def get_all_tasks
    @project.tasks.order(:product, :task_start_date).pluck(:product, :task_name, :task_start_date, :task_end_date).to_json
  end

Upvotes: 1

Related Questions