Commit 23d950855d6d2524d00b1f0618c008e2529f06a4
1 parent
667edcdd
Exists in
master
and in
4 other branches
Milestone basic scaffold
Showing
28 changed files
with
424 additions
and
36 deletions
Show diff stats
app/assets/javascripts/issues.js
@@ -2,6 +2,7 @@ function switchToNewIssue(form){ | @@ -2,6 +2,7 @@ function switchToNewIssue(form){ | ||
2 | $(".issues_content").hide("fade", { direction: "left" }, 150, function(){ | 2 | $(".issues_content").hide("fade", { direction: "left" }, 150, function(){ |
3 | $(".issues_content").after(form); | 3 | $(".issues_content").after(form); |
4 | $('select#issue_assignee_id').chosen(); | 4 | $('select#issue_assignee_id').chosen(); |
5 | + $('select#issue_milestone_id').chosen(); | ||
5 | $("#new_issue_dialog").show("fade", { direction: "right" }, 150); | 6 | $("#new_issue_dialog").show("fade", { direction: "right" }, 150); |
6 | $('.top-tabs .add_new').hide(); | 7 | $('.top-tabs .add_new').hide(); |
7 | }); | 8 | }); |
@@ -11,6 +12,7 @@ function switchToEditIssue(form){ | @@ -11,6 +12,7 @@ function switchToEditIssue(form){ | ||
11 | $(".issues_content").hide("fade", { direction: "left" }, 150, function(){ | 12 | $(".issues_content").hide("fade", { direction: "left" }, 150, function(){ |
12 | $(".issues_content").after(form); | 13 | $(".issues_content").after(form); |
13 | $('select#issue_assignee_id').chosen(); | 14 | $('select#issue_assignee_id').chosen(); |
15 | + $('select#issue_milestone_id').chosen(); | ||
14 | $("#edit_issue_dialog").show("fade", { direction: "right" }, 150); | 16 | $("#edit_issue_dialog").show("fade", { direction: "right" }, 150); |
15 | $('.add_new').hide(); | 17 | $('.add_new').hide(); |
16 | }); | 18 | }); |
app/assets/stylesheets/common.scss
@@ -0,0 +1,31 @@ | @@ -0,0 +1,31 @@ | ||
1 | +/** | ||
2 | + * JQUERY UI datepicker | ||
3 | + * | ||
4 | + */ | ||
5 | +.ui-datepicker { | ||
6 | + border-color:#eee; | ||
7 | + padding:20px; | ||
8 | + | ||
9 | + .ui-state-default { | ||
10 | + background:#f1f1f1; | ||
11 | + padding:5px; | ||
12 | + } | ||
13 | + .ui-state-active { | ||
14 | + background:#fff; | ||
15 | + } | ||
16 | +} | ||
17 | + | ||
18 | +/** | ||
19 | + * JQUERY UI progressbar | ||
20 | + * | ||
21 | + */ | ||
22 | +.ui-progressbar { | ||
23 | + border:1px solid #ddd; | ||
24 | + height:6px; | ||
25 | + | ||
26 | + .ui-progressbar-value { | ||
27 | + background-color: #62C462;//$blue_link; | ||
28 | + margin:0; | ||
29 | + } | ||
30 | +} | ||
31 | + |
app/assets/stylesheets/main.scss
app/controllers/issues_controller.rb
@@ -15,7 +15,7 @@ class IssuesController < ApplicationController | @@ -15,7 +15,7 @@ class IssuesController < ApplicationController | ||
15 | before_filter :authorize_write_issue!, :only => [:new, :create] | 15 | before_filter :authorize_write_issue!, :only => [:new, :create] |
16 | 16 | ||
17 | # Allow modify issue | 17 | # Allow modify issue |
18 | - before_filter :authorize_modify_issue!, :only => [:close, :edit, :update, :sort] | 18 | + before_filter :authorize_modify_issue!, :only => [:close, :edit, :update] |
19 | 19 | ||
20 | # Allow destroy issue | 20 | # Allow destroy issue |
21 | before_filter :authorize_admin_issue!, :only => [:destroy] | 21 | before_filter :authorize_admin_issue!, :only => [:destroy] |
@@ -28,8 +28,10 @@ class IssuesController < ApplicationController | @@ -28,8 +28,10 @@ class IssuesController < ApplicationController | ||
28 | when 2 then @project.issues.closed | 28 | when 2 then @project.issues.closed |
29 | when 3 then @project.issues.opened.assigned(current_user) | 29 | when 3 then @project.issues.opened.assigned(current_user) |
30 | else @project.issues.opened | 30 | else @project.issues.opened |
31 | - end.page(params[:page]).per(20) | 31 | + end |
32 | 32 | ||
33 | + @issues = @issues.where(:milestone_id => params[:milestone_id]) if params[:milestone_id].present? | ||
34 | + @issues = @issues.page(params[:page]).per(20) | ||
33 | @issues = @issues.includes(:author, :project).order("critical, updated_at") | 35 | @issues = @issues.includes(:author, :project).order("critical, updated_at") |
34 | 36 | ||
35 | respond_to do |format| | 37 | respond_to do |format| |
@@ -51,13 +53,6 @@ class IssuesController < ApplicationController | @@ -51,13 +53,6 @@ class IssuesController < ApplicationController | ||
51 | def show | 53 | def show |
52 | @note = @project.notes.new(:noteable => @issue) | 54 | @note = @project.notes.new(:noteable => @issue) |
53 | 55 | ||
54 | - @commits = if @issue.branch_name && @project.repo.heads.map(&:name).include?(@issue.branch_name) | ||
55 | - @project.repo.commits_between("master", @issue.branch_name) | ||
56 | - else | ||
57 | - [] | ||
58 | - end | ||
59 | - | ||
60 | - | ||
61 | respond_to do |format| | 56 | respond_to do |format| |
62 | format.html | 57 | format.html |
63 | format.js | 58 | format.js |
@@ -102,6 +97,8 @@ class IssuesController < ApplicationController | @@ -102,6 +97,8 @@ class IssuesController < ApplicationController | ||
102 | end | 97 | end |
103 | 98 | ||
104 | def sort | 99 | def sort |
100 | + return render_404 unless can?(current_user, :admin_issue, @project) | ||
101 | + | ||
105 | @issues = @project.issues.where(:id => params['issue']) | 102 | @issues = @project.issues.where(:id => params['issue']) |
106 | @issues.each do |issue| | 103 | @issues.each do |issue| |
107 | issue.position = params['issue'].index(issue.id.to_s) + 1 | 104 | issue.position = params['issue'].index(issue.id.to_s) + 1 |
@@ -0,0 +1,94 @@ | @@ -0,0 +1,94 @@ | ||
1 | +class MilestonesController < ApplicationController | ||
2 | + before_filter :authenticate_user! | ||
3 | + before_filter :project | ||
4 | + before_filter :module_enabled | ||
5 | + before_filter :milestone, :only => [:edit, :update, :destroy, :show] | ||
6 | + layout "project" | ||
7 | + | ||
8 | + # Authorize | ||
9 | + before_filter :add_project_abilities | ||
10 | + | ||
11 | + # Allow read any milestone | ||
12 | + before_filter :authorize_read_milestone! | ||
13 | + | ||
14 | + # Allow admin milestone | ||
15 | + before_filter :authorize_admin_milestone!, :except => [:index, :show] | ||
16 | + | ||
17 | + respond_to :html | ||
18 | + | ||
19 | + def index | ||
20 | + @milestones = case params[:f].to_i | ||
21 | + when 1; @project.milestones | ||
22 | + else @project.milestones.active | ||
23 | + end | ||
24 | + | ||
25 | + @milestones = @milestones.includes(:project).order("due_date") | ||
26 | + @milestones = @milestones.page(params[:page]).per(20) | ||
27 | + end | ||
28 | + | ||
29 | + def new | ||
30 | + @milestone = @project.milestones.new | ||
31 | + respond_with(@milestone) | ||
32 | + end | ||
33 | + | ||
34 | + def edit | ||
35 | + respond_with(@milestone) | ||
36 | + end | ||
37 | + | ||
38 | + def show | ||
39 | + respond_to do |format| | ||
40 | + format.html | ||
41 | + format.js | ||
42 | + end | ||
43 | + end | ||
44 | + | ||
45 | + def create | ||
46 | + @milestone = @project.milestones.new(params[:milestone]) | ||
47 | + | ||
48 | + if @milestone.save | ||
49 | + redirect_to project_milestone_path(@project, @milestone) | ||
50 | + else | ||
51 | + render "new" | ||
52 | + end | ||
53 | + end | ||
54 | + | ||
55 | + def update | ||
56 | + @milestone.update_attributes(params[:milestone]) | ||
57 | + | ||
58 | + respond_to do |format| | ||
59 | + format.js | ||
60 | + format.html do | ||
61 | + if @milestone.valid? | ||
62 | + redirect_to [@project, @milestone] | ||
63 | + else | ||
64 | + render :edit | ||
65 | + end | ||
66 | + end | ||
67 | + end | ||
68 | + end | ||
69 | + | ||
70 | + def destroy | ||
71 | + return access_denied! unless can?(current_user, :admin_milestone, @milestone) | ||
72 | + | ||
73 | + @milestone.destroy | ||
74 | + | ||
75 | + respond_to do |format| | ||
76 | + format.html { redirect_to project_milestones_path } | ||
77 | + format.js { render :nothing => true } | ||
78 | + end | ||
79 | + end | ||
80 | + | ||
81 | + protected | ||
82 | + | ||
83 | + def milestone | ||
84 | + @milestone ||= @project.milestones.find(params[:id]) | ||
85 | + end | ||
86 | + | ||
87 | + def authorize_admin_milestone! | ||
88 | + return render_404 unless can?(current_user, :admin_milestone, @project) | ||
89 | + end | ||
90 | + | ||
91 | + def module_enabled | ||
92 | + return render_404 unless @project.issues_enabled | ||
93 | + end | ||
94 | +end |
app/models/ability.rb
@@ -17,6 +17,7 @@ class Ability | @@ -17,6 +17,7 @@ class Ability | ||
17 | :read_project, | 17 | :read_project, |
18 | :read_wiki, | 18 | :read_wiki, |
19 | :read_issue, | 19 | :read_issue, |
20 | + :read_milestone, | ||
20 | :read_snippet, | 21 | :read_snippet, |
21 | :read_team_member, | 22 | :read_team_member, |
22 | :read_merge_request, | 23 | :read_merge_request, |
@@ -42,6 +43,7 @@ class Ability | @@ -42,6 +43,7 @@ class Ability | ||
42 | :modify_merge_request, | 43 | :modify_merge_request, |
43 | :admin_project, | 44 | :admin_project, |
44 | :admin_issue, | 45 | :admin_issue, |
46 | + :admin_milestone, | ||
45 | :admin_snippet, | 47 | :admin_snippet, |
46 | :admin_team_member, | 48 | :admin_team_member, |
47 | :admin_merge_request, | 49 | :admin_merge_request, |
app/models/issue.rb
1 | class Issue < ActiveRecord::Base | 1 | class Issue < ActiveRecord::Base |
2 | belongs_to :project | 2 | belongs_to :project |
3 | + belongs_to :milestone | ||
3 | belongs_to :author, :class_name => "User" | 4 | belongs_to :author, :class_name => "User" |
4 | belongs_to :assignee, :class_name => "User" | 5 | belongs_to :assignee, :class_name => "User" |
5 | has_many :notes, :as => :noteable, :dependent => :destroy | 6 | has_many :notes, :as => :noteable, :dependent => :destroy |
@@ -0,0 +1,29 @@ | @@ -0,0 +1,29 @@ | ||
1 | +class Milestone < ActiveRecord::Base | ||
2 | + belongs_to :project | ||
3 | + has_many :issues | ||
4 | + | ||
5 | + validates_presence_of :project_id | ||
6 | + validates_presence_of :title | ||
7 | + | ||
8 | + def self.active | ||
9 | + where("due_date > ? ", Date.today) | ||
10 | + end | ||
11 | + | ||
12 | + def percent_complete | ||
13 | + @percent_complete ||= begin | ||
14 | + total_i = self.issues.count | ||
15 | + closed_i = self.issues.closed.count | ||
16 | + if total_i > 0 | ||
17 | + (closed_i * 100) / total_i | ||
18 | + else | ||
19 | + 100 | ||
20 | + end | ||
21 | + rescue => ex | ||
22 | + 0 | ||
23 | + end | ||
24 | + end | ||
25 | + | ||
26 | + def expires_at | ||
27 | + "expires at #{due_date.stamp("Aug 21, 2011")}" if due_date | ||
28 | + end | ||
29 | +end |
app/models/project.rb
@@ -12,6 +12,7 @@ class Project < ActiveRecord::Base | @@ -12,6 +12,7 @@ class Project < ActiveRecord::Base | ||
12 | has_many :events, :dependent => :destroy | 12 | has_many :events, :dependent => :destroy |
13 | has_many :merge_requests, :dependent => :destroy | 13 | has_many :merge_requests, :dependent => :destroy |
14 | has_many :issues, :dependent => :destroy, :order => "position" | 14 | has_many :issues, :dependent => :destroy, :order => "position" |
15 | + has_many :milestones, :dependent => :destroy | ||
15 | has_many :users_projects, :dependent => :destroy | 16 | has_many :users_projects, :dependent => :destroy |
16 | has_many :notes, :dependent => :destroy | 17 | has_many :notes, :dependent => :destroy |
17 | has_many :snippets, :dependent => :destroy | 18 | has_many :snippets, :dependent => :destroy |
app/views/issues/_form.html.haml
@@ -9,18 +9,25 @@ | @@ -9,18 +9,25 @@ | ||
9 | %li= msg | 9 | %li= msg |
10 | 10 | ||
11 | .clearfix | 11 | .clearfix |
12 | - = f.label :title, "Issue Subject" | ||
13 | - .input= f.text_field :title, :maxlength => 255, :class => "xxlarge" | ||
14 | - | 12 | + = f.label :title, "Issue Subject *" |
13 | + .input | ||
14 | + = f.text_field :title, :maxlength => 255, :class => "xxlarge" | ||
15 | + | ||
16 | + .clearfix | ||
17 | + = f.label :assignee_id, "Assign to *" | ||
18 | + .input= f.select(:assignee_id, @project.users.all.collect {|p| [ p.name, p.id ] }, { :include_blank => "Assign to user" }) | ||
19 | + | ||
15 | .clearfix | 20 | .clearfix |
16 | = f.label :description, "Issue Details" | 21 | = f.label :description, "Issue Details" |
17 | .input | 22 | .input |
18 | = f.text_area :description, :maxlength => 2000, :class => "xxlarge", :rows => 10 | 23 | = f.text_area :description, :maxlength => 2000, :class => "xxlarge", :rows => 10 |
19 | %p.hint Markdown is enabled. | 24 | %p.hint Markdown is enabled. |
20 | 25 | ||
26 | + | ||
21 | .clearfix | 27 | .clearfix |
22 | - = f.label :assignee_id | ||
23 | - .input= f.select(:assignee_id, @project.users.all.collect {|p| [ p.name, p.id ] }, { :include_blank => "Assign to user" }) | 28 | + = f.label :milestone_id |
29 | + .input= f.select(:milestone_id, @project.milestones.active.all.collect {|p| [ p.title, p.id ] }, { :include_blank => "Select milestone" }) | ||
30 | + | ||
24 | 31 | ||
25 | .clearfix | 32 | .clearfix |
26 | = f.label :critical, "Critical" | 33 | = f.label :critical, "Critical" |
app/views/issues/_head.html.haml
1 | .tabs | 1 | .tabs |
2 | %li{:class => "#{'active' if current_page?(project_issues_path(@project))}"} | 2 | %li{:class => "#{'active' if current_page?(project_issues_path(@project))}"} |
3 | = link_to project_issues_path(@project), :class => "tab" do | 3 | = link_to project_issues_path(@project), :class => "tab" do |
4 | - Issues | 4 | + Browse Issues |
5 | + %li{:class => "#{'active' if current_page?(project_milestones_path(@project))}"} | ||
6 | + = link_to project_milestones_path(@project), :class => "tab" do | ||
7 | + Milestones |
app/views/issues/_issues.html.haml
@@ -10,3 +10,6 @@ | @@ -10,3 +10,6 @@ | ||
10 | .span10= paginate @issues, :remote => true, :theme => "gitlab" | 10 | .span10= paginate @issues, :remote => true, :theme => "gitlab" |
11 | .span4.right | 11 | .span4.right |
12 | %span.cgray.right #{@issues.total_count} issues for this filter | 12 | %span.cgray.right #{@issues.total_count} issues for this filter |
13 | +- else | ||
14 | + %li | ||
15 | + %p.padded Nothing to show here |
app/views/issues/edit.html.haml
app/views/issues/index.html.haml
1 | += render "issues/head" | ||
1 | .issues_content | 2 | .issues_content |
2 | %h3 | 3 | %h3 |
3 | Issues | 4 | Issues |
@@ -5,14 +6,22 @@ | @@ -5,14 +6,22 @@ | ||
5 | = link_to project_issues_path(@project, :atom, { :private_token => current_user.private_token }) do | 6 | = link_to project_issues_path(@project, :atom, { :private_token => current_user.private_token }) do |
6 | = image_tag "Rss-UI.PNG", :width => 16, :title => "feed" | 7 | = image_tag "Rss-UI.PNG", :width => 16, :title => "feed" |
7 | 8 | ||
8 | - - if can? current_user, :write_issue, @project | ||
9 | - = link_to new_project_issue_path(@project), :class => "right btn small", :title => "New Issue", :remote => true do | ||
10 | - New Issue | 9 | + .right |
10 | + .span4.left | ||
11 | + = form_tag search_project_issues_path(@project), :method => :get, :remote => true, :id => "issue_search_form", :class => :left do | ||
12 | + = hidden_field_tag :project_id, @project.id, { :id => 'project_id' } | ||
13 | + = hidden_field_tag :status, params[:f] | ||
14 | + = search_field_tag :issue_search, nil, { :placeholder => 'Search', :class => 'issue_search' } | ||
15 | + | ||
16 | + - if can? current_user, :write_issue, @project | ||
17 | + .span2.left | ||
18 | + = link_to new_project_issue_path(@project), :class => "right btn small", :title => "New Issue", :remote => true do | ||
19 | + New Issue | ||
11 | %br | 20 | %br |
12 | %div#issues-table-holder.ui-box | 21 | %div#issues-table-holder.ui-box |
13 | .title | 22 | .title |
14 | .row | 23 | .row |
15 | - .span8 | 24 | + .span6 |
16 | %ul.pills.left | 25 | %ul.pills.left |
17 | %li{:class => ("active" if (params[:f] == "0" || !params[:f]))} | 26 | %li{:class => ("active" if (params[:f] == "0" || !params[:f]))} |
18 | = link_to project_issues_path(@project, :f => 0) do | 27 | = link_to project_issues_path(@project, :f => 0) do |
@@ -27,17 +36,13 @@ | @@ -27,17 +36,13 @@ | ||
27 | = link_to project_issues_path(@project, :f => 1) do | 36 | = link_to project_issues_path(@project, :f => 1) do |
28 | All | 37 | All |
29 | 38 | ||
30 | - .span3.right | ||
31 | - = form_tag search_project_issues_path(@project), :method => :get, :remote => true, :id => "issue_search_form", :class => :right do | ||
32 | - = hidden_field_tag :project_id, @project.id, { :id => 'project_id' } | ||
33 | - = hidden_field_tag :status, params[:f] | ||
34 | - = search_field_tag :issue_search, nil, { :placeholder => 'Search', :class => 'issue_search' } | 39 | + .span6.right |
40 | + = form_tag project_issues_path(@project), :method => :get, :class => :right do | ||
41 | + = select_tag(:milestone_id, options_from_collection_for_select(@project.milestones.order("id desc").all, "id", "title", params[:milestone_id]), :prompt => "Select milestone") | ||
35 | 42 | ||
36 | %ul#issues-table.unstyled | 43 | %ul#issues-table.unstyled |
37 | = render "issues" | 44 | = render "issues" |
38 | - - if @issues.blank? | ||
39 | - %li | ||
40 | - %p.padded Nothing to show here | 45 | + |
41 | :javascript | 46 | :javascript |
42 | var href = $('.issue_search').parent().attr('action'); | 47 | var href = $('.issue_search').parent().attr('action'); |
43 | var last_terms = ''; | 48 | var last_terms = ''; |
@@ -65,9 +70,8 @@ | @@ -65,9 +70,8 @@ | ||
65 | $('#issues-table').sortable({ | 70 | $('#issues-table').sortable({ |
66 | axis: 'y', | 71 | axis: 'y', |
67 | dropOnEmpty: false, | 72 | dropOnEmpty: false, |
68 | - handle: '.handle', | ||
69 | - cursor: 'crosshair', | ||
70 | - items: 'tr', | 73 | + handle: '.avatar', |
74 | + items: 'li', | ||
71 | opacity: 0.4, | 75 | opacity: 0.4, |
72 | scroll: true, | 76 | scroll: true, |
73 | update: function(){ | 77 | update: function(){ |
@@ -85,4 +89,8 @@ | @@ -85,4 +89,8 @@ | ||
85 | 89 | ||
86 | $(function(){ | 90 | $(function(){ |
87 | setSortable(); | 91 | setSortable(); |
92 | + $("#milestone_id").chosen(); | ||
93 | + $("#milestone_id").live("change", function(){ | ||
94 | + $(this).closest("form").submit(); | ||
95 | + }); | ||
88 | }); | 96 | }); |
app/views/issues/new.html.haml
@@ -0,0 +1,54 @@ | @@ -0,0 +1,54 @@ | ||
1 | +%h3= @milestone.new_record? ? "New Milestone" : "Edit Milestone ##{@milestone.id}" | ||
2 | +.back_link | ||
3 | + = link_to project_milestones_path(@project) do | ||
4 | + ← To milestones | ||
5 | + | ||
6 | +%hr | ||
7 | + | ||
8 | += form_for [@project, @milestone] do |f| | ||
9 | + -if @milestone.errors.any? | ||
10 | + .alert-message.block-message.error | ||
11 | + %ul | ||
12 | + - @milestone.errors.full_messages.each do |msg| | ||
13 | + %li= msg | ||
14 | + .row | ||
15 | + .span7 | ||
16 | + .clearfix | ||
17 | + = f.label :title, "Title" | ||
18 | + .input | ||
19 | + = f.text_field :title, :maxlength => 255, :class => "xlarge" | ||
20 | + %p.hint Required | ||
21 | + .clearfix | ||
22 | + = f.label :description, "Description" | ||
23 | + .input | ||
24 | + = f.text_area :description, :maxlength => 2000, :class => "xlarge", :rows => 10 | ||
25 | + %p.hint Markdown is enabled. | ||
26 | + .span8 | ||
27 | + .clearfix | ||
28 | + = f.label :due_date, "Due Date" | ||
29 | + .input= f.hidden_field :due_date | ||
30 | + .input | ||
31 | + .datepicker | ||
32 | + | ||
33 | + | ||
34 | + .actions | ||
35 | + - if @milestone.new_record? | ||
36 | + = f.submit 'Create milestone', :class => "primary btn" | ||
37 | + -else | ||
38 | + = f.submit 'Save changes', :class => "primary btn" | ||
39 | + | ||
40 | + - if request.xhr? | ||
41 | + = link_to "Cancel", "#back", :onclick => "backToIssues();", :class => "btn" | ||
42 | + - else | ||
43 | + - if @milestone.new_record? | ||
44 | + = link_to "Cancel", project_milestones_path(@project), :class => "btn" | ||
45 | + - else | ||
46 | + = link_to "Cancel", project_milestone_path(@project, @milestone), :class => "btn" | ||
47 | + | ||
48 | +:javascript | ||
49 | + $(function() { | ||
50 | + $( ".datepicker" ).datepicker({ | ||
51 | + dateFormat: "yy-mm-dd", | ||
52 | + onSelect: function(dateText, inst) { $("#milestone_due_date").val(dateText) } | ||
53 | + }); | ||
54 | + }); |
@@ -0,0 +1,21 @@ | @@ -0,0 +1,21 @@ | ||
1 | +%li{:class => "wll", :id => dom_id(milestone) } | ||
2 | + .right | ||
3 | + - if milestone.issues.count > 0 | ||
4 | + = link_to 'Browse Issues', project_issues_path(milestone.project, :milestone_id => milestone.id), :class => "btn small" | ||
5 | + - if milestone.issues.any? | ||
6 | + %span.btn.small.disabled.padded= pluralize milestone.issues.count, 'issues' | ||
7 | + - if can? current_user, :admin_milestone, milestone.project | ||
8 | + = link_to 'Edit', edit_project_milestone_path(milestone.project, milestone), :class => "btn small edit-milestone-link" | ||
9 | + = link_to project_milestone_path(milestone.project, milestone) do | ||
10 | + %h4.row_title | ||
11 | + = truncate(milestone.title, :length => 100) | ||
12 | + %small= milestone.expires_at | ||
13 | + | ||
14 | + .progress.span4 | ||
15 | + | ||
16 | + :javascript | ||
17 | + $(function() { | ||
18 | + $( "##{dom_id(milestone)} .progress" ).progressbar({ | ||
19 | + value: #{milestone.percent_complete} | ||
20 | + }); | ||
21 | + }); |
@@ -0,0 +1,25 @@ | @@ -0,0 +1,25 @@ | ||
1 | += render "issues/head" | ||
2 | +.milestones_content | ||
3 | + %h3 | ||
4 | + Milestones | ||
5 | + - if can? current_user, :admin_milestone, @project | ||
6 | + = link_to "New Milestone", new_project_milestone_path(@project), :class => "right btn small", :title => "New Milestone" | ||
7 | + %br | ||
8 | + %div.ui-box | ||
9 | + .title | ||
10 | + %ul.pills | ||
11 | + %li{:class => ("active" if (params[:f] == "0" || !params[:f]))} | ||
12 | + = link_to project_milestones_path(@project, :f => 0) do | ||
13 | + Active | ||
14 | + %li{:class => ("active" if params[:f] == "1")} | ||
15 | + = link_to project_milestones_path(@project, :f => 1) do | ||
16 | + All | ||
17 | + | ||
18 | + %ul.unstyled | ||
19 | + = render @milestones | ||
20 | + | ||
21 | + - if @milestones.present? | ||
22 | + %li.bottom= paginate @milestones, :remote => true, :theme => "gitlab" | ||
23 | + - else | ||
24 | + %li | ||
25 | + %p.padded Nothing to show here |
@@ -0,0 +1 @@ | @@ -0,0 +1 @@ | ||
1 | += render "form" |
@@ -0,0 +1,50 @@ | @@ -0,0 +1,50 @@ | ||
1 | +%h3 | ||
2 | + Milestone ##{@milestone.id} | ||
3 | + %small | ||
4 | + = @milestone.expires_at | ||
5 | + | ||
6 | + %span.right | ||
7 | + - if can?(current_user, :admin_milestone, @project) | ||
8 | + = link_to edit_project_milestone_path(@project, @milestone), :class => "btn" do | ||
9 | + Edit | ||
10 | + | ||
11 | +.back_link | ||
12 | + = link_to project_milestones_path(@project) do | ||
13 | + ← To milestones list | ||
14 | + | ||
15 | +.main_box | ||
16 | + .top_box_content | ||
17 | + %h5 | ||
18 | + - if @milestone.closed | ||
19 | + .alert-message.error.status_info Closed | ||
20 | + - else | ||
21 | + .alert-message.success.status_info Open | ||
22 | + = @milestone.title | ||
23 | + | ||
24 | + .middle_box_content | ||
25 | + .row | ||
26 | + .span2 | ||
27 | + = link_to 'Browse Issues', project_issues_path(@milestone.project, :milestone_id => @milestone.id), :class => "btn small edit-milestone-link" | ||
28 | + .span4 | ||
29 | + %span | ||
30 | + = @milestone.expires_at | ||
31 | + | ||
32 | + .span4.right | ||
33 | + .progress | ||
34 | + %br | ||
35 | + %span | ||
36 | + #{@milestone.issues.opened.count} open | ||
37 | + – | ||
38 | + #{@milestone.issues.closed.count} closed | ||
39 | + | ||
40 | + - if @milestone.description.present? | ||
41 | + .bottom_box_content | ||
42 | + = markdown @milestone.description | ||
43 | + | ||
44 | + | ||
45 | +:javascript | ||
46 | + $(function() { | ||
47 | + $( ".progress" ).progressbar({ | ||
48 | + value: #{@milestone.percent_complete} | ||
49 | + }); | ||
50 | + }); |
config/routes.rb
@@ -122,6 +122,7 @@ Gitlab::Application.routes.draw do | @@ -122,6 +122,7 @@ Gitlab::Application.routes.draw do | ||
122 | end | 122 | end |
123 | end | 123 | end |
124 | resources :team_members | 124 | resources :team_members |
125 | + resources :milestones | ||
125 | resources :issues do | 126 | resources :issues do |
126 | collection do | 127 | collection do |
127 | post :sort | 128 | post :sort |
@@ -0,0 +1,12 @@ | @@ -0,0 +1,12 @@ | ||
1 | +class CreateMilestones < ActiveRecord::Migration | ||
2 | + def change | ||
3 | + create_table :milestones do |t| | ||
4 | + t.string :title, :null => false | ||
5 | + t.integer :project_id, :null => false | ||
6 | + t.text :description | ||
7 | + t.date :due_date | ||
8 | + t.boolean :closed, :default => false, :null => false | ||
9 | + t.timestamps | ||
10 | + end | ||
11 | + end | ||
12 | +end |
db/schema.rb
@@ -11,7 +11,7 @@ | @@ -11,7 +11,7 @@ | ||
11 | # | 11 | # |
12 | # It's strongly recommended to check this file into your version control system. | 12 | # It's strongly recommended to check this file into your version control system. |
13 | 13 | ||
14 | -ActiveRecord::Schema.define(:version => 20120405211750) do | 14 | +ActiveRecord::Schema.define(:version => 20120408181910) do |
15 | 15 | ||
16 | create_table "events", :force => true do |t| | 16 | create_table "events", :force => true do |t| |
17 | t.string "target_type" | 17 | t.string "target_type" |
@@ -30,13 +30,14 @@ ActiveRecord::Schema.define(:version => 20120405211750) do | @@ -30,13 +30,14 @@ ActiveRecord::Schema.define(:version => 20120405211750) do | ||
30 | t.integer "assignee_id" | 30 | t.integer "assignee_id" |
31 | t.integer "author_id" | 31 | t.integer "author_id" |
32 | t.integer "project_id" | 32 | t.integer "project_id" |
33 | - t.datetime "created_at", :null => false | ||
34 | - t.datetime "updated_at", :null => false | ||
35 | - t.boolean "closed", :default => false, :null => false | ||
36 | - t.integer "position", :default => 0 | ||
37 | - t.boolean "critical", :default => false, :null => false | 33 | + t.datetime "created_at", :null => false |
34 | + t.datetime "updated_at", :null => false | ||
35 | + t.boolean "closed", :default => false, :null => false | ||
36 | + t.integer "position", :default => 0 | ||
37 | + t.boolean "critical", :default => false, :null => false | ||
38 | t.string "branch_name" | 38 | t.string "branch_name" |
39 | t.text "description" | 39 | t.text "description" |
40 | + t.integer "milestone_id" | ||
40 | end | 41 | end |
41 | 42 | ||
42 | add_index "issues", ["project_id"], :name => "index_issues_on_project_id" | 43 | add_index "issues", ["project_id"], :name => "index_issues_on_project_id" |
@@ -69,6 +70,15 @@ ActiveRecord::Schema.define(:version => 20120405211750) do | @@ -69,6 +70,15 @@ ActiveRecord::Schema.define(:version => 20120405211750) do | ||
69 | 70 | ||
70 | add_index "merge_requests", ["project_id"], :name => "index_merge_requests_on_project_id" | 71 | add_index "merge_requests", ["project_id"], :name => "index_merge_requests_on_project_id" |
71 | 72 | ||
73 | + create_table "milestones", :force => true do |t| | ||
74 | + t.string "title", :null => false | ||
75 | + t.text "description" | ||
76 | + t.date "due_date", :null => false | ||
77 | + t.integer "project_id", :null => false | ||
78 | + t.datetime "created_at", :null => false | ||
79 | + t.datetime "updated_at", :null => false | ||
80 | + end | ||
81 | + | ||
72 | create_table "notes", :force => true do |t| | 82 | create_table "notes", :force => true do |t| |
73 | t.text "note" | 83 | t.text "note" |
74 | t.string "noteable_id" | 84 | t.string "noteable_id" |