##// END OF EJS Templates
Gantt progress lines (#12122)....
Jean-Philippe Lang -
r10980:237f297f03e2
parent child
Show More
@@ -12,6 +12,39
12 <%= render :partial => 'queries/filters', :locals => {:query => @query} %>
12 <%= render :partial => 'queries/filters', :locals => {:query => @query} %>
13 </div>
13 </div>
14 </fieldset>
14 </fieldset>
15 <fieldset id="filters" class="collapsible">
16 <legend onclick="toggleFieldset(this);"><%= l(:label_display) %></legend>
17 <div>
18 <table>
19 <tr>
20 <td>
21 <fieldset>
22 <legend><%= l(:label_related_issues) %></legend>
23 <label>
24 <%= check_box_tag "draw_rels", params["draw_rels"], true %>
25 <% rels = [IssueRelation::TYPE_BLOCKS, IssueRelation::TYPE_PRECEDES] %>
26 <% rels.each do |rel| %>
27 <% color = Redmine::Helpers::Gantt::DRAW_TYPES[rel][:color] %>
28 <%= content_tag(:span, '&nbsp;&nbsp;&nbsp;'.html_safe,
29 :style => "background-color: #{color}") %>
30 <%= l(IssueRelation::TYPES[rel][:name]) %>
31 <% end %>
32 </label>
33 </fieldset>
34 </td>
35 <td>
36 <fieldset>
37 <legend><%= l(:label_gantt_progress_line) %></legend>
38 <label>
39 <%= check_box_tag "draw_progress_line", params[:draw_progress_line], false %>
40 <%= l(:label_display) %>
41 </label>
42 </fieldset>
43 </td>
44 </tr>
45 </table>
46 </div>
47 </fieldset>
15
48
16 <p class="contextual">
49 <p class="contextual">
17 <%= gantt_zoom_link(@gantt, :in) %>
50 <%= gantt_zoom_link(@gantt, :in) %>
@@ -229,7 +262,7
229 style += "width:10px;"
262 style += "width:10px;"
230 style += "border-left: 1px dashed red;"
263 style += "border-left: 1px dashed red;"
231 %>
264 %>
232 <%= content_tag(:div, '&nbsp;'.html_safe, :style => style) %>
265 <%= content_tag(:div, '&nbsp;'.html_safe, :style => style, :id => 'today_line') %>
233 <% end %>
266 <% end %>
234 <%
267 <%
235 style = ""
268 style = ""
@@ -279,4 +312,8
279 var issue_relation_type = <%= raw Redmine::Helpers::Gantt::DRAW_TYPES.to_json %>;
312 var issue_relation_type = <%= raw Redmine::Helpers::Gantt::DRAW_TYPES.to_json %>;
280 $(document).ready(drawGanttHandler);
313 $(document).ready(drawGanttHandler);
281 $(window).resize(drawGanttHandler);
314 $(window).resize(drawGanttHandler);
315 $(function() {
316 $("#draw_rels").change(drawGanttHandler);
317 $("#draw_progress_line").change(drawGanttHandler);
318 });
282 <% end %>
319 <% end %>
@@ -886,6 +886,7 en:
886 label_cross_project_tree: With project tree
886 label_cross_project_tree: With project tree
887 label_cross_project_hierarchy: With project hierarchy
887 label_cross_project_hierarchy: With project hierarchy
888 label_cross_project_system: With all projects
888 label_cross_project_system: With all projects
889 label_gantt_progress_line: Progress line
889
890
890 button_login: Login
891 button_login: Login
891 button_submit: Submit
892 button_submit: Submit
@@ -833,6 +833,7 ja:
833 label_git_report_last_commit: ファイルとディレクトリの最新コミットを表示する
833 label_git_report_last_commit: ファイルとディレクトリの最新コミットを表示する
834 label_parent_revision:
834 label_parent_revision:
835 label_child_revision:
835 label_child_revision:
836 label_gantt_progress_line: イナズマ線
836
837
837 button_login: ログイン
838 button_login: ログイン
838 button_submit: 送信
839 button_submit: 送信
@@ -308,10 +308,18 module Redmine
308 html_class << 'icon icon-package '
308 html_class << 'icon icon-package '
309 html_class << (version.behind_schedule? ? 'version-behind-schedule' : '') << " "
309 html_class << (version.behind_schedule? ? 'version-behind-schedule' : '') << " "
310 html_class << (version.overdue? ? 'version-overdue' : '')
310 html_class << (version.overdue? ? 'version-overdue' : '')
311 html_class << ' version-closed' unless version.open?
312 if version.start_date && version.due_date && version.completed_pourcent
313 progress_date = calc_progress_date(version.start_date,
314 version.due_date, version.completed_pourcent)
315 html_class << ' behind-start-date' if progress_date < self.date_from
316 html_class << ' over-end-date' if progress_date > self.date_to
317 end
311 s = view.link_to_version(version).html_safe
318 s = view.link_to_version(version).html_safe
312 subject = view.content_tag(:span, s,
319 subject = view.content_tag(:span, s,
313 :class => html_class).html_safe
320 :class => html_class).html_safe
314 html_subject(options, subject, :css => "version-name")
321 html_subject(options, subject, :css => "version-name",
322 :id => "version-#{version.id}")
315 when :image
323 when :image
316 image_subject(options, version.to_s_with_project)
324 image_subject(options, version.to_s_with_project)
317 when :pdf
325 when :pdf
@@ -332,7 +340,8 module Redmine
332 label = h("#{version.project} -") + label unless @project && @project == version.project
340 label = h("#{version.project} -") + label unless @project && @project == version.project
333 case options[:format]
341 case options[:format]
334 when :html
342 when :html
335 html_task(options, coords, :css => "version task", :label => label, :markers => true)
343 html_task(options, coords, :css => "version task",
344 :label => label, :markers => true, :version => version)
336 when :image
345 when :image
337 image_task(options, coords, :label => label, :markers => true, :height => 3)
346 image_task(options, coords, :label => label, :markers => true, :height => 3)
338 when :pdf
347 when :pdf
@@ -354,6 +363,13 module Redmine
354 css_classes << ' issue-overdue' if issue.overdue?
363 css_classes << ' issue-overdue' if issue.overdue?
355 css_classes << ' issue-behind-schedule' if issue.behind_schedule?
364 css_classes << ' issue-behind-schedule' if issue.behind_schedule?
356 css_classes << ' icon icon-issue' unless Setting.gravatar_enabled? && issue.assigned_to
365 css_classes << ' icon icon-issue' unless Setting.gravatar_enabled? && issue.assigned_to
366 css_classes << ' issue-closed' if issue.closed?
367 if issue.start_date && issue.due_before && issue.done_ratio
368 progress_date = calc_progress_date(issue.start_date,
369 issue.due_before, issue.done_ratio)
370 css_classes << ' behind-start-date' if progress_date < self.date_from
371 css_classes << ' over-end-date' if progress_date > self.date_to
372 end
357 s = "".html_safe
373 s = "".html_safe
358 if issue.assigned_to.present?
374 if issue.assigned_to.present?
359 assigned_string = l(:field_assigned_to) + ": " + issue.assigned_to.name
375 assigned_string = l(:field_assigned_to) + ": " + issue.assigned_to.name
@@ -365,7 +381,7 module Redmine
365 s << view.link_to_issue(issue).html_safe
381 s << view.link_to_issue(issue).html_safe
366 subject = view.content_tag(:span, s, :class => css_classes).html_safe
382 subject = view.content_tag(:span, s, :class => css_classes).html_safe
367 html_subject(options, subject, :css => "issue-subject",
383 html_subject(options, subject, :css => "issue-subject",
368 :title => issue.subject) + "\n"
384 :title => issue.subject, :id => "issue-#{issue.id}") + "\n"
369 when :image
385 when :image
370 image_subject(options, issue.subject)
386 image_subject(options, issue.subject)
371 when :pdf
387 when :pdf
@@ -628,7 +644,7 module Redmine
628 coords[:bar_end] = self.date_to - self.date_from + 1
644 coords[:bar_end] = self.date_to - self.date_from + 1
629 end
645 end
630 if progress
646 if progress
631 progress_date = start_date + (end_date - start_date + 1) * (progress / 100.0)
647 progress_date = calc_progress_date(start_date, end_date, progress)
632 if progress_date > self.date_from && progress_date > start_date
648 if progress_date > self.date_from && progress_date > start_date
633 if progress_date < self.date_to
649 if progress_date < self.date_to
634 coords[:bar_progress_end] = progress_date - self.date_from
650 coords[:bar_progress_end] = progress_date - self.date_from
@@ -655,6 +671,10 module Redmine
655 coords
671 coords
656 end
672 end
657
673
674 def calc_progress_date(start_date, end_date, progress)
675 start_date + (end_date - start_date + 1) * (progress / 100.0)
676 end
677
658 # Sorts a collection of issues by start_date, due_date, id for gantt rendering
678 # Sorts a collection of issues by start_date, due_date, id for gantt rendering
659 def sort_issues!(issues)
679 def sort_issues!(issues)
660 issues.sort! { |a, b| gantt_issue_compare(a, b) }
680 issues.sort! { |a, b| gantt_issue_compare(a, b) }
@@ -695,9 +715,10 module Redmine
695 def html_subject(params, subject, options={})
715 def html_subject(params, subject, options={})
696 style = "position: absolute;top:#{params[:top]}px;left:#{params[:indent]}px;"
716 style = "position: absolute;top:#{params[:top]}px;left:#{params[:indent]}px;"
697 style << "width:#{params[:subject_width] - params[:indent]}px;" if params[:subject_width]
717 style << "width:#{params[:subject_width] - params[:indent]}px;" if params[:subject_width]
698 output = view.content_tag('div', subject,
718 output = view.content_tag(:div, subject,
699 :class => options[:css], :style => style,
719 :class => options[:css], :style => style,
700 :title => options[:title])
720 :title => options[:title],
721 :id => options[:id])
701 @subjects << output
722 @subjects << output
702 output
723 output
703 end
724 end
@@ -742,6 +763,7 module Redmine
742 style << "left:#{coords[:bar_start]}px;"
763 style << "left:#{coords[:bar_start]}px;"
743 style << "width:#{width}px;"
764 style << "width:#{width}px;"
744 html_id = "task-todo-issue-#{options[:issue].id}" if options[:issue]
765 html_id = "task-todo-issue-#{options[:issue].id}" if options[:issue]
766 html_id = "task-todo-version-#{options[:version].id}" if options[:version]
745 content_opt = {:style => style,
767 content_opt = {:style => style,
746 :class => "#{options[:css]} task_todo",
768 :class => "#{options[:css]} task_todo",
747 :id => html_id}
769 :id => html_id}
@@ -768,9 +790,12 module Redmine
768 style << "top:#{params[:top]}px;"
790 style << "top:#{params[:top]}px;"
769 style << "left:#{coords[:bar_start]}px;"
791 style << "left:#{coords[:bar_start]}px;"
770 style << "width:#{width}px;"
792 style << "width:#{width}px;"
793 html_id = "task-done-issue-#{options[:issue].id}" if options[:issue]
794 html_id = "task-done-version-#{options[:version].id}" if options[:version]
771 output << view.content_tag(:div, '&nbsp;'.html_safe,
795 output << view.content_tag(:div, '&nbsp;'.html_safe,
772 :style => style,
796 :style => style,
773 :class => "#{options[:css]} task_done")
797 :class => "#{options[:css]} task_done",
798 :id => html_id)
774 end
799 end
775 end
800 end
776 # Renders the markers
801 # Renders the markers
@@ -98,6 +98,66 function drawRelations() {
98 });
98 });
99 }
99 }
100
100
101 function getProgressLinesArray() {
102 var arr = new Array();
103 var today_left = $('#today_line').position().left;
104 arr.push({left: today_left, top: 0});
105 $.each($('div.issue-subject, div.version-name'), function(index, element) {
106 var t = $(element).position().top - draw_top ;
107 var h = ($(element).height() / 9);
108 var element_top_upper = t - h;
109 var element_top_center = t + (h * 3);
110 var element_top_lower = t + (h * 8);
111 var issue_closed = $(element).children('span').hasClass('issue-closed');
112 var version_closed = $(element).children('span').hasClass('version-closed');
113 if (issue_closed || version_closed) {
114 arr.push({left: today_left, top: element_top_center});
115 } else {
116 var issue_done = $("#task-done-" + $(element).attr("id"));
117 var is_behind_start = $(element).children('span').hasClass('behind-start-date');
118 var is_over_end = $(element).children('span').hasClass('over-end-date');
119 if (is_over_end) {
120 arr.push({left: draw_right, top: element_top_upper, is_right_edge: true});
121 arr.push({left: draw_right, top: element_top_lower, is_right_edge: true, none_stroke: true});
122 } else if (issue_done.size() > 0) {
123 var done_left = issue_done.first().position().left +
124 issue_done.first().width();
125 arr.push({left: done_left, top: element_top_center});
126 } else if (is_behind_start) {
127 arr.push({left: 0 , top: element_top_upper, is_left_edge: true});
128 arr.push({left: 0 , top: element_top_lower, is_left_edge: true, none_stroke: true});
129 } else {
130 var todo_left = today_left;
131 var issue_todo = $("#task-todo-" + $(element).attr("id"));
132 if (issue_todo.size() > 0){
133 todo_left = issue_todo.first().position().left;
134 }
135 arr.push({left: Math.min(today_left, todo_left), top: element_top_center});
136 }
137 }
138 });
139 return arr;
140 }
141
142 function drawGanttProgressLines() {
143 var arr = getProgressLinesArray();
144 var color = $("#today_line")
145 .css("border-left-color");
146 var i;
147 for(i = 1 ; i < arr.length ; i++) {
148 if (!("none_stroke" in arr[i]) &&
149 (!("is_right_edge" in arr[i - 1] && "is_right_edge" in arr[i]) &&
150 !("is_left_edge" in arr[i - 1] && "is_left_edge" in arr[i]))
151 ) {
152 var x1 = (arr[i - 1].left == 0) ? 0 : arr[i - 1].left + draw_left;
153 var x2 = (arr[i].left == 0) ? 0 : arr[i].left + draw_left;
154 draw_gantt.path(["M", x1, arr[i - 1].top,
155 "L", x2, arr[i].top])
156 .attr({stroke: color, "stroke-width": 2});
157 }
158 }
159 }
160
101 function drawGanttHandler() {
161 function drawGanttHandler() {
102 var folder = document.getElementById('gantt_draw_area');
162 var folder = document.getElementById('gantt_draw_area');
103 if(draw_gantt != null)
163 if(draw_gantt != null)
@@ -105,5 +165,8 function drawGanttHandler() {
105 else
165 else
106 draw_gantt = Raphael(folder);
166 draw_gantt = Raphael(folder);
107 setDrawArea();
167 setDrawArea();
108 drawRelations();
168 if ($("#draw_progress_line").attr('checked'))
169 drawGanttProgressLines();
170 if ($("#draw_rels").attr('checked'))
171 drawRelations();
109 }
172 }
General Comments 0
You need to be logged in to leave comments. Login now