diff --git a/app/controllers/submissions_dashboard_controller.rb b/app/controllers/submissions_dashboard_controller.rb
index f71e10b5..3403d22c 100644
--- a/app/controllers/submissions_dashboard_controller.rb
+++ b/app/controllers/submissions_dashboard_controller.rb
@@ -15,9 +15,6 @@ class SubmissionsDashboardController < ApplicationController
@submissions = Submissions.search(@submissions, params[:q], search_template: true)
@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.order(Submitter.arel_table[:completed_at].maximum.desc)
else
diff --git a/app/controllers/submissions_filters_controller.rb b/app/controllers/submissions_filters_controller.rb
index bd5eae1e..b0298d4c 100644
--- a/app/controllers/submissions_filters_controller.rb
+++ b/app/controllers/submissions_filters_controller.rb
@@ -4,6 +4,7 @@ class SubmissionsFiltersController < ApplicationController
ALLOWED_NAMES = %w[
author
completed_at
+ status
created_at
].freeze
diff --git a/app/controllers/templates_controller.rb b/app/controllers/templates_controller.rb
index b11812ab..c023a5e7 100644
--- a/app/controllers/templates_controller.rb
+++ b/app/controllers/templates_controller.rb
@@ -9,12 +9,11 @@ class TemplatesController < ApplicationController
submissions = @template.submissions.accessible_by(current_ability)
submissions = submissions.active if @template.archived_at.blank?
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
- submissions = submissions.pending if params[:status] == 'pending'
- submissions = submissions.completed if params[:status] == 'completed'
+ submissions = Submissions::Filter.filter_by_status(submissions, params)
submissions = if params[:completed_at_from].present? || params[:completed_at_to].present?
submissions.order(Submitter.arel_table[:completed_at].maximum.desc)
diff --git a/app/models/submission.rb b/app/models/submission.rb
index 8567aaa1..c0f1c92c 100644
--- a/app/models/submission.rb
+++ b/app/models/submission.rb
@@ -69,6 +69,8 @@ class Submission < ApplicationRecord
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)
}
+ scope :declined, -> { joins(:submitters).where.not(submitters: { declined_at: nil }).group(:id) }
+ scope :expired, -> { where(expire_at: ..Time.current) }
enum :source, {
invite: 'invite',
diff --git a/app/views/icons/_clock_cancel.html.erb b/app/views/icons/_clock_cancel.html.erb
new file mode 100644
index 00000000..d114f04a
--- /dev/null
+++ b/app/views/icons/_clock_cancel.html.erb
@@ -0,0 +1,3 @@
+
diff --git a/app/views/icons/_clock_edit.html.erb b/app/views/icons/_clock_edit.html.erb
new file mode 100644
index 00000000..ccd629d2
--- /dev/null
+++ b/app/views/icons/_clock_edit.html.erb
@@ -0,0 +1,3 @@
+
diff --git a/app/views/submissions_filters/_applied_filters.html.erb b/app/views/submissions_filters/_applied_filters.html.erb
index ad988d71..f1cdb070 100644
--- a/app/views/submissions_filters/_applied_filters.html.erb
+++ b/app/views/submissions_filters/_applied_filters.html.erb
@@ -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]] %>
+
+ <%= 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') %>
+ <%= t(params[:status]) %>
+ <% 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 %>
+
+<% end %>
+<% if params[:author].present? %>
+
+ <%= 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') %>
+ <%= current_account.users.accessible_by(current_ability).where(account: current_account).find_by(email: params[:author])&.full_name || 'NA' %>
+ <% 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 %>
+
+<% end %>
<% if query_params[:completed_at_from].present? || query_params[:completed_at_to].present? %>
<%= 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 %>
-<% if params[:author].present? %>
-
- <%= 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') %>
- <%= current_account.users.accessible_by(current_ability).where(account: current_account).find_by(email: params[:author])&.full_name || 'NA' %>
- <% 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 %>
-
-<% end %>
diff --git a/app/views/submissions_filters/_filter_button.html.erb b/app/views/submissions_filters/_filter_button.html.erb
index b840729d..3f24aaf6 100644
--- a/app/views/submissions_filters/_filter_button.html.erb
+++ b/app/views/submissions_filters/_filter_button.html.erb
@@ -1,8 +1,8 @@
-<% query_params = params.permit(:q, :status).merge(filter_params) %>
+<% query_params = params.permit(:q).merge(filter_params) %>
-
@@ -17,6 +17,12 @@
<%= t('created_at') %>
<% end %>
+ -
+ <%= 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') %>
+ <%= t('status') %>
+ <% end %>
+
-
<%= 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') %>
diff --git a/app/views/submissions_filters/_filter_modal.html.erb b/app/views/submissions_filters/_filter_modal.html.erb
index afb3916f..af4a9b86 100644
--- a/app/views/submissions_filters/_filter_modal.html.erb
+++ b/app/views/submissions_filters/_filter_modal.html.erb
@@ -1,6 +1,5 @@
<%= 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| %>
- <%= hidden_field_tag :status, params[:status] if params[:status].present? %>
<%= hidden_field_tag :q, params[:q] if params[:q].present? %>
<% local_assigns[:default_params].each do |key, value| %>
<%= hidden_field_tag(key, value) if value.present? %>
@@ -11,7 +10,7 @@
<% if params[:with_remove] %>
- <%= 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 } %>
<% end %>
<% end %>
diff --git a/app/views/submissions_filters/status.html.erb b/app/views/submissions_filters/status.html.erb
new file mode 100644
index 00000000..72541571
--- /dev/null
+++ b/app/views/submissions_filters/status.html.erb
@@ -0,0 +1,17 @@
+<%= render 'filter_modal', title: t('status'), default_params: params.permit(*(Submissions::Filter::ALLOWED_PARAMS - %w[status])) do %>
+
+<% end %>
diff --git a/config/locales/i18n.yml b/config/locales/i18n.yml
index 3e142f59..92ee1d8e 100644
--- a/config/locales/i18n.yml
+++ b/config/locales/i18n.yml
@@ -20,6 +20,8 @@ en: &en
language_ko: 한국어
hi_there: Hi there
thanks: Thanks
+ pending_by_me: Pending by me
+ partially_completed: Partially completed
unarchive: Unarchive
first_party: 'First Party'
remove_filter: Remove filter
@@ -699,6 +701,8 @@ en: &en
read: Read your data
es: &es
+ partially_completed: Parcialmente completado
+ pending_by_me: Pendiente por mi
add: Agregar
adding: Agregando
owner: Propietario
@@ -1380,6 +1384,7 @@ es: &es
read: Leer tus datos
it: &it
+ pending_by_me: In sospeso da me
add: Aggiungi
adding: Aggiungendo
owner: Proprietario
@@ -2061,6 +2066,8 @@ it: &it
read: Leggi i tuoi dati
fr: &fr
+ partially_completed: Partiellement complété
+ pending_by_me: En attente par moi
add: Ajouter
adding: Ajout
owner: Propriétaire
@@ -2743,6 +2750,8 @@ fr: &fr
read: Lire vos données
pt: &pt
+ partially_completed: Parcialmente concluído
+ pending_by_me: Pendente por mim
add: Adicionar
adding: Adicionando
owner: Proprietário
@@ -3424,6 +3433,8 @@ pt: &pt
read: Ler seus dados
de: &de
+ partially_completed: Teilweise abgeschlossen
+ pending_by_me: Ausstehend von mir
add: Hinzufügen
adding: Hinzufügen
owner: Eigentümer
diff --git a/lib/submissions/filter.rb b/lib/submissions/filter.rb
index f4a2271a..11d40a17 100644
--- a/lib/submissions/filter.rb
+++ b/lib/submissions/filter.rb
@@ -4,6 +4,7 @@ module Submissions
module Filter
ALLOWED_PARAMS = %w[
author
+ status
completed_at_from
completed_at_to
created_at_from
@@ -22,31 +23,68 @@ module Submissions
def call(submissions, current_user, params)
filters = normalize_filter_params(params, current_user)
- if filters[:author].present?
- user = current_user.account.users.find_by(email: filters[:author])
- submissions = submissions.where(created_by_user_id: user&.id || -1)
+ submissions = filter_by_author(submissions, filters, current_user)
+ submissions = filter_by_status(submissions, filters)
+ 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
+ submissions
+ end
+
+ def filter_by_created_at(submissions, filters)
submissions = submissions.where(created_at: filters[:created_at_from]..) if filters[:created_at_from].present?
if filters[:created_at_to].present?
submissions = submissions.where(created_at: ..filters[:created_at_to].end_of_day)
end
- if filters[:completed_at_from].present? || filters[:completed_at_to].present?
- completed_arel = Submitter.arel_table[:completed_at].maximum
- submissions = submissions.completed.joins(:submitters).group(:id)
+ submissions
+ end
- if filters[:completed_at_from].present?
- submissions = submissions.having(completed_arel.gteq(filters[:completed_at_from]))
- end
+ def filter_by_completed_at(submissions, filters)
+ return submissions unless filters[:completed_at_from].present? || filters[:completed_at_to].present?
- if filters[:completed_at_to].present?
- submissions = submissions.having(completed_arel.lteq(filters[:completed_at_to].end_of_day))
- end
+ completed_arel = Submitter.arel_table[:completed_at].maximum
+ submissions = submissions.completed.joins(:submitters).group(:id)
+
+ if filters[:completed_at_from].present?
+ submissions = submissions.having(completed_arel.gteq(filters[:completed_at_from]))
end
- submissions
+ return submissions if filters[:completed_at_to].blank?
+
+ submissions.having(completed_arel.lteq(filters[:completed_at_to].end_of_day))
end
def normalize_filter_params(params, current_user)