Merge branch 'improve_large_groups' into 'master'
Improve for large groupsImprove for large groups This merge request is a set of patched to improve application for large groups with more then 100 people. Pages to be improved: ✅ Group#members page ✅ Project#issues page ✅ Project#merge_requests page ✅ Project#new_issue page ✅ Project#new_mr page ✅ Project#members page
This commit is contained in:
commit
f745c6e5b5
27 changed files with 347 additions and 174 deletions
|
@ -3,6 +3,7 @@
|
|||
user_path: "/api/:version/users/:id.json"
|
||||
notes_path: "/api/:version/projects/:id/notes.json"
|
||||
namespaces_path: "/api/:version/namespaces.json"
|
||||
project_users_path: "/api/:version/projects/:id/users.json"
|
||||
|
||||
# Get 20 (depends on api) recent notes
|
||||
# and sort the ascending from oldest to newest
|
||||
|
@ -50,6 +51,23 @@
|
|||
).done (users) ->
|
||||
callback(users)
|
||||
|
||||
# Return project users list. Filtered by query
|
||||
# Only active users retrieved
|
||||
projectUsers: (project_id, query, callback) ->
|
||||
url = Api.buildUrl(Api.project_users_path)
|
||||
url = url.replace(':id', project_id)
|
||||
|
||||
$.ajax(
|
||||
url: url
|
||||
data:
|
||||
private_token: gon.api_token
|
||||
search: query
|
||||
per_page: 20
|
||||
active: true
|
||||
dataType: "json"
|
||||
).done (users) ->
|
||||
callback(users)
|
||||
|
||||
# Return namespaces list. Filtered by query
|
||||
namespaces: (query, callback) ->
|
||||
url = Api.buildUrl(Api.namespaces_path)
|
||||
|
|
44
app/assets/javascripts/project_users_select.js.coffee
Normal file
44
app/assets/javascripts/project_users_select.js.coffee
Normal file
|
@ -0,0 +1,44 @@
|
|||
$ ->
|
||||
projectUserFormatResult = (user) ->
|
||||
if user.avatar_url
|
||||
avatar = user.avatar_url
|
||||
else if gon.gravatar_enabled
|
||||
avatar = gon.gravatar_url
|
||||
avatar = avatar.replace('%{hash}', md5(user.email))
|
||||
avatar = avatar.replace('%{size}', '24')
|
||||
else
|
||||
avatar = gon.relative_url_root + "/assets/no_avatar.png"
|
||||
|
||||
"<div class='user-result'>
|
||||
<div class='user-image'><img class='avatar s24' src='#{avatar}'></div>
|
||||
<div class='user-name'>#{user.name}</div>
|
||||
<div class='user-username'>#{user.username}</div>
|
||||
</div>"
|
||||
|
||||
projectUserFormatSelection = (user) ->
|
||||
user.name
|
||||
|
||||
$('.ajax-project-users-select').each (i, select) ->
|
||||
project_id = $('body').data('project-id')
|
||||
|
||||
$(select).select2
|
||||
placeholder: $(select).data('placeholder') || "Search for a user"
|
||||
multiple: $(select).hasClass('multiselect')
|
||||
minimumInputLength: 0
|
||||
query: (query) ->
|
||||
Api.projectUsers project_id, query.term, (users) ->
|
||||
data = { results: users }
|
||||
query.callback(data)
|
||||
|
||||
initSelection: (element, callback) ->
|
||||
id = $(element).val()
|
||||
if id isnt ""
|
||||
Api.user(id, callback)
|
||||
|
||||
|
||||
formatResult: projectUserFormatResult
|
||||
formatSelection: projectUserFormatSelection
|
||||
dropdownCssClass: "ajax-project-users-dropdown"
|
||||
dropdownAutoWidth: true
|
||||
escapeMarkup: (m) -> # we do not want to escape markup since we are displaying html in results
|
||||
m
|
|
@ -1,7 +1,7 @@
|
|||
$ ->
|
||||
userFormatResult = (user) ->
|
||||
if user.avatar
|
||||
avatar = user.avatar.url
|
||||
if user.avatar_url
|
||||
avatar = user.avatar_url
|
||||
else if gon.gravatar_enabled
|
||||
avatar = gon.gravatar_url
|
||||
avatar = avatar.replace('%{hash}', md5(user.email))
|
||||
|
|
|
@ -65,6 +65,7 @@
|
|||
@import "sections/wall.scss";
|
||||
@import "sections/dashboard.scss";
|
||||
@import "sections/stat_graph.scss";
|
||||
@import "sections/groups.scss";
|
||||
|
||||
/**
|
||||
* Code ighlight
|
||||
|
|
|
@ -112,6 +112,7 @@ pre.well-pre {
|
|||
.dropdown-menu > li > a:hover,
|
||||
.dropdown-menu > li > a:focus {
|
||||
background: #29b;
|
||||
color: #FFF
|
||||
}
|
||||
|
||||
.breadcrumb > li + li:before {
|
||||
|
|
|
@ -51,3 +51,27 @@ label {
|
|||
.input-mn-300 {
|
||||
min-width: 300px;
|
||||
}
|
||||
|
||||
.custom-form-control {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
@media (min-width: $screen-sm-min) {
|
||||
.custom-form-control {
|
||||
width: 150px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Medium devices (desktops, 992px and up) */
|
||||
@media (min-width: $screen-md-min) {
|
||||
.custom-form-control {
|
||||
width: 170px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Large devices (large desktops, 1200px and up) */
|
||||
@media (min-width: $screen-lg-min) {
|
||||
.custom-form-control {
|
||||
width: 200px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
/** Select2 selectbox style override **/
|
||||
|
||||
.select2-container, .select2-container.select2-drop-above {
|
||||
.select2-choice {
|
||||
background: #FFF;
|
||||
|
@ -12,9 +11,13 @@
|
|||
}
|
||||
|
||||
.select2-drop-active {
|
||||
border: 1px solid #BBB;
|
||||
border: 1px solid #BBB !important;
|
||||
margin-top: 4px;
|
||||
|
||||
&.select2-drop-above {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.select2-search input {
|
||||
background: #fafafa;
|
||||
border-color: #DDD;
|
||||
|
@ -78,3 +81,9 @@ select {
|
|||
.project-refs-form .select2-container {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.ajax-users-dropdown, .ajax-project-users-dropdown {
|
||||
.select2-search {
|
||||
padding-top: 4px;
|
||||
}
|
||||
}
|
||||
|
|
9
app/assets/stylesheets/sections/groups.scss
Normal file
9
app/assets/stylesheets/sections/groups.scss
Normal file
|
@ -0,0 +1,9 @@
|
|||
.new-group-member-holder {
|
||||
margin-top: 50px;
|
||||
background: #f9f9f9;
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.member-search-form {
|
||||
float: left;
|
||||
}
|
|
@ -14,8 +14,8 @@
|
|||
|
||||
.issue-check {
|
||||
float: left;
|
||||
padding: 8px 0;
|
||||
padding-right: 8px;
|
||||
margin-bottom: 10px;
|
||||
min-width: 15px;
|
||||
}
|
||||
|
||||
|
@ -38,13 +38,21 @@
|
|||
}
|
||||
}
|
||||
|
||||
input.check_all_issues {
|
||||
.check-all-holder {
|
||||
height: 32px;
|
||||
float: left;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
margin-right: 10px;
|
||||
position: relative;
|
||||
top: 13px;
|
||||
margin-right: 12px;
|
||||
padding: 6px 10px;
|
||||
border: 1px solid #ccc;
|
||||
@include border-radius(4px);
|
||||
|
||||
|
||||
input.check_all_issues {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
position: relative;
|
||||
top: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
.issues_content {
|
||||
|
@ -91,6 +99,13 @@ input.check_all_issues {
|
|||
.update_selected_issues {
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.select2-container .select2-choice {
|
||||
height: 32px;
|
||||
line-height: 28px;
|
||||
color: #444 !important;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -63,7 +63,14 @@ class GroupsController < ApplicationController
|
|||
|
||||
def members
|
||||
@project = group.projects.find(params[:project_id]) if params[:project_id]
|
||||
@members = group.users_groups.order('group_access DESC')
|
||||
@members = group.users_groups
|
||||
|
||||
if params[:search].present?
|
||||
users = group.users.search(params[:search])
|
||||
@members = @members.where(user_id: users)
|
||||
end
|
||||
|
||||
@members = @members.order('group_access DESC').page(params[:page]).per(50)
|
||||
@users_group = UsersGroup.new
|
||||
end
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ class Projects::IssuesController < Projects::ApplicationController
|
|||
@milestone = @project.milestones.find(milestone_id) if milestone_id.present? && !milestone_id.to_i.zero?
|
||||
sort_param = params[:sort] || 'newest'
|
||||
@sort = sort_param.humanize unless sort_param.empty?
|
||||
|
||||
@assignees = User.where(id: @project.issues.pluck(:assignee_id))
|
||||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
|
|
|
@ -28,6 +28,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
|||
assignee_id, milestone_id = params[:assignee_id], params[:milestone_id]
|
||||
@assignee = @project.team.find(assignee_id) if assignee_id.present? && !assignee_id.to_i.zero?
|
||||
@milestone = @project.milestones.find(milestone_id) if milestone_id.present? && !milestone_id.to_i.zero?
|
||||
@assignees = User.where(id: @project.merge_requests.pluck(:assignee_id))
|
||||
end
|
||||
|
||||
def show
|
||||
|
|
|
@ -162,15 +162,6 @@ module ApplicationHelper
|
|||
|
||||
alias_method :url_to_image, :image_url
|
||||
|
||||
def users_select_tag(id, opts = {})
|
||||
css_class = "ajax-users-select "
|
||||
css_class << "multiselect " if opts[:multiple]
|
||||
css_class << (opts[:class] || '')
|
||||
value = opts[:selected] || ''
|
||||
|
||||
hidden_field_tag(id, value, class: css_class)
|
||||
end
|
||||
|
||||
def body_data_page
|
||||
path = controller.controller_path.split('/')
|
||||
namespace = path.first if path.second
|
||||
|
|
|
@ -70,11 +70,11 @@ module IssuesHelper
|
|||
end
|
||||
|
||||
def bulk_update_milestone_options
|
||||
options_for_select(["None (backlog)", nil]) + options_from_collection_for_select(project_active_milestones, "id", "title", params[:milestone_id])
|
||||
options_for_select(["None (backlog)"]) + options_from_collection_for_select(project_active_milestones, "id", "title", params[:milestone_id])
|
||||
end
|
||||
|
||||
def bulk_update_assignee_options
|
||||
options_for_select(["None (unassigned)", nil]) + options_from_collection_for_select(@project.team.members, "id", "name", params[:assignee_id])
|
||||
options_for_select(["None (unassigned)"]) + options_from_collection_for_select(@project.team.members, "id", "name", params[:assignee_id])
|
||||
end
|
||||
|
||||
def assignee_options object
|
||||
|
|
20
app/helpers/selects_helper.rb
Normal file
20
app/helpers/selects_helper.rb
Normal file
|
@ -0,0 +1,20 @@
|
|||
module SelectsHelper
|
||||
def users_select_tag(id, opts = {})
|
||||
css_class = "ajax-users-select "
|
||||
css_class << "multiselect " if opts[:multiple]
|
||||
css_class << (opts[:class] || '')
|
||||
value = opts[:selected] || ''
|
||||
|
||||
hidden_field_tag(id, value, class: css_class)
|
||||
end
|
||||
|
||||
def project_users_select_tag(id, opts = {})
|
||||
css_class = "ajax-project-users-select "
|
||||
css_class << "multiselect " if opts[:multiple]
|
||||
css_class << (opts[:class] || '')
|
||||
value = opts[:selected] || ''
|
||||
placeholder = opts[:placeholder] || 'Select user'
|
||||
|
||||
hidden_field_tag(id, value, class: css_class, 'data-placeholder' => placeholder)
|
||||
end
|
||||
end
|
|
@ -1,15 +1,8 @@
|
|||
= form_for @users_group, url: group_users_groups_path(@group), html: { class: 'form-horizontal users-group-form' } do |f|
|
||||
%h4.append-bottom-20
|
||||
New member(s) for
|
||||
%strong #{@group.name}
|
||||
group
|
||||
|
||||
%p 1. Choose users you want in the group
|
||||
.form-group
|
||||
= f.label :user_ids, "People", class: 'control-label'
|
||||
.col-sm-10= users_select_tag(:user_ids, multiple: true, class: 'input-large')
|
||||
|
||||
%p 2. Set access level for them
|
||||
.form-group
|
||||
= f.label :group_access, "Group Access", class: 'control-label'
|
||||
.col-sm-10= select_tag :group_access, options_for_select(UsersGroup.group_access_roles, @users_group.group_access), class: "project-access-select select2"
|
||||
|
|
|
@ -6,14 +6,34 @@
|
|||
%strong= link_to "here", help_permissions_path, class: "vlink"
|
||||
|
||||
%hr
|
||||
.ui-box
|
||||
|
||||
.clearfix
|
||||
= form_tag members_group_path(@group), method: :get, class: 'form-inline member-search-form' do
|
||||
.form-group
|
||||
= search_field_tag :search, params[:search], { placeholder: 'Find member by name', class: 'form-control search-text-input input-mn-300' }
|
||||
= submit_tag 'Search', class: 'btn'
|
||||
|
||||
- if current_user.can? :manage_group, @group
|
||||
.pull-right
|
||||
= link_to '#', class: 'btn btn-new js-toggle-visibility-link' do
|
||||
Add members
|
||||
%i.icon-chevron-down
|
||||
|
||||
.js-toggle-visibility-container.hide.new-group-member-holder
|
||||
= render "new_group_member"
|
||||
|
||||
.ui-box.prepend-top-20
|
||||
.title
|
||||
%strong #{@group.name}
|
||||
group members
|
||||
%small
|
||||
(#{@members.count})
|
||||
(#{@members.total_count})
|
||||
%ul.well-list
|
||||
- @members.each do |member|
|
||||
= render 'users_groups/users_group', member: member, show_controls: true
|
||||
- if current_user.can? :manage_group, @group
|
||||
= render "new_group_member"
|
||||
= paginate @members, theme: 'gitlab'
|
||||
|
||||
:coffeescript
|
||||
$('form.member-search-form').on 'submit', (event) ->
|
||||
event.preventDefault()
|
||||
Turbolinks.visit @.action + '?' + $(@).serialize()
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
%i.icon-user
|
||||
Assign to
|
||||
.col-sm-10
|
||||
= f.select(:assignee_id, assignee_options(@issue), { include_blank: "Select a user" }, {class: 'select2'})
|
||||
= project_users_select_tag('issue[assignee_id]', placeholder: 'Select a user', class: 'custom-form-control')
|
||||
|
||||
= link_to 'Assign to me', '#', class: 'btn btn-small assign-to-me-link'
|
||||
.form-group
|
||||
|
|
|
@ -17,10 +17,10 @@
|
|||
|
||||
%li.pull-right
|
||||
.pull-right
|
||||
= form_tag project_issues_path(@project), method: :get, id: "issue_search_form", class: 'inline issue-search-form' do
|
||||
= form_tag project_issues_path(@project), method: :get, id: "issue_search_form", class: 'pull-left issue-search-form' do
|
||||
.append-right-10.hidden-xs.hidden-sm
|
||||
= search_field_tag :issue_search, nil, { placeholder: 'Filter by title or description', class: 'form-control issue_search search-text-input input-mn-300' }
|
||||
- if can? current_user, :write_issue, @project
|
||||
= link_to new_project_issue_path(@project, issue: { assignee_id: params[:assignee_id], milestone_id: params[:milestone_id]}), class: "btn btn-new", title: "New Issue", id: "new_issue_link" do
|
||||
= link_to new_project_issue_path(@project, issue: { assignee_id: params[:assignee_id], milestone_id: params[:milestone_id]}), class: "btn btn-new pull-left", title: "New Issue", id: "new_issue_link" do
|
||||
%i.icon-plus
|
||||
New Issue
|
||||
|
|
|
@ -1,87 +1,86 @@
|
|||
.ui-box
|
||||
.title
|
||||
.append-bottom-10
|
||||
.check-all-holder
|
||||
= check_box_tag "check_all_issues", nil, false, class: "check_all_issues left"
|
||||
.clearfix
|
||||
.issues_bulk_update.hide
|
||||
= form_tag bulk_update_project_issues_path(@project), method: :post do
|
||||
%span Update selected issues with
|
||||
= select_tag('update[status]', options_for_select(['open', 'closed']), prompt: "Status")
|
||||
= select_tag('update[assignee_id]', bulk_update_assignee_options, prompt: "Assignee")
|
||||
= select_tag('update[milestone_id]', bulk_update_milestone_options, prompt: "Milestone")
|
||||
= hidden_field_tag 'update[issues_ids]', []
|
||||
= hidden_field_tag :status, params[:status]
|
||||
= button_tag "Save", class: "btn update_selected_issues btn-small btn-save"
|
||||
.issues-filters
|
||||
%span Filter by
|
||||
.dropdown.inline.prepend-left-10
|
||||
%a.dropdown-toggle.btn.btn-small{href: '#', "data-toggle" => "dropdown"}
|
||||
%i.icon-tags
|
||||
%span.light labels:
|
||||
- if params[:label_name].present?
|
||||
%strong= params[:label_name]
|
||||
- else
|
||||
Any
|
||||
%b.caret
|
||||
%ul.dropdown-menu
|
||||
%li
|
||||
= link_to project_filter_path(label_name: nil) do
|
||||
Any
|
||||
- issue_label_names.each do |label_name|
|
||||
%li
|
||||
= link_to project_filter_path(label_name: label_name) do
|
||||
%span{class: "label #{label_css_class(label_name)}"}
|
||||
%i.icon-tag
|
||||
= label_name
|
||||
.dropdown.inline.prepend-left-10
|
||||
%a.dropdown-toggle.btn.btn-small{href: '#', "data-toggle" => "dropdown"}
|
||||
%i.icon-user
|
||||
%span.light assignee:
|
||||
- if @assignee.present?
|
||||
%strong= @assignee.name
|
||||
- elsif params[:assignee_id] == "0"
|
||||
Unassigned
|
||||
- else
|
||||
Any
|
||||
%b.caret
|
||||
%ul.dropdown-menu
|
||||
%li
|
||||
= link_to project_filter_path(assignee_id: nil) do
|
||||
Any
|
||||
= link_to project_filter_path(assignee_id: 0) do
|
||||
Unassigned
|
||||
- @project.team.members.sort_by(&:name).each do |user|
|
||||
%li
|
||||
= link_to project_filter_path(assignee_id: user.id) do
|
||||
= image_tag avatar_icon(user.email), class: "avatar s16", alt: ''
|
||||
= user.name
|
||||
.issues-filters
|
||||
.dropdown.inline
|
||||
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
|
||||
%i.icon-tags
|
||||
%span.light labels:
|
||||
- if params[:label_name].present?
|
||||
%strong= params[:label_name]
|
||||
- else
|
||||
Any
|
||||
%b.caret
|
||||
%ul.dropdown-menu
|
||||
%li
|
||||
= link_to project_filter_path(label_name: nil) do
|
||||
Any
|
||||
- issue_label_names.each do |label_name|
|
||||
%li
|
||||
= link_to project_filter_path(label_name: label_name) do
|
||||
%span{class: "label #{label_css_class(label_name)}"}
|
||||
%i.icon-tag
|
||||
= label_name
|
||||
.dropdown.inline.prepend-left-10
|
||||
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
|
||||
%i.icon-user
|
||||
%span.light assignee:
|
||||
- if @assignee.present?
|
||||
%strong= @assignee.name
|
||||
- elsif params[:assignee_id] == "0"
|
||||
Unassigned
|
||||
- else
|
||||
Any
|
||||
%b.caret
|
||||
%ul.dropdown-menu
|
||||
%li
|
||||
= link_to project_filter_path(assignee_id: nil) do
|
||||
Any
|
||||
= link_to project_filter_path(assignee_id: 0) do
|
||||
Unassigned
|
||||
- @assignees.sort_by(&:name).each do |user|
|
||||
%li
|
||||
= link_to project_filter_path(assignee_id: user.id) do
|
||||
= image_tag avatar_icon(user.email), class: "avatar s16", alt: ''
|
||||
= user.name
|
||||
|
||||
.dropdown.inline.prepend-left-10
|
||||
%a.dropdown-toggle.btn.btn-small{href: '#', "data-toggle" => "dropdown"}
|
||||
%i.icon-time
|
||||
%span.light milestone:
|
||||
- if @milestone.present?
|
||||
%strong= @milestone.title
|
||||
- elsif params[:milestone_id] == "0"
|
||||
None (backlog)
|
||||
- else
|
||||
Any
|
||||
%b.caret
|
||||
%ul.dropdown-menu
|
||||
%li
|
||||
= link_to project_filter_path(milestone_id: nil) do
|
||||
Any
|
||||
= link_to project_filter_path(milestone_id: 0) do
|
||||
None (backlog)
|
||||
- project_active_milestones.each do |milestone|
|
||||
%li
|
||||
= link_to project_filter_path(milestone_id: milestone.id) do
|
||||
%strong= milestone.title
|
||||
%small.light= milestone.expires_at
|
||||
.dropdown.inline.prepend-left-10
|
||||
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
|
||||
%i.icon-time
|
||||
%span.light milestone:
|
||||
- if @milestone.present?
|
||||
%strong= @milestone.title
|
||||
- elsif params[:milestone_id] == "0"
|
||||
None (backlog)
|
||||
- else
|
||||
Any
|
||||
%b.caret
|
||||
%ul.dropdown-menu
|
||||
%li
|
||||
= link_to project_filter_path(milestone_id: nil) do
|
||||
Any
|
||||
= link_to project_filter_path(milestone_id: 0) do
|
||||
None (backlog)
|
||||
- project_active_milestones.each do |milestone|
|
||||
%li
|
||||
= link_to project_filter_path(milestone_id: milestone.id) do
|
||||
%strong= milestone.title
|
||||
%small.light= milestone.expires_at
|
||||
|
||||
.pull-right
|
||||
= render 'shared/sort_dropdown'
|
||||
.pull-right
|
||||
= render 'shared/sort_dropdown'
|
||||
|
||||
.clearfix
|
||||
.issues_bulk_update.hide
|
||||
= form_tag bulk_update_project_issues_path(@project), method: :post do
|
||||
= select_tag('update[status]', options_for_select(['Open', 'Closed']), prompt: "Status")
|
||||
= project_users_select_tag('update[assignee_id]', placeholder: 'Assignee')
|
||||
= select_tag('update[milestone_id]', bulk_update_milestone_options, prompt: "Milestone")
|
||||
= hidden_field_tag 'update[issues_ids]', []
|
||||
= hidden_field_tag :status, params[:status]
|
||||
= button_tag "Update issues", class: "btn update_selected_issues btn-save"
|
||||
|
||||
.ui-box
|
||||
%ul.well-list.issues-list
|
||||
= render @issues
|
||||
- if @issues.blank?
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
%i.icon-user
|
||||
Assign to
|
||||
.col-sm-10
|
||||
= f.select(:assignee_id, assignee_options(@merge_request), { include_blank: "Select a user" }, {class: 'select2'})
|
||||
= project_users_select_tag('merge_request[assignee_id]', placeholder: 'Select a user', class: 'custom-form-control')
|
||||
|
||||
= link_to 'Assign to me', '#', class: 'btn btn-small assign-to-me-link'
|
||||
.form-group
|
||||
|
|
|
@ -10,59 +10,57 @@
|
|||
.col-md-3
|
||||
= render 'shared/project_filter', project_entities_path: project_merge_requests_path(@project)
|
||||
.col-md-9
|
||||
.mr-filters.append-bottom-10
|
||||
.dropdown.inline
|
||||
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
|
||||
%i.icon-user
|
||||
%span.light assignee:
|
||||
- if @assignee.present?
|
||||
%strong= @assignee.name
|
||||
- elsif params[:assignee_id] == "0"
|
||||
Unassigned
|
||||
- else
|
||||
Any
|
||||
%b.caret
|
||||
%ul.dropdown-menu
|
||||
%li
|
||||
= link_to project_filter_path(assignee_id: nil) do
|
||||
Any
|
||||
= link_to project_filter_path(assignee_id: 0) do
|
||||
Unassigned
|
||||
- @assignees.sort_by(&:name).each do |user|
|
||||
%li
|
||||
= link_to project_filter_path(assignee_id: user.id) do
|
||||
= image_tag avatar_icon(user.email), class: "avatar s16", alt: ''
|
||||
= user.name
|
||||
|
||||
.dropdown.inline.prepend-left-10
|
||||
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
|
||||
%i.icon-time
|
||||
%span.light milestone:
|
||||
- if @milestone.present?
|
||||
%strong= @milestone.title
|
||||
- elsif params[:milestone_id] == "0"
|
||||
None (backlog)
|
||||
- else
|
||||
Any
|
||||
%b.caret
|
||||
%ul.dropdown-menu
|
||||
%li
|
||||
= link_to project_filter_path(milestone_id: nil) do
|
||||
Any
|
||||
= link_to project_filter_path(milestone_id: 0) do
|
||||
None (backlog)
|
||||
- project_active_milestones.each do |milestone|
|
||||
%li
|
||||
= link_to project_filter_path(milestone_id: milestone.id) do
|
||||
%strong= milestone.title
|
||||
%small.light= milestone.expires_at
|
||||
|
||||
.pull-right
|
||||
= render 'shared/sort_dropdown'
|
||||
|
||||
.ui-box
|
||||
.title
|
||||
.mr-filters
|
||||
%span Filter by
|
||||
.dropdown.inline.prepend-left-10
|
||||
%a.dropdown-toggle.btn.btn-small{href: '#', "data-toggle" => "dropdown"}
|
||||
%i.icon-user
|
||||
%span.light assignee:
|
||||
- if @assignee.present?
|
||||
%strong= @assignee.name
|
||||
- elsif params[:assignee_id] == "0"
|
||||
Unassigned
|
||||
- else
|
||||
Any
|
||||
%b.caret
|
||||
%ul.dropdown-menu
|
||||
%li
|
||||
= link_to project_filter_path(assignee_id: nil) do
|
||||
Any
|
||||
= link_to project_filter_path(assignee_id: 0) do
|
||||
Unassigned
|
||||
- @project.team.members.sort_by(&:name).each do |user|
|
||||
%li
|
||||
= link_to project_filter_path(assignee_id: user.id) do
|
||||
= image_tag avatar_icon(user.email), class: "avatar s16", alt: ''
|
||||
= user.name
|
||||
|
||||
.dropdown.inline.prepend-left-10
|
||||
%a.dropdown-toggle.btn.btn-small{href: '#', "data-toggle" => "dropdown"}
|
||||
%i.icon-time
|
||||
%span.light milestone:
|
||||
- if @milestone.present?
|
||||
%strong= @milestone.title
|
||||
- elsif params[:milestone_id] == "0"
|
||||
None (backlog)
|
||||
- else
|
||||
Any
|
||||
%b.caret
|
||||
%ul.dropdown-menu
|
||||
%li
|
||||
= link_to project_filter_path(milestone_id: nil) do
|
||||
Any
|
||||
= link_to project_filter_path(milestone_id: 0) do
|
||||
None (backlog)
|
||||
- project_active_milestones.each do |milestone|
|
||||
%li
|
||||
= link_to project_filter_path(milestone_id: milestone.id) do
|
||||
%strong= milestone.title
|
||||
%small.light= milestone.expires_at
|
||||
|
||||
.pull-right
|
||||
= render 'shared/sort_dropdown'
|
||||
|
||||
%ul.well-list.mr-list
|
||||
= render @merge_requests
|
||||
- if @merge_requests.blank?
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
- group_users_count = @group.users_groups.count
|
||||
.ui-box
|
||||
.title
|
||||
%strong #{@group.name}
|
||||
group members (#{@group.users_groups.count})
|
||||
group members (#{group_users_count})
|
||||
.pull-right
|
||||
= link_to members_group_path(@group), class: 'btn btn-small' do
|
||||
%i.icon-edit
|
||||
%ul.well-list
|
||||
- @group.users_groups.order('group_access DESC').each do |member|
|
||||
- @group.users_groups.order('group_access DESC').limit(20).each do |member|
|
||||
= render 'users_groups/users_group', member: member, show_controls: false
|
||||
- if group_users_count > 20
|
||||
%li
|
||||
and #{group_users_count - 20} more. For full list visit #{link_to 'group members page', members_group_path(@group)}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
.dropdown.inline.prepend-left-10
|
||||
%a.dropdown-toggle.btn.btn-small{href: '#', "data-toggle" => "dropdown"}
|
||||
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
|
||||
%span.light sort:
|
||||
- if @sort.present?
|
||||
= @sort
|
||||
|
|
|
@ -29,6 +29,7 @@ class Groups < Spinach::FeatureSteps
|
|||
|
||||
And 'I select user "Mary Jane" from list with role "Reporter"' do
|
||||
user = User.find_by(name: "Mary Jane") || create(:user, name: "Mary Jane")
|
||||
click_link 'Add members'
|
||||
within ".users-group-form" do
|
||||
select2(user.id, from: "#user_ids", multiple: true)
|
||||
select "Reporter", from: "group_access"
|
||||
|
|
|
@ -6,6 +6,12 @@ module API
|
|||
expose :is_admin?, as: :is_admin
|
||||
expose :can_create_group?, as: :can_create_group
|
||||
expose :can_create_project?, as: :can_create_project
|
||||
|
||||
expose :avatar_url do |user, options|
|
||||
if user.avatar.present?
|
||||
user.avatar.url
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class UserSafe < Grape::Entity
|
||||
|
|
|
@ -11,7 +11,7 @@ module API
|
|||
end
|
||||
not_found!
|
||||
end
|
||||
|
||||
|
||||
def map_public_to_visibility_level(attrs)
|
||||
publik = attrs.delete(:public)
|
||||
publik = [ true, 1, '1', 't', 'T', 'true', 'TRUE', 'on', 'ON' ].include?(publik)
|
||||
|
@ -308,6 +308,18 @@ module API
|
|||
projects = Project.where("(id in (?) OR visibility_level in (?)) AND (name LIKE (?))", ids, visibility_levels, "%#{params[:query]}%")
|
||||
present paginate(projects), with: Entities::Project
|
||||
end
|
||||
|
||||
|
||||
# Get a users list
|
||||
#
|
||||
# Example Request:
|
||||
# GET /users
|
||||
get ':id/users' do
|
||||
@users = User.where(id: user_project.team.users.map(&:id))
|
||||
@users = @users.search(params[:search]) if params[:search].present?
|
||||
@users = paginate @users
|
||||
present @users, with: Entities::User
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue