Commit 8e0126e9ec676f1ec40bb1a3dcb811e6477fb8c6
Exists in
master
and in
4 other branches
Merge branch 'issue_dashboard' into improved_dashboard
Showing
20 changed files
with
247 additions
and
22 deletions
Show diff stats
app/assets/stylesheets/style.scss
... | ... | @@ -351,7 +351,7 @@ header h1.logo a{ |
351 | 351 | text-indent: -1000em; |
352 | 352 | } |
353 | 353 | |
354 | -header nav{border-radius: 4px; box-shadow: 0 1px 2px black; width: 294px; margin: auto; | |
354 | +header nav{border-radius: 4px; box-shadow: 0 1px 2px black; width: 392px; margin: auto; | |
355 | 355 | background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #595d63), to(#31363e)); |
356 | 356 | background-image: -webkit-linear-gradient(#595d63 6.6%, #31363e); |
357 | 357 | background-image: -moz-linear-gradient(#595d63 6.6%, #31363e); |
... | ... | @@ -398,6 +398,7 @@ header nav a span{width: 20px; height: 20px; display: inline-block; background: |
398 | 398 | header nav a.dashboard span{background: url('images.png') no-repeat -161px 0;} |
399 | 399 | header nav a.admin span{background: url('images.png') no-repeat -184px 0;} |
400 | 400 | header nav a.project span{background: url('images.png') no-repeat -209px -1px; top: 7px} |
401 | +header nav a.issues span{background: url('images.png') no-repeat -209px -1px; top: 7px} | |
401 | 402 | |
402 | 403 | header .login-top{float: right; width: 180px; |
403 | 404 | background-image: -webkit-gradient(linear, 0 0, 0 62, color-stop(0.032, #464c56), to(#363c45)); | ... | ... |
... | ... | @@ -0,0 +1,21 @@ |
1 | +class UserIssuesController < ApplicationController | |
2 | + before_filter :authenticate_user! | |
3 | + | |
4 | + layout "user" | |
5 | + | |
6 | + respond_to :js, :html | |
7 | + | |
8 | + def index | |
9 | + @user = current_user | |
10 | + @issues = current_user.assigned_issues.opened | |
11 | + | |
12 | + @issues = @issues.includes(:author, :project) | |
13 | + | |
14 | + respond_to do |format| | |
15 | + format.html | |
16 | + format.js | |
17 | + format.atom { render :layout => false } | |
18 | + end | |
19 | + end | |
20 | + | |
21 | +end | ... | ... |
app/models/user.rb
... | ... | @@ -25,6 +25,15 @@ class User < ActiveRecord::Base |
25 | 25 | :foreign_key => :assignee_id, |
26 | 26 | :dependent => :destroy |
27 | 27 | |
28 | + has_many :merge_requests, | |
29 | + :foreign_key => :author_id, | |
30 | + :dependent => :destroy | |
31 | + | |
32 | + has_many :assigned_merge_requests, | |
33 | + :class_name => "MergeRequest", | |
34 | + :foreign_key => :assignee_id, | |
35 | + :dependent => :destroy | |
36 | + | |
28 | 37 | before_create :ensure_authentication_token |
29 | 38 | alias_attribute :private_token, :authentication_token |
30 | 39 | scope :not_in_project, lambda { |project| where("id not in (:ids)", :ids => project.users.map(&:id) ) } | ... | ... |
app/views/issues/_issues.html.haml
1 | 1 | - @issues.critical.each do |issue| |
2 | - = render(:partial => 'show', :locals => {:issue => issue}) | |
2 | + = render(:partial => 'issues/show', :locals => {:issue => issue}) | |
3 | 3 | |
4 | 4 | - @issues.non_critical.each do |issue| |
5 | - = render(:partial => 'show', :locals => {:issue => issue}) | |
5 | + = render(:partial => 'issues/show', :locals => {:issue => issue}) | ... | ... |
app/views/issues/_show.html.haml
1 | -%tr{ :id => dom_id(issue), :class => "issue #{issue.critical ? "critical" : ""}", :url => project_issue_path(@project, issue) } | |
1 | +%tr{ :id => dom_id(issue), :class => "issue #{issue.critical ? "critical" : ""}", :url => project_issue_path(issue.project, issue) } | |
2 | 2 | %td |
3 | 3 | %strong.issue-number{:class => sort_class}= "##{issue.id}" |
4 | 4 | %span |
... | ... | @@ -6,6 +6,9 @@ |
6 | 6 | %br |
7 | 7 | %br |
8 | 8 | %div.note-author |
9 | + - if not @project.present? | |
10 | + %strong= issue.project.name | |
11 | + = '-' | |
9 | 12 | %strong= issue.assignee.name |
10 | 13 | %cite.cgray |
11 | 14 | = time_ago_in_words(issue.created_at) |
... | ... | @@ -17,10 +20,10 @@ |
17 | 20 | .right.action-links |
18 | 21 | - if can? current_user, :write_issue, issue |
19 | 22 | - if issue.closed |
20 | - = link_to 'Reopen', project_issue_path(@project, issue, :issue => {:closed => false }, :status_only => true), :method => :put, :class => "cgray", :remote => true | |
23 | + = link_to 'Reopen', project_issue_path(issue.project, issue, :issue => {:closed => false }, :status_only => true), :method => :put, :class => "cgray", :remote => true | |
21 | 24 | - else |
22 | - = link_to 'Resolve', project_issue_path(@project, issue, :issue => {:closed => true }, :status_only => true), :method => :put, :class => "cgray", :remote => true | |
25 | + = link_to 'Resolve', project_issue_path(issue.project, issue, :issue => {:closed => true }, :status_only => true), :method => :put, :class => "cgray", :remote => true | |
23 | 26 | - if can? current_user, :write_issue, issue |
24 | - = link_to 'Edit', edit_project_issue_path(@project, issue), :class => "cgray edit-issue-link", :remote => true | |
27 | + = link_to 'Edit', edit_project_issue_path(issue.project, issue), :class => "cgray edit-issue-link", :remote => true | |
25 | 28 | - if can?(current_user, :admin_issue, @project) || issue.author == current_user |
26 | - = link_to 'Remove', [@project, issue], :confirm => 'Are you sure?', :method => :delete, :remote => true, :class => "cred delete-issue negative", :id => "destroy_issue_#{issue.id}" | |
29 | + = link_to 'Remove', [issue.project, issue], :confirm => 'Are you sure?', :method => :delete, :remote => true, :class => "cred delete-issue negative", :id => "destroy_issue_#{issue.id}" | ... | ... |
app/views/layouts/_head_panel.html.erb
... | ... | @@ -25,6 +25,9 @@ |
25 | 25 | <%= link_to dashboard_path, :class => current_page?(root_path) ? "current dashboard" : "dashboard" do %> |
26 | 26 | <span></span>Dashboard |
27 | 27 | <% end %> |
28 | + <%= link_to issues_path, :class => current_page?(issues_path) ? "current issues" : "issues" do %> | |
29 | + <span></span>Issues | |
30 | + <% end %> | |
28 | 31 | <%= link_to projects_path, :class => current_page?(projects_path) ? "current project" : "project" do %> |
29 | 32 | <span></span>Projects |
30 | 33 | <% end %> | ... | ... |
... | ... | @@ -0,0 +1,31 @@ |
1 | +!!! | |
2 | +%html | |
3 | + %head | |
4 | + %title | |
5 | + GitLab #{" - #{current_user.name}"} | |
6 | + = stylesheet_link_tag "application" | |
7 | + = javascript_include_tag "application" | |
8 | + = csrf_meta_tags | |
9 | + = javascript_tag do | |
10 | + REQ_URI = "#{request.env["REQUEST_URI"]}"; | |
11 | + REQ_REFFER = "#{request.env["HTTP_REFERER"]}"; | |
12 | + %body{ :class => body_class('project-page'), :id => yield(:boyd_id)} | |
13 | + = render :partial => "layouts/flash" | |
14 | + #container | |
15 | + = render :partial => "layouts/head_panel" | |
16 | + .project-container | |
17 | + .project-sidebar | |
18 | + .fixed | |
19 | + %aside | |
20 | + = link_to issues_path, :class => current_page?(issues_path) ? "current" : nil do | |
21 | + Issues | |
22 | + - unless current_user.assigned_issues.empty? | |
23 | + %span{ :class => "number" }= current_user.assigned_issues.count | |
24 | + = link_to merge_requests_path, :class => current_page?(merge_requests_path) ? "current" : nil do | |
25 | + Merge Requests | |
26 | + - unless current_user.assigned_merge_requests.empty? | |
27 | + %span{ :class => "number" }= current_user.assigned_merge_requests.count | |
28 | + | |
29 | + .project-content | |
30 | + = yield | |
31 | + | ... | ... |
app/views/merge_requests/_merge_request.html.haml
1 | -%a.update-item{:href => project_merge_request_path(@project, merge_request)} | |
1 | +%a.update-item{:href => project_merge_request_path(merge_request.project, merge_request)} | |
2 | 2 | = image_tag gravatar_icon(merge_request.author_email), :class => "left", :width => 40 |
3 | 3 | %span.update-title |
4 | 4 | = merge_request.title |
5 | 5 | %span.update-author |
6 | + - if not @project.present? | |
7 | + %strong= merge_request.project.name | |
8 | + = '-' | |
6 | 9 | %strong= merge_request.author_name |
7 | 10 | authored |
8 | 11 | = time_ago_in_words(merge_request.created_at) | ... | ... |
... | ... | @@ -0,0 +1,24 @@ |
1 | +xml.instruct! | |
2 | +xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do | |
3 | + xml.title "#{@user.name} issues" | |
4 | + xml.link :href => issues_url(:atom, :private_token => @user.private_token), :rel => "self", :type => "application/atom+xml" | |
5 | + xml.link :href => issues_url(:private_token => @user.private_token), :rel => "alternate", :type => "text/html" | |
6 | + xml.id issues_url(:private_token => @user.private_token) | |
7 | + xml.updated @issues.first.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") if @issues.any? | |
8 | + | |
9 | + @issues.each do |issue| | |
10 | + xml.entry do | |
11 | + xml.id project_issue_url(issue.project, issue) | |
12 | + xml.link :href => project_issue_url(issue.project, issue) | |
13 | + xml.title truncate(issue.title, :length => 80) | |
14 | + xml.updated issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") | |
15 | + xml.media :thumbnail, :width => "40", :height => "40", :url => gravatar_icon(issue.author_email) | |
16 | + xml.author do |author| | |
17 | + xml.name issue.author_name | |
18 | + xml.email issue.author_email | |
19 | + end | |
20 | + xml.summary issue.title | |
21 | + end | |
22 | + end | |
23 | +end | |
24 | + | ... | ... |
config/routes.rb
1 | 1 | Gitlab::Application.routes.draw do |
2 | 2 | |
3 | + get "user_issues/index" | |
4 | + | |
3 | 5 | get 'tags'=> 'tags#index' |
4 | 6 | get 'tags/:tag' => 'projects#index' |
5 | 7 | |
... | ... | @@ -21,6 +23,9 @@ Gitlab::Application.routes.draw do |
21 | 23 | put "profile/edit", :to => "profile#social_update" |
22 | 24 | get "profile", :to => "profile#show" |
23 | 25 | get "dashboard", :to => "dashboard#index" |
26 | + get "issues", :to => "user_issues#index", :as => "issues" | |
27 | + get "merge_requests", :to => "user_merge_requests#index", :as => "merge_requests" | |
28 | + | |
24 | 29 | #get "profile/:id", :to => "profile#show" |
25 | 30 | |
26 | 31 | resources :projects, :only => [:new, :create, :index] | ... | ... |
db/schema.rb
... | ... | @@ -13,18 +13,6 @@ |
13 | 13 | |
14 | 14 | ActiveRecord::Schema.define(:version => 20111207211728) do |
15 | 15 | |
16 | - create_table "features", :force => true do |t| | |
17 | - t.string "name" | |
18 | - t.string "branch_name" | |
19 | - t.integer "assignee_id" | |
20 | - t.integer "author_id" | |
21 | - t.integer "project_id" | |
22 | - t.datetime "created_at" | |
23 | - t.datetime "updated_at" | |
24 | - t.string "version" | |
25 | - t.integer "status", :default => 0, :null => false | |
26 | - end | |
27 | - | |
28 | 16 | create_table "issues", :force => true do |t| |
29 | 17 | t.string "title" |
30 | 18 | t.integer "assignee_id" | ... | ... |
spec/models/user_spec.rb
... | ... | @@ -6,6 +6,8 @@ describe User do |
6 | 6 | it { should have_many(:users_projects) } |
7 | 7 | it { should have_many(:issues) } |
8 | 8 | it { should have_many(:assigned_issues) } |
9 | + it { should have_many(:merge_requests) } | |
10 | + it { should have_many(:assigned_merge_requests) } | |
9 | 11 | end |
10 | 12 | |
11 | 13 | describe "Respond to" do | ... | ... |
spec/requests/merge_requests_spec.rb
... | ... | @@ -50,7 +50,7 @@ describe "MergeRequests" do |
50 | 50 | end |
51 | 51 | |
52 | 52 | describe "GET /merge_requests/new" do |
53 | - before do | |
53 | + before do | |
54 | 54 | visit new_project_merge_request_path(project) |
55 | 55 | fill_in "merge_request_title", :with => "Merge Request Title" |
56 | 56 | select "master", :from => "merge_request_source_branch" | ... | ... |
... | ... | @@ -0,0 +1,55 @@ |
1 | +require 'spec_helper' | |
2 | + | |
3 | +describe "User Issues Dashboard" do | |
4 | + describe "GET /issues" do | |
5 | + before do | |
6 | + | |
7 | + login_as :user | |
8 | + | |
9 | + @project1 = Factory :project, | |
10 | + :path => "project1", | |
11 | + :code => "TEST1" | |
12 | + | |
13 | + @project2 = Factory :project, | |
14 | + :path => "project2", | |
15 | + :code => "TEST2" | |
16 | + | |
17 | + @project1.add_access(@user, :read, :write) | |
18 | + @project2.add_access(@user, :read, :write) | |
19 | + | |
20 | + @issue1 = Factory :issue, | |
21 | + :author => @user, | |
22 | + :assignee => @user, | |
23 | + :project => @project1 | |
24 | + | |
25 | + @issue2 = Factory :issue, | |
26 | + :author => @user, | |
27 | + :assignee => @user, | |
28 | + :project => @project2 | |
29 | + | |
30 | + visit issues_path | |
31 | + end | |
32 | + | |
33 | + subject { page } | |
34 | + | |
35 | + it { should have_content(@issue1.title) } | |
36 | + it { should have_content(@issue1.project.name) } | |
37 | + it { should have_content(@issue1.assignee.name) } | |
38 | + | |
39 | + it { should have_content(@issue2.title) } | |
40 | + it { should have_content(@issue2.project.name) } | |
41 | + it { should have_content(@issue2.assignee.name) } | |
42 | + | |
43 | + it "should render atom feed via private token" do | |
44 | + logout | |
45 | + visit issues_path(:atom, :private_token => @user.private_token) | |
46 | + | |
47 | + page.response_headers['Content-Type'].should have_content("application/atom+xml") | |
48 | + page.body.should have_selector("title", :text => "#{@user.name} issues") | |
49 | + page.body.should have_selector("author email", :text => @issue1.author_email) | |
50 | + page.body.should have_selector("entry summary", :text => @issue1.title) | |
51 | + page.body.should have_selector("author email", :text => @issue2.author_email) | |
52 | + page.body.should have_selector("entry summary", :text => @issue2.title) | |
53 | + end | |
54 | + end | |
55 | +end | ... | ... |
... | ... | @@ -0,0 +1,47 @@ |
1 | +require 'spec_helper' | |
2 | + | |
3 | +describe "User MergeRequests" do | |
4 | + describe "GET /issues" do | |
5 | + before do | |
6 | + | |
7 | + login_as :user | |
8 | + | |
9 | + @project1 = Factory :project, | |
10 | + :path => "project1", | |
11 | + :code => "TEST1" | |
12 | + | |
13 | + @project2 = Factory :project, | |
14 | + :path => "project2", | |
15 | + :code => "TEST2" | |
16 | + | |
17 | + @project1.add_access(@user, :read, :write) | |
18 | + @project2.add_access(@user, :read, :write) | |
19 | + | |
20 | + @merge_request1 = Factory :merge_request, | |
21 | + :author => @user, | |
22 | + :assignee => @user, | |
23 | + :project => @project1 | |
24 | + | |
25 | + @merge_request2 = Factory :merge_request, | |
26 | + :author => @user, | |
27 | + :assignee => @user, | |
28 | + :project => @project2 | |
29 | + | |
30 | + visit merge_requests_path | |
31 | + end | |
32 | + | |
33 | + subject { page } | |
34 | + | |
35 | + it { should have_content(@merge_request1.title) } | |
36 | + it { should have_content(@merge_request1.project.name) } | |
37 | + it { should have_content(@merge_request1.target_branch) } | |
38 | + it { should have_content(@merge_request1.source_branch) } | |
39 | + it { should have_content(@merge_request1.assignee.name) } | |
40 | + | |
41 | + it { should have_content(@merge_request2.title) } | |
42 | + it { should have_content(@merge_request2.project.name) } | |
43 | + it { should have_content(@merge_request2.target_branch) } | |
44 | + it { should have_content(@merge_request2.source_branch) } | |
45 | + it { should have_content(@merge_request2.assignee.name) } | |
46 | + end | |
47 | +end | ... | ... |