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