diff --git a/app/controllers/paginable/plans_controller.rb b/app/controllers/paginable/plans_controller.rb index e9e4311..85c2957 100644 --- a/app/controllers/paginable/plans_controller.rb +++ b/app/controllers/paginable/plans_controller.rb @@ -3,20 +3,24 @@ # /paginable/plans/privately_visible/:page def privately_visible raise Pundit::NotAuthorizedError unless Paginable::PlanPolicy.new(current_user).privately_visible? - if params[:page] == 'ALL' - plans = current_user.active_plans + plans = Plan.active(current_user) + if params[:search].present? + plans = plans.search(params[:search]) + plans = params[:page] == 'ALL' ? plans.page(1) : plans.page(params[:page]) else - plans = current_user.active_plans.page(params[:page]) + plans = params[:page] == 'ALL' ? plans : plans.page(params[:page]) end paginable_renderise(partial: 'privately_visible', scope: plans) end # GET /paginable/plans/organisationally_or_publicly_visible/:page def organisationally_or_publicly_visible raise Pundit::NotAuthorizedError unless Paginable::PlanPolicy.new(current_user).organisationally_or_publicly_visible? - if params[:page] == 'ALL' - plans = Plan.organisationally_or_publicly_visible(current_user) + plans = Plan.organisationally_or_publicly_visible(current_user) + if params[:search].present? + plans = plans.search(params[:search]) + plans = params[:page] == 'ALL' ? plans.page(1) : plans.page(params[:page]) else - plans = Plan.organisationally_or_publicly_visible(current_user).page(params[:page]) + plans = params[:page] == 'ALL' ? plans : plans.page(params[:page]) end paginable_renderise(partial: 'organisationally_or_publicly_visible', scope: plans) end diff --git a/app/controllers/paginable/themes_controller.rb b/app/controllers/paginable/themes_controller.rb index 8a4bb30..03d1ec3 100644 --- a/app/controllers/paginable/themes_controller.rb +++ b/app/controllers/paginable/themes_controller.rb @@ -4,9 +4,13 @@ # /paginable/themes/index/:page def index authorize(Theme) - themes = params[:page] == 'ALL' ? - Theme.updated_at_desc : - Theme.updated_at_desc.page(params[:page]) + themes = Theme.updated_at_desc + if params[:search].present? + themes = themes.search(params[:search]) + themes = params[:page] == 'ALL' ? themes.page(1) : themes.page(params[:page]) + else + themes = params[:page] == 'ALL' ? themes : themes.page(params[:page]) + end paginable_renderise(partial: 'index', scope: themes) end end diff --git a/app/controllers/paginable/users_controller.rb b/app/controllers/paginable/users_controller.rb index 7637573..d840c32 100644 --- a/app/controllers/paginable/users_controller.rb +++ b/app/controllers/paginable/users_controller.rb @@ -3,9 +3,13 @@ # /paginable/users/index/:page def index authorize User - users = params[:page] == 'ALL' ? - current_user.org.users.includes(:roles) : - current_user.org.users.includes(:roles).page(params[:page]) + users = current_user.org.users.includes(:roles) + if params[:search].present? + users = users.search(params[:search]) + users = params[:page] == 'ALL' ? users.page(1) : users.page(params[:page]) + else + users = params[:page] == 'ALL' ? users : users.page(params[:page]) + end paginable_renderise(partial: 'index', scope: users) end end \ No newline at end of file diff --git a/app/controllers/plans_controller.rb b/app/controllers/plans_controller.rb index 3bae0a0..38ae6d7 100644 --- a/app/controllers/plans_controller.rb +++ b/app/controllers/plans_controller.rb @@ -7,7 +7,7 @@ def index authorize Plan - @plans = current_user.active_plans.page(1) + @plans = Plan.active(current_user).page(1) @organisationally_or_publicly_visible = Plan.organisationally_or_publicly_visible(current_user).page(1) end diff --git a/app/models/plan.rb b/app/models/plan.rb index 011c9b1..e09ecb0 100644 --- a/app/models/plan.rb +++ b/app/models/plan.rb @@ -54,9 +54,14 @@ # Note that in ActiveRecord::Enum the mappings are exposed through a class method with the pluralized attribute name (e.g visibilities rather than visibility) scope :publicly_visible, -> { where(:visibility => visibilities[:publicly_visible]).order(:title => :asc) } + # Retrieves any plan in which the user has an active role and it is not a reviewer + scope :active, -> (user) { + includes([:template, :roles]).where({ "roles.active": true, "roles.user_id": user.id }).where(Role.not_reviewer_condition) + } + # Retrieves any plan organisationally or publicly visible for a given org id scope :organisationally_or_publicly_visible, -> (user) { - Plan.includes(:template) + includes([:template, :roles]) .where({ visibility: [visibilities[:organisationally_visible], visibilities[:publicly_visible]], "templates.org_id": user.org_id}) @@ -64,6 +69,11 @@ .order(:title => :asc) } + scope :search, -> (term) { + search_pattern = "%#{term}%" + joins(:template).where("plans.title LIKE ? OR templates.title LIKE ?", search_pattern, search_pattern) + } + # Retrieves plan, template, org, phases, sections and questions scope :overview, -> (id) { Plan.includes(:phases, :sections, :questions, template: [ :org ]).find(id) diff --git a/app/models/theme.rb b/app/models/theme.rb index 5a95c48..9b5fe8e 100644 --- a/app/models/theme.rb +++ b/app/models/theme.rb @@ -16,6 +16,11 @@ validates :title, presence: {message: _("can't be blank")} scope :updated_at_desc, -> { self.all.order(updated_at: 'DESC') } + + scope :search, -> (term) { + search_pattern = "%#{term}%" + where("title LIKE ? OR description LIKE ?", search_pattern, search_pattern) + } ## # returns the title of the theme # diff --git a/app/models/user.rb b/app/models/user.rb index 3089e93..f6a87ba 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -54,6 +54,11 @@ ['grant_permissions', 'modify_templates', 'modify_guidance', 'change_org_details']) } + scope :search, -> (term) { + search_pattern = "%#{term}%" + where("firstname LIKE ? OR surname LIKE ? OR email LIKE ?", search_pattern, search_pattern, search_pattern) + } + # EVALUATE CLASS AND INSTANCE METHODS BELOW # # What do they do? do they do it efficiently, and do we need them? diff --git a/app/views/layouts/_paginable.html.erb b/app/views/layouts/_paginable.html.erb index e899eb1..b16b60b 100644 --- a/app/views/layouts/_paginable.html.erb +++ b/app/views/layouts/_paginable.html.erb @@ -3,22 +3,33 @@ # locals: { controller, action, paginable, scope } %>
-
-
- <%= yield %> + -
-
-
+
+
+
+ <% if scope.length > 0 %> + <%= yield %> + <% else %> +

