Commit f745c6e5 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

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
parents 361a8781 fd12e5c6
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
user_path: "/api/:version/users/:id.json" user_path: "/api/:version/users/:id.json"
notes_path: "/api/:version/projects/:id/notes.json" notes_path: "/api/:version/projects/:id/notes.json"
namespaces_path: "/api/:version/namespaces.json" namespaces_path: "/api/:version/namespaces.json"
project_users_path: "/api/:version/projects/:id/users.json"
# Get 20 (depends on api) recent notes # Get 20 (depends on api) recent notes
# and sort the ascending from oldest to newest # and sort the ascending from oldest to newest
...@@ -50,6 +51,23 @@ ...@@ -50,6 +51,23 @@
).done (users) -> ).done (users) ->
callback(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 # Return namespaces list. Filtered by query
namespaces: (query, callback) -> namespaces: (query, callback) ->
url = Api.buildUrl(Api.namespaces_path) url = Api.buildUrl(Api.namespaces_path)
......
$ ->
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
$ -> $ ->
userFormatResult = (user) -> userFormatResult = (user) ->
if user.avatar if user.avatar_url
avatar = user.avatar.url avatar = user.avatar_url
else if gon.gravatar_enabled else if gon.gravatar_enabled
avatar = gon.gravatar_url avatar = gon.gravatar_url
avatar = avatar.replace('%{hash}', md5(user.email)) avatar = avatar.replace('%{hash}', md5(user.email))
......
...@@ -65,6 +65,7 @@ ...@@ -65,6 +65,7 @@
@import "sections/wall.scss"; @import "sections/wall.scss";
@import "sections/dashboard.scss"; @import "sections/dashboard.scss";
@import "sections/stat_graph.scss"; @import "sections/stat_graph.scss";
@import "sections/groups.scss";
/** /**
* Code ighlight * Code ighlight
......
...@@ -112,6 +112,7 @@ pre.well-pre { ...@@ -112,6 +112,7 @@ pre.well-pre {
.dropdown-menu > li > a:hover, .dropdown-menu > li > a:hover,
.dropdown-menu > li > a:focus { .dropdown-menu > li > a:focus {
background: #29b; background: #29b;
color: #FFF
} }
.breadcrumb > li + li:before { .breadcrumb > li + li:before {
......
...@@ -51,3 +51,27 @@ label { ...@@ -51,3 +51,27 @@ label {
.input-mn-300 { .input-mn-300 {
min-width: 300px; 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;
}
}
/** Select2 selectbox style override **/ /** Select2 selectbox style override **/
.select2-container, .select2-container.select2-drop-above { .select2-container, .select2-container.select2-drop-above {
.select2-choice { .select2-choice {
background: #FFF; background: #FFF;
...@@ -12,9 +11,13 @@ ...@@ -12,9 +11,13 @@
} }
.select2-drop-active { .select2-drop-active {
border: 1px solid #BBB; border: 1px solid #BBB !important;
margin-top: 4px; margin-top: 4px;
&.select2-drop-above {
margin-bottom: 8px;
}
.select2-search input { .select2-search input {
background: #fafafa; background: #fafafa;
border-color: #DDD; border-color: #DDD;
...@@ -78,3 +81,9 @@ select { ...@@ -78,3 +81,9 @@ select {
.project-refs-form .select2-container { .project-refs-form .select2-container {
margin-right: 10px; margin-right: 10px;
} }
.ajax-users-dropdown, .ajax-project-users-dropdown {
.select2-search {
padding-top: 4px;
}
}
.new-group-member-holder {
margin-top: 50px;
background: #f9f9f9;
padding-top: 20px;
}
.member-search-form {
float: left;
}
...@@ -14,8 +14,8 @@ ...@@ -14,8 +14,8 @@
.issue-check { .issue-check {
float: left; float: left;
padding: 8px 0;
padding-right: 8px; padding-right: 8px;
margin-bottom: 10px;
min-width: 15px; min-width: 15px;
} }
...@@ -38,13 +38,21 @@ ...@@ -38,13 +38,21 @@
} }
} }
input.check_all_issues { .check-all-holder {
height: 32px;
float: left; float: left;
margin-right: 12px;
padding: 6px 10px;
border: 1px solid #ccc;
@include border-radius(4px);
input.check_all_issues {
padding: 0; padding: 0;
margin: 0; margin: 0;
margin-right: 10px;
position: relative; position: relative;
top: 13px; top: 3px;
}
} }
.issues_content { .issues_content {
...@@ -91,6 +99,13 @@ input.check_all_issues { ...@@ -91,6 +99,13 @@ input.check_all_issues {
.update_selected_issues { .update_selected_issues {
margin-left: 4px; 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 ...@@ -63,7 +63,14 @@ class GroupsController < ApplicationController
def members def members
@project = group.projects.find(params[:project_id]) if params[:project_id] @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 @users_group = UsersGroup.new
end end
......
...@@ -28,7 +28,7 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -28,7 +28,7 @@ class Projects::IssuesController < Projects::ApplicationController
@milestone = @project.milestones.find(milestone_id) if milestone_id.present? && !milestone_id.to_i.zero? @milestone = @project.milestones.find(milestone_id) if milestone_id.present? && !milestone_id.to_i.zero?
sort_param = params[:sort] || 'newest' sort_param = params[:sort] || 'newest'
@sort = sort_param.humanize unless sort_param.empty? @sort = sort_param.humanize unless sort_param.empty?
@assignees = User.where(id: @project.issues.pluck(:assignee_id))
respond_to do |format| respond_to do |format|
format.html format.html
......
...@@ -28,6 +28,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -28,6 +28,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
assignee_id, milestone_id = params[:assignee_id], params[:milestone_id] 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? @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? @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 end
def show def show
......
...@@ -162,15 +162,6 @@ module ApplicationHelper ...@@ -162,15 +162,6 @@ module ApplicationHelper
alias_method :url_to_image, :image_url 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 def body_data_page
path = controller.controller_path.split('/') path = controller.controller_path.split('/')
namespace = path.first if path.second namespace = path.first if path.second
......
...@@ -70,11 +70,11 @@ module IssuesHelper ...@@ -70,11 +70,11 @@ module IssuesHelper
end end
def bulk_update_milestone_options 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 end
def bulk_update_assignee_options 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 end
def assignee_options object def assignee_options object
......
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
= form_for @users_group, url: group_users_groups_path(@group), html: { class: 'form-horizontal users-group-form' } do |f| = 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 .form-group
= f.label :user_ids, "People", class: 'control-label' = f.label :user_ids, "People", class: 'control-label'
.col-sm-10= users_select_tag(:user_ids, multiple: true, class: 'input-large') .col-sm-10= users_select_tag(:user_ids, multiple: true, class: 'input-large')
%p 2. Set access level for them
.form-group .form-group
= f.label :group_access, "Group Access", class: 'control-label' = 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" .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 @@ ...@@ -6,14 +6,34 @@
%strong= link_to "here", help_permissions_path, class: "vlink" %strong= link_to "here", help_permissions_path, class: "vlink"
%hr %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 .title
%strong #{@group.name} %strong #{@group.name}
group members group members
%small %small
(#{@members.count}) (#{@members.total_count})
%ul.well-list %ul.well-list
- @members.each do |member| - @members.each do |member|
= render 'users_groups/users_group', member: member, show_controls: true = render 'users_groups/users_group', member: member, show_controls: true
- if current_user.can? :manage_group, @group = paginate @members, theme: 'gitlab'
= render "new_group_member"
:coffeescript
$('form.member-search-form').on 'submit', (event) ->
event.preventDefault()
Turbolinks.visit @.action + '?' + $(@).serialize()
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
%i.icon-user %i.icon-user
Assign to Assign to
.col-sm-10 .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')
&nbsp; &nbsp;
= link_to 'Assign to me', '#', class: 'btn btn-small assign-to-me-link' = link_to 'Assign to me', '#', class: 'btn btn-small assign-to-me-link'
.form-group .form-group
......
...@@ -17,10 +17,10 @@ ...@@ -17,10 +17,10 @@
%li.pull-right %li.pull-right
.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 .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' } = 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 - 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 %i.icon-plus
New Issue New Issue
.ui-box .append-bottom-10
.title .check-all-holder
= check_box_tag "check_all_issues", nil, false, class: "check_all_issues left" = 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 &nbsp;
= 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 .issues-filters
%span Filter by .dropdown.inline
.dropdown.inline.prepend-left-10 %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%a.dropdown-toggle.btn.btn-small{href: '#', "data-toggle" => "dropdown"}
%i.icon-tags %i.icon-tags
%span.light labels: %span.light labels:
- if params[:label_name].present? - if params[:label_name].present?
...@@ -33,7 +22,7 @@ ...@@ -33,7 +22,7 @@
%i.icon-tag %i.icon-tag
= label_name = label_name
.dropdown.inline.prepend-left-10 .dropdown.inline.prepend-left-10
%a.dropdown-toggle.btn.btn-small{href: '#', "data-toggle" => "dropdown"} %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%i.icon-user %i.icon-user
%span.light assignee: %span.light assignee:
- if @assignee.present? - if @assignee.present?
...@@ -49,14 +38,14 @@ ...@@ -49,14 +38,14 @@
Any Any
= link_to project_filter_path(assignee_id: 0) do = link_to project_filter_path(assignee_id: 0) do
Unassigned Unassigned
- @project.team.members.sort_by(&:name).each do |user| - @assignees.sort_by(&:name).each do |user|
%li %li
= link_to project_filter_path(assignee_id: user.id) do = link_to project_filter_path(assignee_id: user.id) do
= image_tag avatar_icon(user.email), class: "avatar s16", alt: '' = image_tag avatar_icon(user.email), class: "avatar s16", alt: ''
= user.name = user.name
.dropdown.inline.prepend-left-10 .dropdown.inline.prepend-left-10
%a.dropdown-toggle.btn.btn-small{href: '#', "data-toggle" => "dropdown"} %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%i.icon-time %i.icon-time
%span.light milestone: %span.light milestone:
- if @milestone.present? - if @milestone.present?
...@@ -81,7 +70,17 @@ ...@@ -81,7 +70,17 @@
.pull-right .pull-right
= render 'shared/sort_dropdown' = 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 %ul.well-list.issues-list
= render @issues = render @issues
- if @issues.blank? - if @issues.blank?
......
...@@ -47,7 +47,7 @@ ...@@ -47,7 +47,7 @@
%i.icon-user %i.icon-user
Assign to Assign to
.col-sm-10 .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')
&nbsp; &nbsp;
= link_to 'Assign to me', '#', class: 'btn btn-small assign-to-me-link' = link_to 'Assign to me', '#', class: 'btn btn-small assign-to-me-link'
.form-group .form-group
......
...@@ -10,12 +10,9 @@ ...@@ -10,12 +10,9 @@
.col-md-3 .col-md-3
= render 'shared/project_filter', project_entities_path: project_merge_requests_path(@project) = render 'shared/project_filter', project_entities_path: project_merge_requests_path(@project)
.col-md-9 .col-md-9
.ui-box .mr-filters.append-bottom-10
.title .dropdown.inline
.mr-filters %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%span Filter by
.dropdown.inline.prepend-left-10
%a.dropdown-toggle.btn.btn-small{href: '#', "data-toggle" => "dropdown"}
%i.icon-user %i.icon-user
%span.light assignee: %span.light assignee:
- if @assignee.present? - if @assignee.present?
...@@ -31,14 +28,14 @@ ...@@ -31,14 +28,14 @@
Any Any
= link_to project_filter_path(assignee_id: 0) do = link_to project_filter_path(assignee_id: 0) do
Unassigned Unassigned
- @project.team.members.sort_by(&:name).each do |user| - @assignees.sort_by(&:name).each do |user|
%li %li
= link_to project_filter_path(assignee_id: user.id) do = link_to project_filter_path(assignee_id: user.id) do
= image_tag avatar_icon(user.email), class: "avatar s16", alt: '' = image_tag avatar_icon(user.email), class: "avatar s16", alt: ''
= user.name = user.name
.dropdown.inline.prepend-left-10 .dropdown.inline.prepend-left-10
%a.dropdown-toggle.btn.btn-small{href: '#', "data-toggle" => "dropdown"} %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%i.icon-time %i.icon-time
%span.light milestone: %span.light milestone:
- if @milestone.present? - if @milestone.present?
...@@ -63,6 +60,7 @@ ...@@ -63,6 +60,7 @@
.pull-right .pull-right
= render 'shared/sort_dropdown' = render 'shared/sort_dropdown'
.ui-box
%ul.well-list.mr-list %ul.well-list.mr-list
= render @merge_requests = render @merge_requests
- if @merge_requests.blank? - if @merge_requests.blank?
......
- group_users_count = @group.users_groups.count
.ui-box .ui-box
.title .title
%strong #{@group.name} %strong #{@group.name}
group members (#{@group.users_groups.count}) group members (#{group_users_count})
.pull-right .pull-right
= link_to members_group_path(@group), class: 'btn btn-small' do = link_to members_group_path(@group), class: 'btn btn-small' do
%i.icon-edit %i.icon-edit
%ul.well-list %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 = 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)}
.dropdown.inline.prepend-left-10 .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: %span.light sort:
- if @sort.present? - if @sort.present?
= @sort = @sort
......
...@@ -29,6 +29,7 @@ class Groups < Spinach::FeatureSteps ...@@ -29,6 +29,7 @@ class Groups < Spinach::FeatureSteps
And 'I select user "Mary Jane" from list with role "Reporter"' do And 'I select user "Mary Jane" from list with role "Reporter"' do
user = User.find_by(name: "Mary Jane") || create(:user, name: "Mary Jane") user = User.find_by(name: "Mary Jane") || create(:user, name: "Mary Jane")
click_link 'Add members'
within ".users-group-form" do within ".users-group-form" do
select2(user.id, from: "#user_ids", multiple: true) select2(user.id, from: "#user_ids", multiple: true)
select "Reporter", from: "group_access" select "Reporter", from: "group_access"
......
...@@ -6,6 +6,12 @@ module API ...@@ -6,6 +6,12 @@ module API
expose :is_admin?, as: :is_admin expose :is_admin?, as: :is_admin
expose :can_create_group?, as: :can_create_group expose :can_create_group?, as: :can_create_group
expose :can_create_project?, as: :can_create_project expose :can_create_project?, as: :can_create_project
expose :avatar_url do |user, options|
if user.avatar.present?
user.avatar.url
end
end
end end
class UserSafe < Grape::Entity class UserSafe < Grape::Entity
......
...@@ -308,6 +308,18 @@ module API ...@@ -308,6 +308,18 @@ module API
projects = Project.where("(id in (?) OR visibility_level in (?)) AND (name LIKE (?))", ids, visibility_levels, "%#{params[:query]}%") projects = Project.where("(id in (?) OR visibility_level in (?)) AND (name LIKE (?))", ids, visibility_levels, "%#{params[:query]}%")
present paginate(projects), with: Entities::Project present paginate(projects), with: Entities::Project
end 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 end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment