diff --git a/Gemfile b/Gemfile index 7342df7bc001dd45ae8116efe417f4f191626f6d..ff36280ae62dba7835de29234c1bf9e32ee9c02d 100644 --- a/Gemfile +++ b/Gemfile @@ -37,6 +37,7 @@ gem 'two_factor_authentication', git: 'https://github.com/noesya/two_factor_auth # gem 'two_factor_authentication', path: '../two_factor_authentication' gem 'curation'#, path: '../../arnaudlevy/curation' gem "cocoon", "~> 1.2" +gem "has_scope", "~> 0.8.0" # Front gem 'jquery-rails' diff --git a/Gemfile.lock b/Gemfile.lock index 164634b6632742a8a7d721823e1824f707be1ab3..cb51e670c31951d6e0542d52b9680194633b2e4d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -204,6 +204,9 @@ GEM sassc-rails globalid (1.0.0) activesupport (>= 5.0) + has_scope (0.8.0) + actionpack (>= 5.2) + activesupport (>= 5.2) http-cookie (1.0.4) domain_name (~> 0.5) i18n (1.9.1) @@ -437,6 +440,7 @@ DEPENDENCIES figaro front_matter_parser gdpr + has_scope (~> 0.8.0) image_processing jbuilder jquery-rails diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 1d00d143ee4a55d2065f34f21947215c3da09892..36cb5ed0b39a8b735c9071d43505f253d6b837b8 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -1,8 +1,12 @@ class Admin::UsersController < Admin::ApplicationController load_and_authorize_resource through: :current_university + has_scope :for_role + has_scope :for_search_term + def index - @users = @users.ordered.page(params[:page]) + @filters = ::Filters::User.new(current_user).list + @users = apply_scopes(@users).ordered.page(params[:page]) breadcrumb end diff --git a/app/models/user.rb b/app/models/user.rb index 602fa6c6fbeacd71974cace3381ced6872fd5fa9..e5d355ceaababe864aafae32d30ca31ec71c3a21 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -63,6 +63,16 @@ class User < ApplicationRecord belongs_to :language scope :ordered, -> { order(:last_name, :first_name) } + scope :for_search_term, -> (term) { + where(" + unaccent(concat(users.first_name, ' ', users.last_name)) ILIKE unaccent(:term) OR + unaccent(concat(users.last_name, ' ', users.first_name)) ILIKE unaccent(:term) OR + unaccent(users.first_name) ILIKE unaccent(:term) OR + unaccent(users.last_name) ILIKE unaccent(:term) OR + unaccent(users.email) ILIKE unaccent(:term) OR + unaccent(users.mobile_phone) ILIKE unaccent(:term) + ", term: "%#{sanitize_sql_like(term)}%") + } def to_s "#{first_name} #{last_name}" diff --git a/app/services/filters/base.rb b/app/services/filters/base.rb new file mode 100644 index 0000000000000000000000000000000000000000..b3a1a56740a09939a8ee0d7e4b53c4dcf6d2b52d --- /dev/null +++ b/app/services/filters/base.rb @@ -0,0 +1,39 @@ +module Filters + class Base + attr_accessor :list + + def initialize(user) + @user = user + @list = [] + end + + protected + + def add(scope_name, choices, label, multiple = false) + @list << { + scope_name: scope_name, + choices: choices, + label: label, + multiple: multiple + } + end + + def add_if(condition, args) + add *args if condition + end + + def add_search + add :for_search_term, nil, I18n.t('search') + end + + def add_date_filter(objects, attribute) + dates = objects.map { |obj| + { + to_s: I18n.l(obj[attribute], format: "%B %Y"), + id: I18n.l(obj[attribute], format: "%Y-%m") + } + }.uniq + add :for_date, dates, t('filters.attributes.date') + end + end +end diff --git a/app/services/filters/user.rb b/app/services/filters/user.rb new file mode 100644 index 0000000000000000000000000000000000000000..a71e11effad3718dc0f3e890188e92e6ec83c9e1 --- /dev/null +++ b/app/services/filters/user.rb @@ -0,0 +1,9 @@ +module Filters + class User < Base + def initialize(user) + super + add_search + add :for_role, ::User.roles.keys.map { |r| { to_s: r.humanize, id: r } }, I18n.t('filters.attributes.role') + end + end +end diff --git a/app/views/admin/application/_filters.html.erb b/app/views/admin/application/_filters.html.erb new file mode 100644 index 0000000000000000000000000000000000000000..4c2238c74afe79c882e0eee50241274c4f0e37a9 --- /dev/null +++ b/app/views/admin/application/_filters.html.erb @@ -0,0 +1,43 @@ +<% +collapsable = true if collapsable.nil? # not ||= because collapsable can be "false" +should_be_open = false +filters.each { |filter| should_be_open = true if params.has_key?(filter[:scope_name]) } +%> +<div class="row"> + <% if collapsable %> + <div class="col-md-2"> + <a class="btn btn-primary" + data-bs-toggle="collapse" + href="#collapseFilters" + role="button" + aria-expanded="false" + aria-controls="collapseFilters"> + <%= t('filters.buttons.expand') %> + </a> + </div> + <% end %> + <div class="col-md-<%= collapsable ? '10' : '12' %>"> + <div class="collapse <%= (!collapsable || should_be_open) ? 'show' : '' %>" id="collapseFilters"> + <%= form_tag current_path, method: :get, class: 'do-not-unlock' do |f| %> + <div class="row"> + <div class="col-md-4"> + <% filters.each do |filter| %> + <% if filter[:scope_name] == :for_search_term %> + <%= text_field_tag filter[:scope_name], params[filter[:scope_name]], placeholder: filter[:label], class: 'form-control mb-1 filter' %> + <% else %> + <% choices = filter[:choices].map { |elmt| elmt.is_a?(String) ? [elmt, elmt] : [elmt.is_a?(Hash) ? elmt[:to_s] : elmt.to_s, elmt[:id]] } %> + <% field_name = filter[:multiple] ? "#{filter[:scope_name]}[]" : filter[:scope_name] %> + <%= select_tag field_name, options_for_select(choices, params[filter[:scope_name]]), include_blank: filter[:label], class: 'form-select mb-1 filter' %> + <% end %> + <% end %> + </div> + <div class="col-md-3"> + <%= submit_tag t('filters.buttons.submit'), class: 'btn btn-primary btn-submit' %> + <%= link_to t('reset'), current_path, class: 'btn btn-warning' %> + </div> + </div> + <% end %> + </div> + </div> +</div> +<br> diff --git a/app/views/admin/users/index.html.erb b/app/views/admin/users/index.html.erb index b161dcbcd90a13939ca78fddb993908723e7f261..af80ff5b3d6d1520db414931e45d74790689756d 100644 --- a/app/views/admin/users/index.html.erb +++ b/app/views/admin/users/index.html.erb @@ -1,5 +1,8 @@ <% content_for :title, "#{User.model_name.human(count: 2)} (#{@users.total_count})" %> +<%= render 'admin/application/filters', + current_path: admin_users_path, + filters: @filters if @filters.any? %> <table class="table"> <thead> diff --git a/config/locales/en.yml b/config/locales/en.yml index 9153a8cc3e5c2403e1253442f81e5268959a0216..e535a3d133575b6ec0d4b52886dd78a7689a5088 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -88,6 +88,13 @@ en: edit: Edit empty_folder: Empty folder false: No + filters: + attributes: + date: Filter by Date + role: Filter by Role + buttons: + expand: Filter table + submit: Filter gdpr: privacy_policy: https://osuny.org/politique-de-confidentialite hello: "Hello %{name}!" @@ -113,7 +120,9 @@ en: privacy_policy_url: https://osuny.org/politique-de-confidentialite quit: Quit remove: Remove + reset: Reset save: Save + search: Search select_language: Select language simple_form: error_notification: diff --git a/config/locales/fr.yml b/config/locales/fr.yml index ca2c06384ec47c404be424affa5445723faffe80..f16346f8df5c30959b017b8be3cd7321791864fd 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -88,6 +88,13 @@ fr: edit: Modifier empty_folder: Dossier vide false: Non + filters: + attributes: + date: Filtrer par Date + role: Filtrer par Rôle + buttons: + expand: Filtrer le tableau + submit: Filtrer gdpr: privacy_policy: https://osuny.org/politique-de-confidentialite hello: "Bonjour %{name} !" @@ -113,7 +120,9 @@ fr: privacy_policy_url: https://osuny.org/politique-de-confidentialite quit: Quitter remove: Retirer + reset: Réinitialiser save: Enregistrer + search: Rechercher select_language: Sélectionnez une langue simple_form: error_notification: diff --git a/db/migrate/20220207093702_add_unaccent_extension.rb b/db/migrate/20220207093702_add_unaccent_extension.rb new file mode 100644 index 0000000000000000000000000000000000000000..f1d34afada495e269aaab53e2547af11082a09d7 --- /dev/null +++ b/db/migrate/20220207093702_add_unaccent_extension.rb @@ -0,0 +1,5 @@ +class AddUnaccentExtension < ActiveRecord::Migration[6.1] + def change + enable_extension "unaccent" + end +end diff --git a/db/schema.rb b/db/schema.rb index 69faf1dd08219eaefd1d8aa3edc4015d2403dba9..6fa4a6e619daf0b0e010e9a61b6ffdab27f367f8 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,11 +10,12 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2022_02_03_160802) do +ActiveRecord::Schema.define(version: 2022_02_07_093702) do # These are extensions that must be enabled in order to support this database enable_extension "pgcrypto" enable_extension "plpgsql" + enable_extension "unaccent" create_table "action_text_rich_texts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| t.string "name", null: false