+ <%= _('There are no records associated') %> +

+ <% end %> +
+
+
+
<% total = paginable ? scope.total_count : scope.length %> - <% if total > Kaminari.config.default_per_page %> - <% if paginable %> - <%= link_to(_('View all'), url_for(controller: controller, action: action, page: 'ALL'), { 'data-remote': true }) %> - <% else %> - <%= link_to(_('View less'), url_for(controller: controller, action: action, page: 1), { 'data-remote': true }) %> - <% end %> + <% if paginable %> + <%= link_to(_('View all'), url_for(controller: controller, action: action, page: 'ALL'), { 'data-remote': true, class: 'view-all' }) %> + <% else %> + <%= link_to(_('View less'), url_for(controller: controller, action: action, page: 1), { 'data-remote': true }) if total > Kaminari.config.default_per_page %> <% end %>
@@ -30,4 +41,4 @@
-
\ No newline at end of file +
diff --git a/app/views/paginable/plans/_organisationally_or_publicly_visible.html.erb b/app/views/paginable/plans/_organisationally_or_publicly_visible.html.erb index 9075978..65a57dd 100644 --- a/app/views/paginable/plans/_organisationally_or_publicly_visible.html.erb +++ b/app/views/paginable/plans/_organisationally_or_publicly_visible.html.erb @@ -9,14 +9,6 @@
- <% if scope.length > TABLE_FILTER_MIN_ROWS %> - - - - <% end %> diff --git a/app/views/paginable/plans/_privately_visible.html.erb b/app/views/paginable/plans/_privately_visible.html.erb index 93fe83b..cfbce39 100644 --- a/app/views/paginable/plans/_privately_visible.html.erb +++ b/app/views/paginable/plans/_privately_visible.html.erb @@ -1,13 +1,6 @@
- <%= render(partial: "shared/table_filter", - locals: { placeholder: _('Filter plans')}) %> -
<%= _('Project Title') %> <%= _('Template') %>
- <% if scope.length > TABLE_FILTER_MIN_ROWS %> - - - - <% end %> diff --git a/app/views/paginable/users/_index.html.erb b/app/views/paginable/users/_index.html.erb index 6b0caa3..2872ca8 100644 --- a/app/views/paginable/users/_index.html.erb +++ b/app/views/paginable/users/_index.html.erb @@ -1,67 +1,43 @@ -
-
-

<%= _('List of users') %>

-

- <%= _('Below is a list of users registered for your organisation. You can sort the data by each field.')%> -

