Feature #2024 » gantt_edit_r3764.patch
| app/controllers/gantts_controller.rb (working copy) | ||
|---|---|---|
| 1 | 1 |
class GanttsController < ApplicationController |
| 2 | 2 |
before_filter :find_optional_project |
| 3 |
before_filter :find_issue, :only => [:edit_gantt] |
|
| 3 | 4 | |
| 4 | 5 |
rescue_from Query::StatementInvalid, :with => :query_statement_invalid |
| 5 | 6 | |
| ... | ... | |
| 10 | 11 |
helper :sort |
| 11 | 12 |
include SortHelper |
| 12 | 13 |
include Redmine::Export::PDF |
| 14 |
include IssuesHelper |
|
| 13 | 15 |
|
| 14 | 16 |
def show |
| 15 | 17 |
@gantt = Redmine::Helpers::Gantt.new(params) |
| ... | ... | |
| 42 | 44 |
end |
| 43 | 45 |
end |
| 44 | 46 | |
| 47 |
def find_optional_project |
|
| 48 |
@project = Project.find(params[:project_id]) unless params[:project_id].blank? |
|
| 49 |
if params[:action] == "edit_gantt" |
|
| 50 |
allowed = User.current.allowed_to?({:controller => 'issues', :action => 'edit'}, @project, :global => true)
|
|
| 51 |
else |
|
| 52 |
allowed = User.current.allowed_to?({:controller => params[:controller], :action => params[:action]}, @project, :global => true)
|
|
| 53 |
end |
|
| 54 |
allowed ? true : deny_access |
|
| 55 |
rescue ActiveRecord::RecordNotFound |
|
| 56 |
render_404 |
|
| 57 |
end |
|
| 58 | ||
| 59 |
def edit_gantt |
|
| 60 |
if !@issue.start_date || !@issue.due_before |
|
| 61 |
render :text=>l(:notice_locking_conflict), :status=>400 |
|
| 62 |
return |
|
| 63 |
end |
|
| 64 |
@issue.init_journal(User.current) |
|
| 65 |
date_from = Date.parse(params[:date_from]) |
|
| 66 |
old_start_date = @issue.start_date |
|
| 67 |
o = get_position(@issue, date_from, Date.parse(params[:date_to]), params[:zoom]) |
|
| 68 |
text_for_revert = "#{@issue.id}=#{format_date(@issue.start_date)},#{@issue.start_date},#{format_date(@issue.due_before)},#{@issue.due_before},#{o[0]},#{o[1]},#{o[2]},#{o[3]}"
|
|
| 69 | ||
| 70 |
if params[:day] |
|
| 71 |
#bar moved |
|
| 72 |
day = params[:day].to_i |
|
| 73 |
duration = @issue.due_before - @issue.start_date |
|
| 74 |
@issue.start_date = date_from + day |
|
| 75 |
@issue.due_date = @issue.start_date + duration.to_i if @issue.due_date |
|
| 76 |
elsif params[:start_date] |
|
| 77 |
#start date changed |
|
| 78 |
start_date = Date.parse(params[:start_date]) |
|
| 79 |
if @issue.start_date == start_date |
|
| 80 |
render :text=>"" |
|
| 81 |
return #nothing has changed |
|
| 82 |
end |
|
| 83 |
@issue.start_date = start_date |
|
| 84 |
@issue.due_date = start_date if @issue.due_date && start_date > @issue.due_date |
|
| 85 |
elsif params[:due_date] |
|
| 86 |
#due date changed |
|
| 87 |
due_date = Date.parse(params[:due_date]) |
|
| 88 |
if @issue.due_date == due_date |
|
| 89 |
render :text=>"" |
|
| 90 |
return #nothing has changed |
|
| 91 |
end |
|
| 92 |
@issue.due_date = due_date |
|
| 93 |
@issue.start_date = due_date if due_date < @issue.start_date |
|
| 94 |
end |
|
| 95 |
fv = @issue.fixed_version |
|
| 96 |
if fv && fv.effective_date && !@issue.due_date && fv.effective_date < @issue.start_date |
|
| 97 |
@issue.start_date = old_start_date |
|
| 98 |
end |
|
| 99 | ||
| 100 |
begin |
|
| 101 |
@issue.save! |
|
| 102 |
o = get_position(@issue, date_from, Date.parse(params[:date_to]), params[:zoom]) |
|
| 103 |
text = "#{@issue.id}=#{format_date(@issue.start_date)},#{@issue.start_date},#{format_date(@issue.due_before)},#{@issue.due_before},#{o[0]},#{o[1]},#{o[2]},#{o[3]}"
|
|
| 104 | ||
| 105 |
#check dependencies |
|
| 106 |
issues = @issue.all_precedes_issues |
|
| 107 |
issues.each do |i| |
|
| 108 |
o = get_position(i, date_from, Date.parse(params[:date_to]), params[:zoom]) |
|
| 109 |
text += "|#{i.id}=#{format_date(i.start_date)},#{i.start_date},#{format_date(i.due_before)},#{i.due_before},#{o[0]},#{o[1]},#{o[2]},#{o[3]}"
|
|
| 110 |
end |
|
| 111 |
render :text=>text |
|
| 112 |
rescue |
|
| 113 |
render :text=>@issue.errors.full_messages.join("\n") + "|" + text_for_revert , :status=>400
|
|
| 114 |
end |
|
| 115 |
end |
|
| 116 | ||
| 117 |
private |
|
| 118 |
def find_issue |
|
| 119 |
@issue = Issue.find(params[:id], :include => [:project, :tracker, :status, :author, :priority, :category]) |
|
| 120 |
@project = @issue.project |
|
| 121 |
rescue ActiveRecord::RecordNotFound |
|
| 122 |
render_404 |
|
| 123 |
end |
|
| 124 | ||
| 45 | 125 |
end |
| app/helpers/issues_helper.rb (working copy) | ||
|---|---|---|
| 36 | 36 |
@cached_label_priority ||= l(:field_priority) |
| 37 | 37 |
|
| 38 | 38 |
link_to_issue(issue) + "<br /><br />" + |
| 39 |
"<strong>#{@cached_label_start_date}</strong>: #{format_date(issue.start_date)}<br />" +
|
|
| 40 |
"<strong>#{@cached_label_due_date}</strong>: #{format_date(issue.due_date)}<br />" +
|
|
| 39 |
"<strong>#{@cached_label_start_date}</strong>: <span id='tooltip_start_date_#{issue.id}'>#{format_date(issue.start_date)}</span><br />" +
|
|
| 40 |
"<strong>#{@cached_label_due_date}</strong>: <span id='tooltip_due_date_#{issue.id}'>#{format_date(issue.due_date)}</span><br />" +
|
|
| 41 | 41 |
"<strong>#{@cached_label_assigned_to}</strong>: #{issue.assigned_to}<br />" +
|
| 42 | 42 |
"<strong>#{@cached_label_priority}</strong>: #{issue.priority.name}"
|
| 43 | 43 |
end |
| ... | ... | |
| 259 | 259 |
end |
| 260 | 260 |
end |
| 261 | 261 |
end |
| 262 | ||
| 263 |
def get_position(event, date_from, date_to, zoom_str) |
|
| 264 |
zoom = zoom_str.to_i |
|
| 265 |
i_start_date = (event.start_date >= date_from ? event.start_date : date_from ) |
|
| 266 |
i_end_date = (event.due_before <= date_to ? event.due_before : date_to ) |
|
| 267 | ||
| 268 |
i_done_date = event.start_date + ((event.due_before - event.start_date+1)*event.done_ratio/100).floor |
|
| 269 |
i_done_date = (i_done_date <= date_from ? date_from : i_done_date ) |
|
| 270 |
i_done_date = (i_done_date >= date_to ? date_to : i_done_date ) |
|
| 271 | ||
| 272 |
i_late_date = [i_end_date, Date.today].min if i_start_date <= Date.today |
|
| 273 | ||
| 274 |
i_left = ((i_start_date - date_from)*zoom).floor |
|
| 275 |
i_left = 0 if i_left < 0 |
|
| 276 |
i_width = ((i_end_date - i_start_date + 1)*zoom).floor - 2 # total width of the issue (- 2 for left and right borders) |
|
| 277 |
i_width = 0 if i_width < 0 |
|
| 278 |
d_width = ((i_done_date - i_start_date)*zoom).floor - 2 # done width |
|
| 279 |
d_width = 0 if d_width < 0 |
|
| 280 |
l_width = i_late_date ? ((i_late_date - i_start_date+1)*zoom).floor - 2 : 0 # delay width |
|
| 281 |
l_width = 0 if l_width < 0 |
|
| 282 |
return i_left, i_width, l_width, d_width |
|
| 283 |
end |
|
| 262 | 284 |
end |
| app/models/issue.rb (working copy) | ||
|---|---|---|
| 415 | 415 |
end |
| 416 | 416 |
dependencies |
| 417 | 417 |
end |
| 418 | ||
| 419 |
def all_precedes_issues |
|
| 420 |
dependencies = [] |
|
| 421 |
relations_from.each do |relation| |
|
| 422 |
next unless relation.relation_type == IssueRelation::TYPE_PRECEDES |
|
| 423 |
dependencies << relation.issue_to |
|
| 424 |
dependencies += relation.issue_to.all_dependent_issues |
|
| 425 |
end |
|
| 426 |
dependencies |
|
| 427 |
end |
|
| 418 | 428 |
|
| 419 | 429 |
# Returns an array of issues that duplicate this one |
| 420 | 430 |
def duplicates |
| app/views/gantts/show.html.erb (working copy) | ||
|---|---|---|
| 1 |
<% include_calendar_headers_tags %> |
|
| 1 | 2 |
<h2><%= l(:label_gantt) %></h2> |
| 2 | 3 | |
| 3 | 4 |
<% form_tag(params.merge(:month => nil, :year => nil, :months => nil), :id => 'query_form') do %> |
| ... | ... | |
| 38 | 39 |
<% zoom = 1 |
| 39 | 40 |
@gantt.zoom.times { zoom = zoom * 2 }
|
| 40 | 41 | |
| 41 |
subject_width = 330
|
|
| 42 |
subject_width = 280
|
|
| 42 | 43 |
header_heigth = 18 |
| 43 | 44 | |
| 44 | 45 |
headers_height = header_heigth |
| ... | ... | |
| 58 | 59 |
g_height = [(20 * @gantt.events.length + 6)+150, 206].max |
| 59 | 60 |
t_height = g_height + headers_height |
| 60 | 61 |
%> |
| 62 |
<input type="hidden" name="_date_from" id="i_date_from" value="<%=h(@gantt.date_from)%>" /> |
|
| 63 |
<input type="hidden" name="_date_to" id="i_date_to" value="<%=h(@gantt.date_to)%>" /> |
|
| 64 |
<input type="hidden" name="zm" id="i_zm" value="<%=h(zoom)%>" /> |
|
| 65 |
<script type='text/javascript'> |
|
| 66 |
function issue_moved(elem) {
|
|
| 67 |
var id_str = elem.id.substring(3, elem.id.length); |
|
| 68 |
var v_date_from = document.getElementById('i_date_from').getAttribute("value");
|
|
| 69 |
var v_date_to = document.getElementById('i_date_to').getAttribute("value");
|
|
| 70 |
var v_zm = document.getElementById('i_zm').getAttribute("value");
|
|
| 71 |
var url_str = '/issues/edit_gantt/' + id_str; |
|
| 72 |
var day = parseInt(elem.style.left)/parseInt(v_zm); |
|
| 73 |
new Ajax.Request(url_str, {asynchronous:true, evalScripts:true,
|
|
| 74 |
parameters: 'day='+day+'&date_from='+v_date_from+'&date_to='+v_date_to+'&zoom='+v_zm, |
|
| 75 |
onSuccess:function(obj) {
|
|
| 76 |
change_dates(obj.responseText); |
|
| 77 |
}, |
|
| 78 |
onFailure:function(obj) {
|
|
| 79 |
handle_failure(obj.responseText); |
|
| 80 |
} |
|
| 81 |
}); |
|
| 61 | 82 | |
| 83 |
} |
|
| 84 |
|
|
| 85 |
function handle_failure(res_text) {
|
|
| 86 |
var text = res_text.split('|');
|
|
| 87 |
alert(text[0]); |
|
| 88 |
if (text.length == 1) {
|
|
| 89 |
return; |
|
| 90 |
} |
|
| 91 |
change_dates(text[1]);//revert |
|
| 92 |
} |
|
| 93 | ||
| 94 |
function change_dates(issue_infos) {
|
|
| 95 |
if (!issue_infos) {
|
|
| 96 |
return; |
|
| 97 |
} |
|
| 98 |
var issue_list = issue_infos.split("|");
|
|
| 99 |
for (i = 0; i < issue_list.length; i++) {
|
|
| 100 |
change_date(issue_list[i]); |
|
| 101 |
} |
|
| 102 |
} |
|
| 103 | ||
| 104 |
function change_date(text) {
|
|
| 105 |
if (!text) {
|
|
| 106 |
return; |
|
| 107 |
} |
|
| 108 |
var issue_info = text.split("=");
|
|
| 109 |
var elem_id = issue_info[0]; |
|
| 110 |
var vals = issue_info[1].split(',');
|
|
| 111 |
var start_date_elem = document.getElementById(elem_id + '_start_date_str'); |
|
| 112 |
if (!start_date_elem) {
|
|
| 113 |
//target not exists |
|
| 114 |
return; |
|
| 115 |
} |
|
| 116 |
start_date_elem.innerHTML = vals[0]; |
|
| 117 |
var tooltip_start_date_elem = document.getElementById('tooltip_start_date_' + elem_id);
|
|
| 118 |
tooltip_start_date_elem.innerHTML = vals[0]; |
|
| 119 |
var due_date_elem = document.getElementById(elem_id + '_due_date_str'); |
|
| 120 |
due_date_elem.innerHTML = vals[2]; |
|
| 121 |
var tooltip_due_date_elem = document.getElementById('tooltip_due_date_' + elem_id);
|
|
| 122 |
tooltip_due_date_elem.innerHTML = vals[2]; |
|
| 123 | ||
| 124 |
var ev_elem = document.getElementById('ev_' + elem_id);
|
|
| 125 |
ev_elem.style.left = vals[4] + 'px'; |
|
| 126 |
ev_elem.style.width = (parseInt(vals[5])+100)+'px'; |
|
| 127 |
var todo_elem = document.getElementById('task_todo_' + elem_id);
|
|
| 128 |
todo_elem.style.width = vals[5] + 'px'; |
|
| 129 |
var late_elem = document.getElementById('task_late_' + elem_id);
|
|
| 130 |
if (late_elem) {
|
|
| 131 |
late_elem.style.width = vals[6] + 'px'; |
|
| 132 |
if (vals[6] == '0') {
|
|
| 133 |
late_elem.className = 'task task_none'; |
|
| 134 |
} else {
|
|
| 135 |
late_elem.className = 'task task_late'; |
|
| 136 |
} |
|
| 137 |
} |
|
| 138 |
var done_elem = document.getElementById('task_done_' + elem_id);
|
|
| 139 |
if (done_elem) {
|
|
| 140 |
done_elem.style.width = vals[7] + 'px'; |
|
| 141 |
if (vals[7] == '0') {
|
|
| 142 |
done_elem.className = 'task task_none'; |
|
| 143 |
} else {
|
|
| 144 |
done_elem.className = 'task task_done'; |
|
| 145 |
} |
|
| 146 |
} |
|
| 147 |
var task_elem = document.getElementById('task_task_' + elem_id);
|
|
| 148 |
if (task_elem) {
|
|
| 149 |
task_elem.style.left = (parseInt(vals[5])+5) + 'px'; |
|
| 150 |
} |
|
| 151 |
var tooltip = document.getElementById("tt_" + elem_id);
|
|
| 152 |
tooltip.style.left = ev_elem.style.left; |
|
| 153 | ||
| 154 |
//change calendar date |
|
| 155 |
document.getElementById(elem_id+"_hidden_start_date").value = vals[1]; |
|
| 156 |
document.getElementById(elem_id+"_start_date").value = vals[1]; |
|
| 157 |
document.getElementById(elem_id+"_hidden_due_date").value = vals[3]; |
|
| 158 |
document.getElementById(elem_id+"_due_date").value = vals[3]; |
|
| 159 |
} |
|
| 160 |
</script> |
|
| 161 | ||
| 62 | 162 |
<table width="100%" style="border:0; border-collapse: collapse;"> |
| 63 | 163 |
<tr> |
| 64 | 164 |
<td style="width:<%= subject_width %>px; padding:0px;"> |
| ... | ... | |
| 88 | 188 |
end %> |
| 89 | 189 |
</div> |
| 90 | 190 |
</td> |
| 191 |
<td style="width:225px; padding:0px;"> |
|
| 192 |
<div style="position:relative;height:<%= t_height + 24 %>px;width:225px;"> |
|
| 193 |
<div style="width:225px;height:<%= headers_height %>px;background: #eee;" class="gantt_hdr"></div> |
|
| 194 |
<div style="width:225px;height:<%= t_height %>px;border-left: 1px solid #c0c0c0;overflow:hidden;" class="gantt_hdr"></div> |
|
| 195 |
<% |
|
| 196 |
top = headers_height + 8 |
|
| 197 |
@gantt.events.each do |i| %> |
|
| 198 |
<div style="position: absolute;line-height:1.2em;height:16px;top:<%= top %>px;"><small> |
|
| 199 |
<% if i.is_a? Issue %> |
|
| 200 |
<span id="<%=i.id%>_start_date_str" style="font-size: 7pt;"><%= format_date(i.start_date) %></span> |
|
| 201 |
<input type="hidden" size="12" id="<%=i.id%>_hidden_start_date" value="<%= i.start_date %>"> |
|
| 202 |
<input type="hidden" size="12" id="<%=i.id%>_start_date" value="<%= i.start_date %>"><%= calendar_for("#{i.id}_start_date") if i.is_a? Issue %>
|
|
| 203 |
<script type="text/javascript"> |
|
| 204 |
//<![CDATA[ |
|
| 205 |
new Form.Element.Observer('<%=i.id%>_start_date', 0.25,
|
|
| 206 |
function(element, value) {
|
|
| 207 |
if (value == document.getElementById('<%=i.id%>_hidden_start_date').value) {
|
|
| 208 |
return ; |
|
| 209 |
} |
|
| 210 |
new Ajax.Request('<%=url_for(:controller=>:issues, :action => :edit_gantt, :id=>i.id, :date_from=>@gantt.date_from.strftime("%Y-%m-%d"), :date_to=>@gantt.date_to.strftime("%Y-%m-%d"), :zoom=>zoom, :escape => false) %>', {asynchronous:true, evalScripts:true, onFailure:function(request){handle_failure(request.responseText)}, onSuccess:function(request){change_dates(request.responseText)}, parameters:'start_date=' + encodeURIComponent(value)});
|
|
| 211 |
}) |
|
| 212 |
//]]> |
|
| 213 |
</script> |
|
| 214 |
<span id="<%=i.id%>_due_date_str" style="font-size: 7pt;<%= "color: blue;" unless i.due_date%>"> |
|
| 215 |
<%= format_date(i.due_before) %> |
|
| 216 |
</span> |
|
| 217 |
<input type="hidden" size="12" id="<%=i.id%>_hidden_due_date" value="<%= i.due_date %>"> |
|
| 218 |
<input type="hidden" size="12" id="<%=i.id%>_due_date" value="<%= i.due_date %>"><%= calendar_for("#{i.id}_due_date") if i.due_date%>
|
|
| 219 |
<script type="text/javascript"> |
|
| 220 |
//<![CDATA[ |
|
| 221 |
new Form.Element.Observer('<%=i.id%>_due_date', 0.25,
|
|
| 222 |
function(element, value) {
|
|
| 223 |
if (value == document.getElementById('<%=i.id%>_hidden_due_date').value) {
|
|
| 224 |
return ; |
|
| 225 |
} |
|
| 226 |
new Ajax.Request('<%=url_for(:controller=>:issues, :action => :edit_gantt, :id=>i.id, :date_from=>@gantt.date_from.strftime("%Y-%m-%d"), :date_to=>@gantt.date_to.strftime("%Y-%m-%d"), :zoom=>zoom, :escape => false) %>', {asynchronous:true, evalScripts:true, onFailure:function(request){handle_failure(request.responseText)}, onSuccess:function(request){change_dates(request.responseText)}, parameters:'due_date=' + encodeURIComponent(value)});
|
|
| 227 |
}) |
|
| 228 |
//]]> |
|
| 229 |
</script> |
|
| 230 |
<% else %> |
|
| 231 |
<span style="font-size: 7pt;"><%= format_date(i.start_date) %></span> |
|
| 232 |
<% end %> |
|
| 233 |
</small></div> |
|
| 234 |
<% top = top + 20 |
|
| 235 |
end %> |
|
| 236 |
</div> |
|
| 237 |
</td> |
|
| 91 | 238 |
<td> |
| 92 | 239 | |
| 93 |
<div style="position:relative;height:<%= t_height + 24 %>px;overflow:auto;"> |
|
| 240 |
<div style="position:relative;height:<%= t_height + 24 %>px;overflow:auto;" id="gantt-container">
|
|
| 94 | 241 |
<div style="width:<%= g_width-1 %>px;height:<%= headers_height %>px;background: #eee;" class="gantt_hdr"> </div> |
| 95 | 242 |
<% |
| 96 | 243 |
# |
| ... | ... | |
| 150 | 297 |
left = 0 |
| 151 | 298 |
height = g_height + header_heigth - 1 |
| 152 | 299 |
wday = @gantt.date_from.cwday |
| 300 |
dt = @gantt.date_from |
|
| 153 | 301 |
(@gantt.date_to - @gantt.date_from + 1).to_i.times do |
| 154 | 302 |
width = zoom - 1 |
| 155 | 303 |
%> |
| 156 | 304 |
<div style="left:<%= left %>px;top:37px;width:<%= width %>px;height:<%= height %>px;font-size:0.7em;<%= "background:#f1f1f1;" if wday > 5 %>" class="gantt_hdr"> |
| 157 |
<%= day_name(wday).first %> |
|
| 305 |
<%= "#{dt.day}<br>"if @gantt.zoom == 4 %><%= day_name(wday).first %>
|
|
| 158 | 306 |
</div> |
| 159 | 307 |
<% |
| 160 | 308 |
left = left + width+1 |
| 161 | 309 |
wday = wday + 1 |
| 310 |
dt = dt + 1 |
|
| 162 | 311 |
wday = 1 if wday > 7 |
| 163 | 312 |
end |
| 164 | 313 |
end %> |
| ... | ... | |
| 169 | 318 |
# |
| 170 | 319 |
top = headers_height + 10 |
| 171 | 320 |
@gantt.events.each do |i| |
| 172 |
if i.is_a? Issue |
|
| 173 |
i_start_date = (i.start_date >= @gantt.date_from ? i.start_date : @gantt.date_from ) |
|
| 174 |
i_end_date = (i.due_before <= @gantt.date_to ? i.due_before : @gantt.date_to ) |
|
| 175 |
|
|
| 176 |
i_done_date = i.start_date + ((i.due_before - i.start_date+1)*i.done_ratio/100).floor |
|
| 177 |
i_done_date = (i_done_date <= @gantt.date_from ? @gantt.date_from : i_done_date ) |
|
| 178 |
i_done_date = (i_done_date >= @gantt.date_to ? @gantt.date_to : i_done_date ) |
|
| 179 |
|
|
| 180 |
i_late_date = [i_end_date, Date.today].min if i_start_date < Date.today |
|
| 181 |
|
|
| 182 |
i_left = ((i_start_date - @gantt.date_from)*zoom).floor |
|
| 183 |
i_width = ((i_end_date - i_start_date + 1)*zoom).floor - 2 # total width of the issue (- 2 for left and right borders) |
|
| 184 |
d_width = ((i_done_date - i_start_date)*zoom).floor - 2 # done width |
|
| 185 |
l_width = i_late_date ? ((i_late_date - i_start_date+1)*zoom).floor - 2 : 0 # delay width |
|
| 186 |
css = "task " + (i.leaf? ? 'leaf' : 'parent') |
|
| 321 |
if i.is_a? Issue |
|
| 322 |
i_left, i_width, l_width, d_width = get_position(i, @gantt.date_from, @gantt.date_to, zoom) |
|
| 187 | 323 |
%> |
| 188 |
<div style="top:<%= top %>px;left:<%= i_left %>px;width:<%= i_width %>px;" class="<%= css %> task_todo"><div class="left"></div> <div class="right"></div></div> |
|
| 189 |
<% if l_width > 0 %> |
|
| 190 |
<div style="top:<%= top %>px;left:<%= i_left %>px;width:<%= l_width %>px;" class="<%= css %> task_late"> </div> |
|
| 191 |
<% end %> |
|
| 192 |
<% if d_width > 0 %> |
|
| 193 |
<div style="top:<%= top %>px;left:<%= i_left %>px;width:<%= d_width %>px;" class="<%= css %> task_done"> </div> |
|
| 194 |
<% end %> |
|
| 195 |
<div style="top:<%= top %>px;left:<%= i_left + i_width + 8 %>px;background:#fff;" class="<%= css %>"> |
|
| 196 |
<%= i.status.name %> |
|
| 197 |
<%= (i.done_ratio).to_i %>% |
|
| 198 |
</div> |
|
| 199 |
<div class="tooltip" style="position: absolute;top:<%= top %>px;left:<%= i_left %>px;width:<%= i_width %>px;height:12px;"> |
|
| 200 |
<span class="tip"> |
|
| 324 |
<div id="ev_<%=i.id%>" style="position:absolute;left:<%= i_left %>px;top:<%= top %>px;padding-top:3px;height:18px;width:<%= i_width+100 %>px;" class="handle"> |
|
| 325 |
<div id="task_todo_<%=i.id%>" style="float:left:0px; width:<%= i_width %>px;" class="task task_todo"> </div> |
|
| 326 |
<div id="task_late_<%=i.id%>" style="float:left:0px; width:<%= l_width %>px;" class="<%= l_width == 0 ? 'task task_none' : 'task task_late' %>"> </div> |
|
| 327 |
<div id="task_done_<%=i.id%>" style="float:left:0px; width:<%= d_width %>px;" class="<%= d_width == 0 ? 'task task_none' : 'task task_done' %>"> </div> |
|
| 328 |
<div id="task_task_<%=i.id%>" style="position:absolute;left:<%= (i_width + 5) %>px;background:#fff;" class="task"> |
|
| 329 |
<%= h(i.status.name) %> |
|
| 330 |
<%= (i.done_ratio).to_i %>% |
|
| 331 |
<% if !i.due_date && i.fixed_version %> |
|
| 332 |
- <strong><%= h(i.fixed_version.name) %></strong> |
|
| 333 |
<% end %> |
|
| 334 |
</div> |
|
| 335 |
</div> |
|
| 336 |
<%# === tooltip === %> |
|
| 337 |
<div id="tt_<%=i.id%>" class="tooltip" style="position: absolute;top:<%= top+14 %>px;left:<%= i_left %>px;width:<%= i_width %>px;height:8px;"> |
|
| 338 |
<span class="tip" style="position: relative;top:-2px;"> |
|
| 201 | 339 |
<%= render_issue_tooltip i %> |
| 202 | 340 |
</span></div> |
| 341 |
<%= draggable_element("ev_#{i.id}",
|
|
| 342 |
:revert =>false, :scroll=>"'gantt-container'", :constraint => "'horizontal'", :snap=>zoom, |
|
| 343 |
:onEnd=>"function( draggable, event ) {issue_moved(draggable.element);}"
|
|
| 344 |
) %> |
|
| 203 | 345 |
<% else |
| 204 | 346 |
i_left = ((i.start_date - @gantt.date_from)*zoom).floor |
| 205 | 347 |
%> |
| config/routes.rb (working copy) | ||
|---|---|---|
| 111 | 111 |
issues_views.connect 'projects/:project_id/issues.:format', :action => 'index' |
| 112 | 112 |
issues_views.connect 'projects/:project_id/issues/new', :action => 'new' |
| 113 | 113 |
issues_views.connect 'projects/:project_id/issues/gantt', :controller => 'gantts', :action => 'show' |
| 114 |
issues_views.connect 'projects/:project_id/issues/edit_gantt/:id', :controller => 'gantts', :action => 'edit_gantt', :id => /\d+/ |
|
| 114 | 115 |
issues_views.connect 'projects/:project_id/issues/calendar', :controller => 'calendars', :action => 'show' |
| 115 | 116 |
issues_views.connect 'projects/:project_id/issues/:copy_from/copy', :action => 'new' |
| 116 | 117 |
issues_views.connect 'issues/:id', :action => 'show', :id => /\d+/ |
| ... | ... | |
| 122 | 123 |
issues_actions.connect 'issues', :action => 'index' |
| 123 | 124 |
issues_actions.connect 'projects/:project_id/issues', :action => 'create' |
| 124 | 125 |
issues_actions.connect 'projects/:project_id/issues/gantt', :controller => 'gantts', :action => 'show' |
| 126 |
issues_actions.connect 'projects/:project_id/issues/edit_gantt:id', :controller => 'gantts', :action => 'edit_gantt', :id => /\d+/ |
|
| 125 | 127 |
issues_actions.connect 'projects/:project_id/issues/calendar', :controller => 'calendars', :action => 'show' |
| 126 | 128 |
issues_actions.connect 'issues/:id/quoted', :action => 'reply', :id => /\d+/ |
| 127 | 129 |
issues_actions.connect 'issues/:id/:action', :action => /edit|move|destroy/, :id => /\d+/ |
| ... | ... | |
| 135 | 137 |
issues_actions.connect 'issues/:id.:format', :action => 'destroy', :id => /\d+/, :format => /xml/ |
| 136 | 138 |
end |
| 137 | 139 |
issues_routes.connect 'issues/gantt', :controller => 'gantts', :action => 'show' |
| 140 |
issues_routes.connect 'issues/edit_gantt/:id', :controller => 'gantts', :action => 'edit_gantt', :id => /\d+/ |
|
| 138 | 141 |
issues_routes.connect 'issues/calendar', :controller => 'calendars', :action => 'show' |
| 139 | 142 |
issues_routes.connect 'issues/:action' |
| 140 | 143 |
end |
| public/stylesheets/application.css (working copy) | ||
|---|---|---|
| 788 | 788 |
.task_late { background:#f66 url(../images/task_late.png); border: 1px solid #f66; }
|
| 789 | 789 |
.task_done { background:#66f url(../images/task_done.png); border: 1px solid #66f; }
|
| 790 | 790 |
.task_todo { background:#aaa url(../images/task_todo.png); border: 1px solid #aaa; }
|
| 791 |
.task_none { background:transparent; border-style: none; }
|
|
| 791 | 792 | |
| 792 | 793 |
.task_todo.parent { background: #888; border: 1px solid #888; height: 6px;}
|
| 793 | 794 |
.task_late.parent, .task_done.parent { height: 3px;}
|