add status filters
This commit is contained in:
@@ -15,9 +15,6 @@ class SubmissionsDashboardController < ApplicationController
|
|||||||
@submissions = Submissions.search(@submissions, params[:q], search_template: true)
|
@submissions = Submissions.search(@submissions, params[:q], search_template: true)
|
||||||
@submissions = Submissions::Filter.call(@submissions, current_user, params)
|
@submissions = Submissions::Filter.call(@submissions, current_user, params)
|
||||||
|
|
||||||
@submissions = @submissions.pending if params[:status] == 'pending'
|
|
||||||
@submissions = @submissions.completed if params[:status] == 'completed'
|
|
||||||
|
|
||||||
@submissions = if params[:completed_at_from].present? || params[:completed_at_to].present?
|
@submissions = if params[:completed_at_from].present? || params[:completed_at_to].present?
|
||||||
@submissions.order(Submitter.arel_table[:completed_at].maximum.desc)
|
@submissions.order(Submitter.arel_table[:completed_at].maximum.desc)
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ class SubmissionsFiltersController < ApplicationController
|
|||||||
ALLOWED_NAMES = %w[
|
ALLOWED_NAMES = %w[
|
||||||
author
|
author
|
||||||
completed_at
|
completed_at
|
||||||
|
status
|
||||||
created_at
|
created_at
|
||||||
].freeze
|
].freeze
|
||||||
|
|
||||||
|
|||||||
@@ -9,12 +9,11 @@ class TemplatesController < ApplicationController
|
|||||||
submissions = @template.submissions.accessible_by(current_ability)
|
submissions = @template.submissions.accessible_by(current_ability)
|
||||||
submissions = submissions.active if @template.archived_at.blank?
|
submissions = submissions.active if @template.archived_at.blank?
|
||||||
submissions = Submissions.search(submissions, params[:q], search_values: true)
|
submissions = Submissions.search(submissions, params[:q], search_values: true)
|
||||||
submissions = Submissions::Filter.call(submissions, current_user, params)
|
submissions = Submissions::Filter.call(submissions, current_user, params.except(:status))
|
||||||
|
|
||||||
@base_submissions = submissions
|
@base_submissions = submissions
|
||||||
|
|
||||||
submissions = submissions.pending if params[:status] == 'pending'
|
submissions = Submissions::Filter.filter_by_status(submissions, params)
|
||||||
submissions = submissions.completed if params[:status] == 'completed'
|
|
||||||
|
|
||||||
submissions = if params[:completed_at_from].present? || params[:completed_at_to].present?
|
submissions = if params[:completed_at_from].present? || params[:completed_at_to].present?
|
||||||
submissions.order(Submitter.arel_table[:completed_at].maximum.desc)
|
submissions.order(Submitter.arel_table[:completed_at].maximum.desc)
|
||||||
|
|||||||
@@ -69,6 +69,8 @@ class Submission < ApplicationRecord
|
|||||||
where.not(Submitter.where(Submitter.arel_table[:submission_id].eq(Submission.arel_table[:id])
|
where.not(Submitter.where(Submitter.arel_table[:submission_id].eq(Submission.arel_table[:id])
|
||||||
.and(Submitter.arel_table[:completed_at].eq(nil))).select(1).arel.exists)
|
.and(Submitter.arel_table[:completed_at].eq(nil))).select(1).arel.exists)
|
||||||
}
|
}
|
||||||
|
scope :declined, -> { joins(:submitters).where.not(submitters: { declined_at: nil }).group(:id) }
|
||||||
|
scope :expired, -> { where(expire_at: ..Time.current) }
|
||||||
|
|
||||||
enum :source, {
|
enum :source, {
|
||||||
invite: 'invite',
|
invite: 'invite',
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="<%= local_assigns[:class] %>" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" /><path d="M20.997 12.25a9 9 0 1 0 -8.718 8.745" /><path d="M19 19m-3 0a3 3 0 1 0 6 0a3 3 0 1 0 -6 0" /><path d="M17 21l4 -4" /><path d="M12 7v5l2 2" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 433 B |
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="<%= local_assigns[:class] %>" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" /><path d="M21 12a9 9 0 1 0 -9.972 8.948c.32 .034 .644 .052 .972 .052" /><path d="M12 7v5l2 2" /><path d="M18.42 15.61a2.1 2.1 0 0 1 2.97 2.97l-3.39 3.42h-3v-3l3.42 -3.39z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 456 B |
@@ -1,4 +1,26 @@
|
|||||||
<% query_params = params.permit(:q, :status).merge(filter_params) %>
|
<% query_params = params.permit(:q).merge(filter_params) %>
|
||||||
|
<% if icon = { 'declined' => 'x_circle', 'expired' => 'clock_cancel', 'partially_completed' => 'clock_edit' }[params[:status]] %>
|
||||||
|
<div class="flex h-10 px-2 py-1 text-lg items-center justify-between border text-center text-neutral font-semibold rounded-xl w-full md:w-34 border-neutral-700">
|
||||||
|
<%= link_to submissions_filter_path('status', query_params.merge(path: url_for, with_remove: true)), data: { turbo_frame: 'modal' }, class: 'flex items-center space-x-1 w-full pr-1 md:max-w-[140px]' do %>
|
||||||
|
<%= svg_icon(icon, class: 'w-5 h-5 shrink-0') %>
|
||||||
|
<span class="font-normal truncate"><%= t(params[:status]) %></span>
|
||||||
|
<% end %>
|
||||||
|
<%= link_to url_for(params.to_unsafe_h.except(:status)), class: 'rounded-lg ml-1 hover:bg-base-content hover:text-white' do %>
|
||||||
|
<%= svg_icon('x', class: 'w-5 h-5') %>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
<% if params[:author].present? %>
|
||||||
|
<div class="tooltip tooltip-bottom flex h-10 px-2 py-1 text-lg items-center justify-between border text-center text-neutral font-semibold rounded-xl w-full md:w-34 border-neutral-700" data-tip="<%= t('author') %>">
|
||||||
|
<%= link_to submissions_filter_path('author', query_params.merge(path: url_for, with_remove: true)), data: { turbo_frame: 'modal' }, class: 'flex items-center space-x-1 w-full pr-1 md:max-w-[140px]' do %>
|
||||||
|
<%= svg_icon('user', class: 'w-5 h-5 shrink-0') %>
|
||||||
|
<span class="font-normal truncate"><%= current_account.users.accessible_by(current_ability).where(account: current_account).find_by(email: params[:author])&.full_name || 'NA' %></span>
|
||||||
|
<% end %>
|
||||||
|
<%= link_to url_for(params.to_unsafe_h.except(:author)), class: 'rounded-lg ml-1 hover:bg-base-content hover:text-white' do %>
|
||||||
|
<%= svg_icon('x', class: 'w-5 h-5') %>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
<% if query_params[:completed_at_from].present? || query_params[:completed_at_to].present? %>
|
<% if query_params[:completed_at_from].present? || query_params[:completed_at_to].present? %>
|
||||||
<div class="tooltip tooltip-bottom flex h-10 px-2 py-1 text-lg items-center justify-between border text-center text-neutral font-semibold rounded-xl w-full md:w-34 border-neutral-700" data-tip="<%= t('completed_at') %>">
|
<div class="tooltip tooltip-bottom flex h-10 px-2 py-1 text-lg items-center justify-between border text-center text-neutral font-semibold rounded-xl w-full md:w-34 border-neutral-700" data-tip="<%= t('completed_at') %>">
|
||||||
<%= link_to submissions_filter_path('completed_at', query_params.merge(path: url_for, with_remove: true)), data: { turbo_frame: 'modal' }, class: 'flex items-center space-x-1 w-full pr-1 md:max-w-[140px]' do %>
|
<%= link_to submissions_filter_path('completed_at', query_params.merge(path: url_for, with_remove: true)), data: { turbo_frame: 'modal' }, class: 'flex items-center space-x-1 w-full pr-1 md:max-w-[140px]' do %>
|
||||||
@@ -37,14 +59,3 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% if params[:author].present? %>
|
|
||||||
<div class="tooltip tooltip-bottom flex h-10 px-2 py-1 text-lg items-center justify-between border text-center text-neutral font-semibold rounded-xl w-full md:w-34 border-neutral-700" data-tip="<%= t('author') %>">
|
|
||||||
<%= link_to submissions_filter_path('author', query_params.merge(path: url_for, with_remove: true)), data: { turbo_frame: 'modal' }, class: 'flex items-center space-x-1 w-full pr-1 md:max-w-[140px]' do %>
|
|
||||||
<%= svg_icon('user', class: 'w-5 h-5 shrink-0') %>
|
|
||||||
<span class="font-normal truncate"><%= current_account.users.accessible_by(current_ability).where(account: current_account).find_by(email: params[:author])&.full_name || 'NA' %></span>
|
|
||||||
<% end %>
|
|
||||||
<%= link_to url_for(params.to_unsafe_h.except(:author)), class: 'rounded-lg ml-1 hover:bg-base-content hover:text-white' do %>
|
|
||||||
<%= svg_icon('x', class: 'w-5 h-5') %>
|
|
||||||
<% end %>
|
|
||||||
</div>
|
|
||||||
<% end %>
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<% query_params = params.permit(:q, :status).merge(filter_params) %>
|
<% query_params = params.permit(:q).merge(filter_params) %>
|
||||||
<div class="dropdown dropdown-end">
|
<div class="dropdown dropdown-end">
|
||||||
<label tabindex="0" class="cursor-pointer flex h-10 px-3 py-1 space-x-1 text-lg items-center justify-between border text-center text-neutral rounded-xl border-neutral-300 hover:border-neutral-700">
|
<label tabindex="0" class="cursor-pointer flex h-10 px-3 py-1 space-x-1 text-lg items-center justify-between border text-center text-neutral rounded-xl border-neutral-300 hover:border-neutral-700">
|
||||||
<%= svg_icon('filter', class: 'w-5 h-5 flex-shrink-0 stroke-2') %>
|
<%= svg_icon('filter', class: 'w-5 h-5 flex-shrink-0 stroke-2') %>
|
||||||
<span class="<%= filter_params.present? ? 'md:hidden' : '' %>">Filter</span>
|
<span class="<%= filter_params.then { |f| f[:status].in?(%w[pending completed]) ? f.except(:status) : f }.present? ? 'md:hidden' : '' %>">Filter</span>
|
||||||
</label>
|
</label>
|
||||||
<ul tabindex="0" class="z-10 dropdown-content p-2 mt-2 shadow menu text-base bg-base-100 rounded-box min-w-[180px] text-right">
|
<ul tabindex="0" class="z-10 dropdown-content p-2 mt-2 shadow menu text-base bg-base-100 rounded-box min-w-[180px] text-right">
|
||||||
<li class="flex">
|
<li class="flex">
|
||||||
@@ -17,6 +17,12 @@
|
|||||||
<span><%= t('created_at') %></span>
|
<span><%= t('created_at') %></span>
|
||||||
<% end %>
|
<% end %>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="flex">
|
||||||
|
<%= link_to submissions_filter_path('status', query_params.merge(path: url_for)), data: { turbo_frame: 'modal' } do %>
|
||||||
|
<%= svg_icon('info_circle', class: 'w-5 h-5 flex-shrink-0 stroke-2') %>
|
||||||
|
<span><%= t('status') %></span>
|
||||||
|
<% end %>
|
||||||
|
</li>
|
||||||
<li class="flex">
|
<li class="flex">
|
||||||
<%= link_to submissions_filter_path('author', query_params.merge(path: url_for)), data: { turbo_frame: 'modal' } do %>
|
<%= link_to submissions_filter_path('author', query_params.merge(path: url_for)), data: { turbo_frame: 'modal' } do %>
|
||||||
<%= svg_icon('user', class: 'w-5 h-5 flex-shrink-0 stroke-2') %>
|
<%= svg_icon('user', class: 'w-5 h-5 flex-shrink-0 stroke-2') %>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<%= render 'shared/turbo_modal', title: local_assigns[:title] do %>
|
<%= render 'shared/turbo_modal', title: local_assigns[:title] do %>
|
||||||
<%= form_for '', url: params[:path], method: :get, data: { turbo_frame: :_top }, html: { autocomplete: :off } do |f| %>
|
<%= form_for '', url: params[:path], method: :get, data: { turbo_frame: :_top }, html: { autocomplete: :off } do |f| %>
|
||||||
<%= hidden_field_tag :status, params[:status] if params[:status].present? %>
|
|
||||||
<%= hidden_field_tag :q, params[:q] if params[:q].present? %>
|
<%= hidden_field_tag :q, params[:q] if params[:q].present? %>
|
||||||
<% local_assigns[:default_params].each do |key, value| %>
|
<% local_assigns[:default_params].each do |key, value| %>
|
||||||
<%= hidden_field_tag(key, value) if value.present? %>
|
<%= hidden_field_tag(key, value) if value.present? %>
|
||||||
@@ -11,7 +10,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<% if params[:with_remove] %>
|
<% if params[:with_remove] %>
|
||||||
<div class="text-center w-full mt-4">
|
<div class="text-center w-full mt-4">
|
||||||
<%= link_to t('remove_filter'), "#{params[:path]}?#{params.to_unsafe_h.slice(:q, :status).merge(local_assigns[:default_params]).to_query}", class: 'link', data: { turbo_frame: :_top } %>
|
<%= link_to t('remove_filter'), "#{params[:path]}?#{params.to_unsafe_h.slice(:q).merge(local_assigns[:default_params]).to_query}", class: 'link', data: { turbo_frame: :_top } %>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<%= render 'filter_modal', title: t('status'), default_params: params.permit(*(Submissions::Filter::ALLOWED_PARAMS - %w[status])) do %>
|
||||||
|
<div class="space-y-3">
|
||||||
|
<div class="flex flex-col md:flex-row gap-2">
|
||||||
|
<div class="form-control w-full">
|
||||||
|
<%= label_tag 'status', t('status'), class: 'label text-sm' %>
|
||||||
|
<div id="status" class="radio-select grid grid-cols-2 gap-2 px-1">
|
||||||
|
<% ['', 'pending', 'completed', 'declined', 'expired', 'partially_completed'].each do |status| %>
|
||||||
|
<label class="radio-label cursor-pointer inline-flex items-center space-x-2">
|
||||||
|
<%= radio_button_tag 'status', status, params[:status] == status || (status == '' && params[:status].blank?), class: 'base-radio' %>
|
||||||
|
<span><%= t(status.presence || 'all') %></span>
|
||||||
|
</label>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
@@ -20,6 +20,8 @@ en: &en
|
|||||||
language_ko: 한국어
|
language_ko: 한국어
|
||||||
hi_there: Hi there
|
hi_there: Hi there
|
||||||
thanks: Thanks
|
thanks: Thanks
|
||||||
|
pending_by_me: Pending by me
|
||||||
|
partially_completed: Partially completed
|
||||||
unarchive: Unarchive
|
unarchive: Unarchive
|
||||||
first_party: 'First Party'
|
first_party: 'First Party'
|
||||||
remove_filter: Remove filter
|
remove_filter: Remove filter
|
||||||
@@ -699,6 +701,8 @@ en: &en
|
|||||||
read: Read your data
|
read: Read your data
|
||||||
|
|
||||||
es: &es
|
es: &es
|
||||||
|
partially_completed: Parcialmente completado
|
||||||
|
pending_by_me: Pendiente por mi
|
||||||
add: Agregar
|
add: Agregar
|
||||||
adding: Agregando
|
adding: Agregando
|
||||||
owner: Propietario
|
owner: Propietario
|
||||||
@@ -1380,6 +1384,7 @@ es: &es
|
|||||||
read: Leer tus datos
|
read: Leer tus datos
|
||||||
|
|
||||||
it: &it
|
it: &it
|
||||||
|
pending_by_me: In sospeso da me
|
||||||
add: Aggiungi
|
add: Aggiungi
|
||||||
adding: Aggiungendo
|
adding: Aggiungendo
|
||||||
owner: Proprietario
|
owner: Proprietario
|
||||||
@@ -2061,6 +2066,8 @@ it: &it
|
|||||||
read: Leggi i tuoi dati
|
read: Leggi i tuoi dati
|
||||||
|
|
||||||
fr: &fr
|
fr: &fr
|
||||||
|
partially_completed: Partiellement complété
|
||||||
|
pending_by_me: En attente par moi
|
||||||
add: Ajouter
|
add: Ajouter
|
||||||
adding: Ajout
|
adding: Ajout
|
||||||
owner: Propriétaire
|
owner: Propriétaire
|
||||||
@@ -2743,6 +2750,8 @@ fr: &fr
|
|||||||
read: Lire vos données
|
read: Lire vos données
|
||||||
|
|
||||||
pt: &pt
|
pt: &pt
|
||||||
|
partially_completed: Parcialmente concluído
|
||||||
|
pending_by_me: Pendente por mim
|
||||||
add: Adicionar
|
add: Adicionar
|
||||||
adding: Adicionando
|
adding: Adicionando
|
||||||
owner: Proprietário
|
owner: Proprietário
|
||||||
@@ -3424,6 +3433,8 @@ pt: &pt
|
|||||||
read: Ler seus dados
|
read: Ler seus dados
|
||||||
|
|
||||||
de: &de
|
de: &de
|
||||||
|
partially_completed: Teilweise abgeschlossen
|
||||||
|
pending_by_me: Ausstehend von mir
|
||||||
add: Hinzufügen
|
add: Hinzufügen
|
||||||
adding: Hinzufügen
|
adding: Hinzufügen
|
||||||
owner: Eigentümer
|
owner: Eigentümer
|
||||||
|
|||||||
+51
-13
@@ -4,6 +4,7 @@ module Submissions
|
|||||||
module Filter
|
module Filter
|
||||||
ALLOWED_PARAMS = %w[
|
ALLOWED_PARAMS = %w[
|
||||||
author
|
author
|
||||||
|
status
|
||||||
completed_at_from
|
completed_at_from
|
||||||
completed_at_to
|
completed_at_to
|
||||||
created_at_from
|
created_at_from
|
||||||
@@ -22,31 +23,68 @@ module Submissions
|
|||||||
def call(submissions, current_user, params)
|
def call(submissions, current_user, params)
|
||||||
filters = normalize_filter_params(params, current_user)
|
filters = normalize_filter_params(params, current_user)
|
||||||
|
|
||||||
if filters[:author].present?
|
submissions = filter_by_author(submissions, filters, current_user)
|
||||||
user = current_user.account.users.find_by(email: filters[:author])
|
submissions = filter_by_status(submissions, filters)
|
||||||
submissions = submissions.where(created_by_user_id: user&.id || -1)
|
submissions = filter_by_created_at(submissions, filters)
|
||||||
|
|
||||||
|
filter_by_completed_at(submissions, filters)
|
||||||
|
end
|
||||||
|
|
||||||
|
def filter_by_author(submissions, filters, current_user)
|
||||||
|
return submissions if filters[:author].blank?
|
||||||
|
|
||||||
|
user = current_user.account.users.find_by(email: filters[:author])
|
||||||
|
submissions.where(created_by_user_id: user&.id || -1)
|
||||||
|
end
|
||||||
|
|
||||||
|
def filter_by_status(submissions, filters)
|
||||||
|
submissions = submissions.pending if filters[:status] == 'pending'
|
||||||
|
submissions = submissions.completed if filters[:status] == 'completed'
|
||||||
|
submissions = submissions.declined if filters[:status] == 'declined'
|
||||||
|
submissions = submissions.expired if filters[:status] == 'expired'
|
||||||
|
|
||||||
|
if filters[:status] == 'partially_completed'
|
||||||
|
submissions =
|
||||||
|
submissions.joins(:submitters)
|
||||||
|
.group(:id)
|
||||||
|
.having(Arel::Nodes::NamedFunction.new(
|
||||||
|
'COUNT', [Arel::Nodes::NamedFunction.new('NULLIF',
|
||||||
|
[Submitter.arel_table[:completed_at].eq(nil),
|
||||||
|
Arel::Nodes.build_quoted(false)])]
|
||||||
|
).gt(0))
|
||||||
|
.having(Arel::Nodes::NamedFunction.new(
|
||||||
|
'COUNT', [Arel::Nodes::NamedFunction.new('NULLIF',
|
||||||
|
[Submitter.arel_table[:completed_at].not_eq(nil),
|
||||||
|
Arel::Nodes.build_quoted(false)])]
|
||||||
|
).gt(0))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
submissions
|
||||||
|
end
|
||||||
|
|
||||||
|
def filter_by_created_at(submissions, filters)
|
||||||
submissions = submissions.where(created_at: filters[:created_at_from]..) if filters[:created_at_from].present?
|
submissions = submissions.where(created_at: filters[:created_at_from]..) if filters[:created_at_from].present?
|
||||||
|
|
||||||
if filters[:created_at_to].present?
|
if filters[:created_at_to].present?
|
||||||
submissions = submissions.where(created_at: ..filters[:created_at_to].end_of_day)
|
submissions = submissions.where(created_at: ..filters[:created_at_to].end_of_day)
|
||||||
end
|
end
|
||||||
|
|
||||||
if filters[:completed_at_from].present? || filters[:completed_at_to].present?
|
submissions
|
||||||
completed_arel = Submitter.arel_table[:completed_at].maximum
|
end
|
||||||
submissions = submissions.completed.joins(:submitters).group(:id)
|
|
||||||
|
|
||||||
if filters[:completed_at_from].present?
|
def filter_by_completed_at(submissions, filters)
|
||||||
submissions = submissions.having(completed_arel.gteq(filters[:completed_at_from]))
|
return submissions unless filters[:completed_at_from].present? || filters[:completed_at_to].present?
|
||||||
end
|
|
||||||
|
|
||||||
if filters[:completed_at_to].present?
|
completed_arel = Submitter.arel_table[:completed_at].maximum
|
||||||
submissions = submissions.having(completed_arel.lteq(filters[:completed_at_to].end_of_day))
|
submissions = submissions.completed.joins(:submitters).group(:id)
|
||||||
end
|
|
||||||
|
if filters[:completed_at_from].present?
|
||||||
|
submissions = submissions.having(completed_arel.gteq(filters[:completed_at_from]))
|
||||||
end
|
end
|
||||||
|
|
||||||
submissions
|
return submissions if filters[:completed_at_to].blank?
|
||||||
|
|
||||||
|
submissions.having(completed_arel.lteq(filters[:completed_at_to].end_of_day))
|
||||||
end
|
end
|
||||||
|
|
||||||
def normalize_filter_params(params, current_user)
|
def normalize_filter_params(params, current_user)
|
||||||
|
|||||||
Reference in New Issue
Block a user