-
-
- -
-
-
-
- <%= render(partial: "shared/table_filter", locals: { placeholder: _('Filter plans')}) %> -
<%= _('Project Title') %> <%= _('Template') %>
- - <% if scope.count > TABLE_FILTER_MIN_ROWS %> - - - - <% end %> - - - - - - - - - - <% scope.each do |user| %> - <% if !user.nil? then%> - - - - - - - - <% end %> - <% end %> - -
- <%= render(partial: "shared/table_filter", - locals: {path: admin_index_users_path, - placeholder: _('Filter users')}) %> -
<%= _('Name') %><%= _('Email address') %><%= _('Last logged in') %><%= _('How many plans?') %><%= _('Privileges') %>
- <% if !user.name.nil? %> - <%= user.name(false) %> - <% end %> - - <%= user.email %> - - <% if !user.last_sign_in_at.nil? %> - <%= l user.last_sign_in_at.to_date, :formats => :short %> - <% end %> - - <% if !user.roles.nil? %> - <%= user.roles.length %> - <% end %> - - <% unless current_user == user %> - <% b_label = _('Edit')%> - <%= link_to b_label, admin_grant_permissions_user_path(user)%> - <% end %> -
-
-
-
\ No newline at end of file + + + + + + + + + + + + <% scope.each do |user| %> + <% if !user.nil? then%> + + + + + + + + <% end %> + <% end %> + +
<%= _('Name') %><%= _('Email address') %><%= _('Last logged in') %><%= _('How many plans?') %><%= _('Privileges') %>
+ <% if !user.name.nil? %> + <%= user.name(false) %> + <% end %> + + <%= user.email %> + + <% if !user.last_sign_in_at.nil? %> + <%= l user.last_sign_in_at.to_date, :formats => :short %> + <% end %> + + <% if !user.roles.nil? %> + <%= user.roles.length %> + <% end %> + + <% unless current_user == user %> + <% b_label = _('Edit')%> + <%= link_to b_label, admin_grant_permissions_user_path(user)%> + <% end %> +
\ No newline at end of file diff --git a/app/views/shared/_search.html.erb b/app/views/shared/_search.html.erb new file mode 100644 index 0000000..0b73a2c --- /dev/null +++ b/app/views/shared/_search.html.erb @@ -0,0 +1,11 @@ +<%= form_tag(url, method: :get, remote: true, class: 'form-inline') do %> +
+
+ + + + <%= text_field_tag(:search, nil, class: 'form-control', 'aria-describedby': 'search-addon', 'aria-required': true) %> +
+
+ <%= submit_tag(_('Search'), class: 'btn btn-default', style: 'margin-top: 8px;') %> +<% end %> \ No newline at end of file diff --git a/app/views/users/admin_index.html.erb b/app/views/users/admin_index.html.erb index 21ca2da..0e15e76 100644 --- a/app/views/users/admin_index.html.erb +++ b/app/views/users/admin_index.html.erb @@ -1,5 +1,19 @@ -<%= paginable_renderise( - partial: '/paginable/users/index', - controller: 'paginable/users', - action: 'index', - scope: @users) %> \ No newline at end of file +
+
+

<%= _('List of users') %>

+

+ <%= _('Below is a list of users registered for your organisation. You can sort the data by each field.')%> +

+
+
+
+
+
+ <%= paginable_renderise( + partial: '/paginable/users/index', + controller: 'paginable/users', + action: 'index', + scope: @users) %> +
+
+
\ No newline at end of file diff --git a/lib/assets/javascripts/utils/paginable.js b/lib/assets/javascripts/utils/paginable.js index 9abef4a..491382e 100644 --- a/lib/assets/javascripts/utils/paginable.js +++ b/lib/assets/javascripts/utils/paginable.js @@ -1,7 +1,21 @@ +import { isValidText } from './isValidInputType'; + $(() => { - // Event delegation for paginable class so that any children a[data-remote="true"] - // now or in future will be handled through this eventListener - $('.paginable').on('ajax:success', 'a[data-remote="true"]', (e, data) => { - $(e.target).closest('.paginable').html(data); + const onAjaxSuccessHandler = (e, data) => { + $(e.target).closest('.paginable').find('.paginable-results').html($(data).find('.paginable-results').children()); + }; + $('.paginable-search form[data-remote="true"]').on('ajax:before', e => isValidText($(e.target).find('input[name="search"]').val())); + $('.paginable-results').on('ajax:before', (e) => { + const target = $(e.target); + if (target.hasClass('view-all')) { + // Clears out search box when view-all link was triggered + $(e.target).closest('.paginable').find('.paginable-search input[name="search"]').val(''); + } }); + // Event listener for Ajax success event in response to search button clicked + $('.paginable-search form[data-remote="true"]').on('ajax:success', '', onAjaxSuccessHandler); + // Event listener for Ajax success event captured in response to a paginable link clicked. + // Note the presence of a selector for on (e.g. a[data-remote="true"]) so that descendant elements + // from .paginable-results that are added in future are also automatically handled. + $('.paginable-results').on('ajax:success', 'a[data-remote="true"]', onAjaxSuccessHandler); });