diff --git a/.hound.yml b/.hound.yml deleted file mode 100644 index 5d0ff60b..00000000 --- a/.hound.yml +++ /dev/null @@ -1,2 +0,0 @@ -ruby: - config_file: .rubocop.yml diff --git a/app/assets/javascripts/shortcuts.js b/app/assets/javascripts/shortcuts.js new file mode 100644 index 00000000..ed130cf7 --- /dev/null +++ b/app/assets/javascripts/shortcuts.js @@ -0,0 +1,31 @@ +function focusField(event, key, field) { + const tag = event.target.tagName; + if (field === null || event.defaultPrevented || + event.key !== key || tag === 'INPUT' || tag === 'TEXTAREA') { + return; // Do nothing if the event was already processed or key was not s + } + + field.focus(); + event.preventDefault(); +} + +function searchKeydown(event) { + focusField(event, 's', document.getElementById('search-card')) +} + +function adjustKey(event) { + focusField(event, 'a', document.getElementById('adjust')) +} + +function setupShortcuts() { + document.addEventListener('keydown', adjustKey, true); + document.addEventListener('keydown', searchKeydown, true); +} + +function removeShortcuts() { + document.addEventListener('keydown', adjustKey, true); + document.removeEventListener('keydown', searchKeydown, true); +} + +document.addEventListener('turbolinks:load', setupShortcuts); +document.addEventListener('turbolinks:before-cache', removeShortcuts); diff --git a/app/controllers/admin/searches_controller.rb b/app/controllers/admin/searches_controller.rb index 4dab6686..6a33de3f 100644 --- a/app/controllers/admin/searches_controller.rb +++ b/app/controllers/admin/searches_controller.rb @@ -5,7 +5,8 @@ class SearchesController < Admin::BaseController authorize_resource(class: false) def card - @vote_users = User.card_number(card_params) + card_number = params.require(:search).permit(:card_number) + @vote_user = User.card_number(card_number.fetch(:card_number, '')) end def user @@ -14,10 +15,6 @@ def user private - def card_params - params.require(:search).permit(:card_number).fetch(:card_number, '') - end - def presence res = params.require(:search).fetch(:presence, '') return [true, false] if res.blank? @@ -25,7 +22,7 @@ def presence end def user_params - params.require(:search).permit(:firstname, :lastname) + params.require(:search).permit(:firstname, :lastname, :card_number) .reject { |_, v| v.blank? } end end diff --git a/app/models/user.rb b/app/models/user.rb index aeab2ec7..6115c520 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -2,6 +2,7 @@ # Model for allowing users to identify and sign in class User < ApplicationRecord + EMPTY_CARD = '____-____-____-____' acts_as_paranoid paginates_per(40) devise(:database_authenticatable, :registerable, @@ -14,10 +15,11 @@ class User < ApplicationRecord validates :firstname, :lastname, presence: true validates :votecode, uniqueness: true, allow_nil: true - validates :card_number, uniqueness: { allow_blank: true }, - format: { with: /\A\b[0-9]{4}\-[0-9]{4}\-[0-9]{4}\-[0-9]{4}\z/, - message: I18n.t('model.user.card_number_format'), - allow_blank: true } + validates :card_number, + uniqueness: { allow_blank: true }, + format: { with: /\A\b[0-9]{4}\-[0-9]{4}\-[0-9]{4}\-[0-9]{4}\z/, + message: I18n.t('model.user.card_number_format'), + allow_blank: true } validate :presence_require_confirmation, :votecode_require_confirmation @@ -38,12 +40,13 @@ class User < ApplicationRecord end) def self.card_number(card_number) - return if card_number == '____-____-____-____' - User.where('card_number LIKE ?', "%#{card_number}%") + return if card_number == EMPTY_CARD + User.where(card_number: card_number).first end def self.search(options) return User.all if options.empty? + options = options.reject! { |_, v| v.blank? || v == EMPTY_CARD } User.fuzzy_search(options.to_h) end diff --git a/app/views/admin/adjustments/index.html.erb b/app/views/admin/adjustments/index.html.erb index e1b6d379..38d8b1c8 100644 --- a/app/views/admin/adjustments/index.html.erb +++ b/app/views/admin/adjustments/index.html.erb @@ -4,7 +4,7 @@ -
+
<%= render('application/admin_sidebar', current: :adjustments) %>
<% if can_administrate? :manage, :vote_user %> @@ -13,11 +13,12 @@ <% end %>
-
- <%= render '/admin/votes/status', vote_status_view: @vote_status_view %> -
- -
+
+
+ <%= render '/admin/votes/status', vote_status_view: @vote_status_view %> +
+
+
<%= simple_form_for(:search, url: card_admin_search_path, remote: true) do |f| %> <%= f.input(:card_number, required: false, autofocus: true, label: User.human_attribute_name(:card_number), @@ -25,8 +26,7 @@ autocomplete: 'off', id: 'search-card' }) %> <% end %> -
- <%= render('/admin/vote_users/table') %> +

diff --git a/app/views/admin/searches/_user_form.html.erb b/app/views/admin/searches/_user_form.html.erb index 4a62642e..be27153f 100644 --- a/app/views/admin/searches/_user_form.html.erb +++ b/app/views/admin/searches/_user_form.html.erb @@ -1,16 +1,24 @@ <%= simple_form_for(:search, url: user_admin_search_path, wrapper: :inline_form, - html: { class: 'form-inline searches' }, + html: { class: 'form-inline searches flex-column align-items-start' }, remote: true) do |f| %> - <%= f.input(:firstname, placeholder: User.human_attribute_name(:firstname), - required: false) %> - <%= f.input(:lastname, placeholder: User.human_attribute_name(:lastname), - required: false) %> - <%= f.input(:presence, collection: search_presences, - include_blank: false, - label: User.human_attribute_name(:presence), - required: false) %> - <%= f.button(:submit, t('.search'), class: 'px-3 py-1 m-0 ml-2 mb-2') %> +
+ <%= f.input(:firstname, placeholder: User.human_attribute_name(:firstname), + required: false) %> + <%= f.input(:lastname, placeholder: User.human_attribute_name(:lastname), + required: false) %> +
+
+ <%= f.input(:card_number, required: false, autofocus: true, + label: User.human_attribute_name(:card_number), + input_html: { data: { mask: '9999-9999-9999-9999' }, + autocomplete: 'off', id: 'search-card' }) %> + <%= f.input(:presence, collection: search_presences, + include_blank: false, + label: User.human_attribute_name(:presence), + required: false) %> + <%= f.button(:submit, t('.search'), class: 'px-3 py-1 m-0 ml-2 mb-2') %> +
<% end %>
diff --git a/app/views/admin/searches/card.js.erb b/app/views/admin/searches/card.js.erb index 716a13e1..cc2d4f49 100644 --- a/app/views/admin/searches/card.js.erb +++ b/app/views/admin/searches/card.js.erb @@ -1,10 +1,13 @@ -var table = document.getElementById('vote-users-table') -table.innerHTML = "<%= j render('admin/vote_users/table', vote_users: @vote_users) %>" +var user = document.getElementById('vote-user') var statusDisplay = document.getElementById('status-display'); -<% if @vote_users.any? %> - statusDisplay.innerHTML = ""; +<% if @vote_user.present? %> + user.innerHTML = "<%= j render('admin/vote_users/user', vote_user: @vote_user) %>" statusDisplay.classList.replace('d-flex', 'd-none'); + statusDisplay.innerHTML = ""; + var link = document.getElementById('adjust'); + link.focus() <% else %> + user.innerHTML = ""; statusDisplay.classList.replace('d-none', 'd-flex'); - statusDisplay.innerHTML = "<%= j t('admin.searches.no_results') %>"; + statusDisplay.innerHTML = "<%= t('admin.searches.no_results') %>"; <% end %> diff --git a/app/views/admin/vote_users/_user.html.erb b/app/views/admin/vote_users/_user.html.erb new file mode 100644 index 00000000..df45ec56 --- /dev/null +++ b/app/views/admin/vote_users/_user.html.erb @@ -0,0 +1,24 @@ +
+

+ <%= vote_user.to_s %> +

+
+ <%= vote_user.card_number %> +
+ +
+ <% if vote_user.presence %> + <%= link_to(t('model.vote_user.make_not_present'), + admin_attendance_path(vote_user), + method: :delete, remote: true, + class: 'btn btn-primary', + id: 'adjust') %> + <% else %> + <%= link_to(t('model.vote_user.make_present'), + admin_attendance_path(vote_user), + method: :patch, remote: true, + class: 'btn btn-primary', + id: 'adjust') %> + <% end %> +
+
diff --git a/app/views/admin/vote_users/index.html.erb b/app/views/admin/vote_users/index.html.erb index 210903be..f9f4f720 100644 --- a/app/views/admin/vote_users/index.html.erb +++ b/app/views/admin/vote_users/index.html.erb @@ -22,11 +22,10 @@
-
- <%= render '/admin/votes/status', vote_status_view: @vote_status_view %> -
- -
+
+
+ <%= render '/admin/votes/status', vote_status_view: @vote_status_view %> +
<%= render('admin/searches/user_form') %>
<%= render('table', vote_users: @vote_users, pagination: true) %>