From b1108f97ac211103476963ed3a787ab42ba2bc6a Mon Sep 17 00:00:00 2001
From: Audrey Hamelers
Date: Wed, 12 Jun 2024 18:26:05 +0200
Subject: [PATCH 1/8] basically functional customizable admin dashboard, most
filters (ugly)
---
.../admin_dashboard_controller.rb | 141 ++++++++++++++++++
app/models/stash_datacite/affiliation.rb | 4 +-
app/models/stash_engine/resource.rb | 10 +-
app/models/stash_engine/tenant.rb | 7 +
app/policies/stash_engine/tenant_policy.rb | 4 +-
.../admin_dashboard/_fields.html.erb | 48 ++++++
.../admin_dashboard/_filter_date.html.erb | 10 ++
.../admin_dashboard/_filters.html.erb | 26 ++++
.../admin_dashboard/_table_header.html.erb | 62 ++++++++
.../admin_dashboard/_table_row.html.erb | 71 +++++++++
.../admin_dashboard/index.csv.erb | 60 ++++++++
.../admin_dashboard/index.html.erb | 45 ++++++
config/routes.rb | 1 +
.../stash/repo/merritt_oaidc_builder_spec.rb | 10 +-
.../models/stash_datacite/affiliation_spec.rb | 3 +-
15 files changed, 480 insertions(+), 22 deletions(-)
create mode 100644 app/controllers/stash_engine/admin_dashboard_controller.rb
create mode 100644 app/views/stash_engine/admin_dashboard/_fields.html.erb
create mode 100644 app/views/stash_engine/admin_dashboard/_filter_date.html.erb
create mode 100644 app/views/stash_engine/admin_dashboard/_filters.html.erb
create mode 100644 app/views/stash_engine/admin_dashboard/_table_header.html.erb
create mode 100644 app/views/stash_engine/admin_dashboard/_table_row.html.erb
create mode 100644 app/views/stash_engine/admin_dashboard/index.csv.erb
create mode 100644 app/views/stash_engine/admin_dashboard/index.html.erb
diff --git a/app/controllers/stash_engine/admin_dashboard_controller.rb b/app/controllers/stash_engine/admin_dashboard_controller.rb
new file mode 100644
index 0000000000..b31ff77485
--- /dev/null
+++ b/app/controllers/stash_engine/admin_dashboard_controller.rb
@@ -0,0 +1,141 @@
+module StashEngine
+ class AdminDashboardController < ApplicationController
+ helper SortableTableHelper
+ before_action :require_user_login
+ before_action :setup_paging, only: :index
+ before_action :setup_search, only: :index
+ # before_action :load, only: %i[popup note_popup edit]
+
+ # rubocop:disable Metrics/MethodLength
+ def index
+ @datasets = StashEngine::Resource.latest_per_dataset
+ .left_outer_joins(identifier: %i[counter_stat internal_data])
+ .preload(identifier: %i[counter_stat internal_data])
+ .left_outer_joins(:last_curation_activity)
+ .preload(:last_curation_activity)
+ .left_outer_joins(:authors)
+ .preload(:authors)
+ .joins("
+ left outer join stash_engine_curation_activities seca on seca.id = (
+ select ca.id from stash_engine_curation_activities ca where ca.resource_id = stash_engine_resources.id
+ and ca.status in ('submitted', 'peer_review')
+ order by ca.created_at limit 1
+ )")
+ .joins("left outer join (
+ select stash_engine_users.* from stash_engine_users
+ inner join stash_engine_roles on stash_engine_users.id = stash_engine_roles.user_id
+ and role in ('curator', 'superuser')
+ ) curator on curator.id = stash_engine_resources.current_editor_id")
+ .joins("left outer join stash_engine_journals on stash_engine_internal_data.data_type = 'publicationISSN'
+ and stash_engine_journals.issn like CONCAT('%', stash_engine_internal_data.value ,'%')")
+ .select("
+ distinct stash_engine_resources.*, stash_engine_curation_activities.status,
+ stash_engine_counter_stats.unique_investigation_count, stash_engine_counter_stats.citation_count,
+ stash_engine_counter_stats.unique_request_count, seca.created_at as submit_date,
+ stash_engine_journals.title as journal_title, stash_engine_journals.sponsor_id, stash_engine_journals.issn,
+ CONCAT_WS(' ', curator.first_name, curator.last_name) as curator_name,
+ (select GROUP_CONCAT(distinct CONCAT_WS(', ', sea.author_last_name, sea.author_first_name) ORDER BY sea.author_order, sea.id separator '; ')
+ from stash_engine_authors sea where sea.resource_id = stash_engine_resources.id) as author_string,
+ MATCH(stash_engine_identifiers.search_words) AGAINST('#{@search_string}') as relevance
+ ")
+
+ add_fields
+ add_filters
+ date_filters
+
+ order_string = 'relevance desc'
+ if params[:sort].present?
+ order_list = %w[title author_string status total_file_size unique_investigation_count curator_name
+ updated_at submit_date publication_date]
+ order_string = helpers.sortable_table_order(whitelist: order_list)
+ order_string += ', relevance desc' if @search_string.present?
+ end
+
+ @datasets = @datasets.order(order_string)
+ @datasets = @datasets.page(@page).per(@page_size)
+
+ respond_to do |format|
+ format.html
+ format.csv do
+ headers['Content-Disposition'] = "attachment; filename=#{Time.new.strftime('%F')}_report.csv"
+ end
+ end
+ end
+ # rubocop:enable Metrics/MethodLength
+
+ private
+
+ def setup_paging
+ if request.format.csv?
+ @page = 1
+ @page_size = 2_000
+ return
+ end
+ @page = params[:page] || '1'
+ @page_size = if params[:page_size].blank? || params[:page_size].to_i == 0
+ 10
+ else
+ params[:page_size].to_i
+ end
+ end
+
+ def setup_search
+ @search_string = params[:q] || ''
+ @filters = params[:filters] || session[:admin_search_filters] || {}
+ session[:admin_search_filters] = params[:filters] if params[:filters].present?
+ @fields = params[:fields] || session[:admin_search_fields]
+ session[:admin_search_fields] = params[:fields] if params[:fields].present?
+ return unless @fields.blank?
+
+ @fields = %w[doi keywords authors status metrics submit_date publication_date]
+ @fields << 'curator' if current_user.min_curator?
+ end
+
+ def add_fields
+ @datasets = @datasets.preload(:funders) if @fields.include?('funders')
+ @datasets = @datasets.preload(:subjects) if @fields.include?('keywords')
+ @datasets = @datasets.preload(authors: :affiliations).preload(:tenant) if @fields.include?('affiliations')
+ @datasets = @datasets.preload(tenant: :ror_orgs).preload(authors: { affiliations: :ror_org }) if @fields.include?('countries')
+ end
+
+ # rubocop:disable Style/MultilineIfModifier
+ def add_filters
+ @datasets = @datasets.where('stash_engine_curation_activities.status': @filters[:status]) if @filters[:status].present?
+ @datasets = @datasets.where(
+ 'curator.id': Integer(@filters[:curator], exception: false) ? @filters[:curator] : nil
+ ) if @filters[:curator].present?
+ @datasets = @datasets.where('stash_engine_journals.sponsor_id': @filters[:sponsor]) if @filters[:sponsor].present?
+ @datasets = @datasets.where("MATCH(stash_engine_identifiers.search_words) AGAINST('#{@search_string}') > 0") unless @search_string.blank?
+ return unless @filters[:member].present? && StashEngine::Tenant.find_by(id: @filters[:member]).present?
+
+ tenant_orgs = StashEngine::Tenant.find(@filters[:member]).ror_ids
+ @datasets = @datasets.left_outer_joins(authors: :affiliations).left_outer_joins(:funders)
+ @datasets = @datasets.where(
+ 'stash_engine_resources.tenant_id = ? or stash_engine_identifiers.payment_id = ?
+ or dcs_affiliations.ror_id in (?) or dcs_contributors.name_identifier_id in (?)',
+ @filters[:member], @filters[:member], tenant_orgs, tenant_orgs
+ )
+ end
+
+ def date_filters
+ @datasets = @datasets.where(
+ "stash_engine_curation_activities.updated_at #{date_string(@filters[:updated_at])}"
+ ) unless @filters[:updated_at].nil? || @filters[:updated_at].values.all?(&:blank?)
+ @datasets = @datasets.where(
+ "seca.created_at #{date_string(@filters[:submit_date])}"
+ ) unless @filters[:submit_date].nil? || @filters[:submit_date].values.all?(&:blank?)
+ @datasets = @datasets.where(
+ "stash_engine_resources.publication_date #{date_string(@filters[:publication_date])}"
+ ) unless @filters[:publication_date].nil? || @filters[:publication_date].values.all?(&:blank?)
+ end
+ # rubocop:enable Style/MultilineIfModifier
+
+ def date_string(date_hash)
+ from = date_hash[:start_date]
+ to = date_hash[:end_date]
+ return "< '#{to}'" if from.blank?
+
+ "BETWEEN '#{from}' AND #{to.blank? ? 'now()' : "'#{to}'"}"
+ end
+ end
+end
diff --git a/app/models/stash_datacite/affiliation.rb b/app/models/stash_datacite/affiliation.rb
index 9f7afcbf88..18603cab3f 100644
--- a/app/models/stash_datacite/affiliation.rb
+++ b/app/models/stash_datacite/affiliation.rb
@@ -24,6 +24,7 @@ class Affiliation < ApplicationRecord
self.table_name = 'dcs_affiliations'
has_and_belongs_to_many :authors, class_name: 'StashEngine::Author', join_table: 'dcs_affiliations_authors'
has_and_belongs_to_many :contributors, class_name: 'StashDatacite::Contributor'
+ belongs_to :ror_org, class_name: 'StashEngine::RorOrg', primary_key: 'ror_id', foreign_key: 'ror_id', optional: true
validates :long_name, presence: true
@@ -42,9 +43,6 @@ def smart_name(show_asterisk: false)
end
def country_name
- return nil if ror_id.blank?
-
- ror_org = StashEngine::RorOrg.find_by_ror_id(ror_id)
return nil if ror_org.nil? || ror_org.country.nil?
ror_org.country
diff --git a/app/models/stash_engine/resource.rb b/app/models/stash_engine/resource.rb
index 2e12d90655..fdcf0672e2 100644
--- a/app/models/stash_engine/resource.rb
+++ b/app/models/stash_engine/resource.rb
@@ -83,6 +83,7 @@ class Resource < ApplicationRecord # rubocop:disable Metrics/ClassLength
has_one :language, class_name: 'StashDatacite::Language', dependent: :destroy
has_many :descriptions, class_name: 'StashDatacite::Description', dependent: :destroy
has_many :contributors, class_name: 'StashDatacite::Contributor', dependent: :destroy
+ has_many :funders, -> { where(contributor_type: 'funder') }, class_name: 'StashDatacite::Contributor'
has_many :datacite_dates, class_name: 'StashDatacite::DataciteDate', dependent: :destroy
has_many :descriptions, class_name: 'StashDatacite::Description', dependent: :destroy
has_many :geolocations, class_name: 'StashDatacite::Geolocation', dependent: :destroy
@@ -609,15 +610,6 @@ def previous_curated_resource
.order(id: :desc).first
end
- # ------------------------------------------------------------
- # Ownership
-
- def tenant
- return nil unless tenant_id
-
- Tenant.find(tenant_id)
- end
-
# -----------------------------------------------------------
# Permissions
diff --git a/app/models/stash_engine/tenant.rb b/app/models/stash_engine/tenant.rb
index bc128c1e8f..84c02d95c7 100644
--- a/app/models/stash_engine/tenant.rb
+++ b/app/models/stash_engine/tenant.rb
@@ -62,6 +62,13 @@ def ror_ids
tenant_ror_orgs.map(&:ror_id)
end
+ def country_name
+ ror_org = ror_orgs.first
+ return nil if ror_org.nil? || ror_org.country.nil?
+
+ ror_org.country
+ end
+
def omniauth_login_path(params = nil)
@omniauth_login_path ||= send(:"#{authentication.strategy}_login_path", params)
end
diff --git a/app/policies/stash_engine/tenant_policy.rb b/app/policies/stash_engine/tenant_policy.rb
index eec088372e..a43459d9e4 100644
--- a/app/policies/stash_engine/tenant_policy.rb
+++ b/app/policies/stash_engine/tenant_policy.rb
@@ -21,8 +21,10 @@ def initialize(user, scope)
def resolve
if @user.tenant_limited?
@scope.enabled.joins(:tenant_ror_orgs).where(tenant_ror_orgs: { ror_id: user.tenant.ror_ids }).distinct
- else
+ elsif @user.system_user?
@scope.all
+ else
+ []
end
end
diff --git a/app/views/stash_engine/admin_dashboard/_fields.html.erb b/app/views/stash_engine/admin_dashboard/_fields.html.erb
new file mode 100644
index 0000000000..9dbc22b3c2
--- /dev/null
+++ b/app/views/stash_engine/admin_dashboard/_fields.html.erb
@@ -0,0 +1,48 @@
+<%
+def check(id)
+ h = {funders: 'Grant funders', awards: 'Award IDs', sponsor: 'Journal sponsor', dpc: 'DPC paid by', updated_at: 'Last modified', submit_date: 'Submitted', publication_date: 'Published'}
+ label = h[id.to_sym]
+ label ||= id.length < 4 ? id.upcase : id.capitalize
+
+ '' +
+ check_box_tag('fields[]', id, @fields.include?(id), {id: id}) +
+ label_tag('fields[]', label, {for: id})+
+ '
'
+end
+%>
+
+
+ Fields
+
+
+ Description:
+ <%= check('doi').html_safe %>
+ <%= check('keywords').html_safe %>
+
+
+ <%= check('authors').html_safe %>
+ <%= check('affiliations').html_safe %>
+
<%= check('countries').html_safe %>
+
+
+ <%= check('status').html_safe %>
+ <%= check('metrics').html_safe %>
+ <%= check('size').html_safe %>
+
+
+ <%= check('journal').html_safe %>
+ <%= check('sponsor').html_safe %>
+ <%= check('dpc').html_safe %>
+
+
+ <%= check('funders').html_safe %>
+
<%= check('awards').html_safe %>
+ <% if current_user.min_curator? %><%= check('curator').html_safe %><% end %>
+
+
+ <%= check('updated_at').html_safe %>
+ <%= check('submit_date').html_safe %>
+ <%= check('publication_date').html_safe %>
+
+
+
\ No newline at end of file
diff --git a/app/views/stash_engine/admin_dashboard/_filter_date.html.erb b/app/views/stash_engine/admin_dashboard/_filter_date.html.erb
new file mode 100644
index 0000000000..1126f82297
--- /dev/null
+++ b/app/views/stash_engine/admin_dashboard/_filter_date.html.erb
@@ -0,0 +1,10 @@
+<%# locals: id, label %>
+
+
+ <%= label %>
+ from:
+ <%= date_field_tag("filters[#{id}][start_date]", @filters.dig(id.to_sym, :start_date), class: 'c-input__text', id: "#{id}start") %>
+ to:
+ <%= date_field_tag("filters[#{id}][end_date]", @filters.dig(id.to_sym, :end_date), class: 'c-input__text', id: "#{id}end") %>
+
+
\ No newline at end of file
diff --git a/app/views/stash_engine/admin_dashboard/_filters.html.erb b/app/views/stash_engine/admin_dashboard/_filters.html.erb
new file mode 100644
index 0000000000..833c2ceed2
--- /dev/null
+++ b/app/views/stash_engine/admin_dashboard/_filters.html.erb
@@ -0,0 +1,26 @@
+<%
+def selecter(id, options)
+ h = { member: 'Dryad member', sponsor: 'Journal sponsor' }
+ label = h[id.to_sym] || id.capitalize
+
+ '' +
+ "#{label}: " +
+ select_tag("filters[#{id}]", options_for_select(options, @filters.dig(id.to_sym)), id: "filter-#{id}", class: 'c-input__select') +
+ '
'
+end
+%>
+
+Filter
+
+ <%= selecter('member', [['', '']] + institution_select).html_safe %>
+ <%= selecter('status', [['', '']] + status_select).html_safe %>
+ <% if current_user.min_curator? %><%= selecter('curator', [['', ''], ['Unassigned', 'unassigned']] + editor_select).html_safe %><% end %>
+ <%# journal search select %>
+ <%= selecter('sponsor', [['', '']] + sponsor_select).html_safe %>
+ <%# funder search select %>
+ <%# affiliation search select %>
+
+ <%= render partial: 'filter_date', locals: { id: 'updated_at', label: 'Last modified' } %>
+ <%= render partial: 'filter_date', locals: { id: 'submit_date', label: 'Submitted' } %>
+ <%= render partial: 'filter_date', locals: { id: 'publication_date', label: 'Published' } %>
+
\ No newline at end of file
diff --git a/app/views/stash_engine/admin_dashboard/_table_header.html.erb b/app/views/stash_engine/admin_dashboard/_table_header.html.erb
new file mode 100644
index 0000000000..82fa050fa2
--- /dev/null
+++ b/app/views/stash_engine/admin_dashboard/_table_header.html.erb
@@ -0,0 +1,62 @@
+
+
+
+ <%= sortable_column_head sort_field: 'title', title: 'Description' %>
+
+ <% if @fields.include?('authors') %>
+
+ <%= sortable_column_head sort_field: 'author_string', title: 'Authors' %>
+
+ <% end %>
+ <% if @fields.include?('affiliations') || @fields.include?('countries') %>
+ <%= @fields.include?('affiliations') ? 'Affiliations' : 'Countries' %>
+ <% end %>
+ <% if @fields.include?('status') %>
+
+ <%= sortable_column_head sort_field: 'status', title: 'Status' %>
+
+ <% end %>
+ <% if @fields.include?('size') %>
+
+ <%= sortable_column_head sort_field: 'total_file_size', title: 'Size' %>
+
+ <% end %>
+ <% if @fields.include?('metrics') %>
+
+ <%= sortable_column_head sort_field: 'unique_investigation_count', title: 'Metrics' %>
+
+ <% end %>
+ <% if @fields.include?('funders') || @fields.include?('awards') %>
+ <%= @fields.include?('funders') ? 'Grant funders' : 'Award IDs' %>
+ <% end %>
+ <% if @fields.include?('journal') %>
+ Journal
+ <% end %>
+ <% if @fields.include?('sponsor') %>
+ Journal sponsor
+ <% end %>
+ <% if @fields.include?('curator') %>
+
+ <%= sortable_column_head sort_field: 'curator_name', title: 'Curator' %>
+
+ <% end %>
+ <% if @fields.include?('dpc') %>
+ DPC paid by
+ <% end %>
+ <% if @fields.include?('updated_at') %>
+
+ <%= sortable_column_head sort_field: 'updated_at', title: 'Last modified' %>
+
+ <% end %>
+ <% if @fields.include?('submit_date') %>
+
+ <%= sortable_column_head sort_field: 'submit_date', title: 'Submitted' %>
+
+ <% end %>
+ <% if @fields.include?('publication_date') %>
+
+ <%= sortable_column_head sort_field: 'publication_date', title: 'Published' %>
+
+ <% end %>
+
+
\ No newline at end of file
diff --git a/app/views/stash_engine/admin_dashboard/_table_row.html.erb b/app/views/stash_engine/admin_dashboard/_table_row.html.erb
new file mode 100644
index 0000000000..77c104e0e9
--- /dev/null
+++ b/app/views/stash_engine/admin_dashboard/_table_row.html.erb
@@ -0,0 +1,71 @@
+<% @datasets.each do |dataset| %>
+
+
+
+
+ <%= link_to dataset.title, stash_url_helpers.show_path(id: dataset.identifier_str), target: '_blank' %>
+ <% if @fields.include?('doi') %>
+
DOI: <%= dataset.identifier.identifier %>
+ <% end %>
+ <% if @fields.include?('keywords') && dataset.subjects.present? %>
+
<%= dataset.subjects.map(&:subject).join(', ') %>
+ <% end %>
+
+
+
+ <% if @fields.include?('authors') %>
+ <%= dataset.author_string %>
+ <% end %>
+ <% if @fields.include?('affiliations') || @fields.include?('countries') %>
+ <% affs = dataset.authors.map(&:affiliations).flatten.uniq.each_with_object([]) { |a, arr|
+ if a.ror_id
+ arr << a
+ end
+ arr }.map { |aff| ([@fields.include?('affiliations') ? aff.smart_name : nil] + [@fields.include?('countries') ? aff.country_name : nil]).reject(&:blank?).join(', ') }%>
+ <%= ([dataset.tenant_id == 'dryad' ? nil : ([@fields.include?('affiliations') ? dataset.tenant&.short_name : nil] + [@fields.include?('countries') ? dataset.tenant&.country_name : nil]).reject(&:blank?).join(', ')].flatten).concat(affs).uniq.reject(&:blank?).join('; ') %>
+ <% end %>
+ <% if @fields.include?('status') %>
+ <%= StashEngine::CurationActivity.readable_status(dataset.status) %>
+ <% end %>
+ <% if @fields.include?('size') %>
+ <%= filesize(dataset.total_file_size) %>
+ <% end %>
+ <% if @fields.include?('metrics') %>
+ <%= dataset.unique_investigation_count.blank? || dataset.unique_investigation_count < dataset.unique_request_count ? dataset.unique_request_count || 0 : dataset.unique_investigation_count %> views
+ <%= dataset.unique_request_count || 0 %> downloads
+ <%= dataset.citation_count || 0 %> citations
+
+ <% end %>
+ <% if @fields.include?('funders') || @fields.include?('awards') %>
+ <%= dataset.funders.map { |f| ([@fields.include?('funders') ? f.contributor_name : nil] + [@fields.include?('awards') ? f.award_number : nil]).reject(&:blank?).join(', ')}.uniq.reject(&:blank?).join('; ') %>
+ <% end %>
+ <% if @fields.include?('journal') %>
+ <%= dataset.journal_title %>
+ <% end %>
+ <% if @fields.include?('sponsor') %>
+ <%= dataset.sponsor_id && StashEngine::JournalOrganization.where(id: dataset.sponsor_id).first&.name %>
+ <% end %>
+ <% if @fields.include?('curator') %>
+ <%= dataset.curator_name %>
+ <% end %>
+ <% if @fields.include?('dpc') %>
+
+ <% dpc = ''
+ dpc = dataset.tenant&.short_name if dataset.identifier.payment_id = dataset.tenant_id
+ dpc = dataset.journal_title if dataset&.issn&.include?(dataset.identifier.payment_id)
+ dpc = dataset.identifier.payment_id.split("funder:").last.split("|").first if dataset.identifier.payment_id.starts_with?('funder')
+ %>
+ <%= dpc %>
+
+ <% end %>
+ <% if @fields.include?('updated_at') %>
+ <%= formatted_datetime(dataset.last_curation_activity.updated_at) %>
+ <% end %>
+ <% if @fields.include?('submit_date') %>
+ <%= formatted_datetime(dataset.submit_date) %>
+ <% end %>
+ <% if @fields.include?('publication_date') %>
+ <%= formatted_datetime(dataset.publication_date) %>
+ <% end %>
+
+<% end %>
\ No newline at end of file
diff --git a/app/views/stash_engine/admin_dashboard/index.csv.erb b/app/views/stash_engine/admin_dashboard/index.csv.erb
new file mode 100644
index 0000000000..364bb56028
--- /dev/null
+++ b/app/views/stash_engine/admin_dashboard/index.csv.erb
@@ -0,0 +1,60 @@
+<%
+head = 'Title'
+head += ',DOI' if @fields.include?('doi')
+head += ',Keywords' if @fields.include?('keywords')
+head += ',Authors' if @fields.include?('authors')
+if @fields.include?('affiliations') || @fields.include?('countries')
+ head += @fields.include?('affiliations') ? ',Affiliations' : ',Countries'
+end
+head += ',Status' if @fields.include?('status')
+head += ',Size' if @fields.include?('size')
+head += ',Metrics' if @fields.include?('metrics')
+if @fields.include?('funders') || @fields.include?('awards')
+ head += @fields.include?('funders') ? ',Grant funders' : ',Award IDs'
+end
+head += ',Journal' if @fields.include?('journal')
+head += ',Journal sponsor' if @fields.include?('sponsor')
+head += ',Curator' if @fields.include?('curator')
+head += ',DPC paid by' if @fields.include?('dpc')
+head += ',Last modified' if @fields.include?('updated_at')
+head += ',Submitted' if @fields.include?('submit_date')
+head += ',Published' if @fields.include?('publication_date')
+%>
+<%= head %>
+<% @datasets.each do |dataset|
+ row = [dataset.title]
+ row << dataset.identifier.identifier if @fields.include?('doi')
+ row << dataset.subjects.map(&:subject).join(', ') if @fields.include?('keywords')
+ row << dataset.author_string if @fields.include?('authors')
+ if @fields.include?('affiliations') || @fields.include?('countries')
+ affs = dataset.authors.map(&:affiliations).flatten.uniq.each_with_object([]) { |a, arr|
+ if a.ror_id
+ arr << a
+ end
+ arr }.map { |aff| ([@fields.include?('affiliations') ? aff.smart_name : nil] + [@fields.include?('countries')? aff.country_name : nil]).reject(&:blank?).join(', ') }
+ row << ([dataset.tenant_id == 'dryad' ? nil : ([@fields.include?('affiliations') ? dataset.tenant&.short_name : nil] + [@fields.include?('countries') ? dataset.tenant&.country_name : nil]).reject(&:blank?).join(', ')].flatten).concat(affs).uniq.reject(&:blank?).join('; ')
+ end
+ row << StashEngine::CurationActivity.readable_status(dataset.status) if @fields.include?('status')
+ row << filesize(dataset.total_file_size) if @fields.include?('size')
+ if @fields.include?('metrics')
+ row << "#{dataset.unique_investigation_count.blank? || dataset.unique_investigation_count < dataset.unique_request_count ? dataset.unique_request_count || 0 : dataset.unique_investigation_count} views, #{dataset.unique_request_count || 0} downloads, #{dataset.citation_count || 0} citations"
+ end
+ if @fields.include?('funders') || @fields.include?('awards')
+ row << dataset.funders.map { |f| ([@fields.include?('funders') ? f.contributor_name : nil] + [@fields.include?('awards') ? f.award_number : nil]).reject(&:blank?).join(', ')}.uniq.reject(&:blank?).join('; ')
+ end
+ row << dataset.journal_title if @fields.include?('journal')
+ row << dataset.sponsor_id && StashEngine::JournalOrganization.where(id: dataset.sponsor_id).first&.name if @fields.include?('sponsor')
+ row << dataset.curator_name if @fields.include?('curator')
+ if @fields.include?('dpc')
+ dpc = ''
+ dpc = dataset.tenant&.short_name if dataset.identifier.payment_id = dataset.tenant_id
+ dpc = dataset.journal_title if dataset&.issn&.include?(dataset.identifier.payment_id)
+ dpc = dataset.identifier.payment_id.split("funder:").last.split("|").first if dataset.identifier.payment_id.starts_with?('funder')
+ row << dpc
+ end
+ row << dataset.last_curation_activity.updated_at if @fields.include?('updated_at')
+ row << dataset.submit_date if @fields.include?('submit_date')
+ row << dataset.publication_date if @fields.include?('publication_date')
+%>
+<%= row.to_csv(row_sep: nil).html_safe %>
+<% end %>
\ No newline at end of file
diff --git a/app/views/stash_engine/admin_dashboard/index.html.erb b/app/views/stash_engine/admin_dashboard/index.html.erb
new file mode 100644
index 0000000000..98f04bd35e
--- /dev/null
+++ b/app/views/stash_engine/admin_dashboard/index.html.erb
@@ -0,0 +1,45 @@
+<% @page_title = "Admin dashboard" %>
+
+Admin dashboard
+
+<%= form_with(url: stash_url_helpers.admin_dashboard_path, method: 'post', id: 'search_form') do %>
+ <%= render partial: 'fields' %>
+ <%= render partial: 'filters' %>
+
+ Search terms:
+ <%= search_field_tag(:q, @search_string, class: 'c-input__text', id: 'search-string' ) %>
+ <%= submit_tag('Apply', name: nil, class: 'o-button__submit', style: 'margin-right: 0;' ) %>
+
+<% end %>
+
+
+ <%= @datasets.total_count %> results
+ <%= link_to 'Export all as CSV', stash_url_helpers.admin_dashboard_path(request.parameters.except(:action, :controller, :fields, :authenticity_token, :fields, :filters).merge(format: :csv)), class: 'o-link__buttonlink' %>
+
+
+
+ <%= render partial: 'table_header' %>
+
+ <%= render partial: 'table_row' %>
+
+
+
+
+
diff --git a/config/routes.rb b/config/routes.rb
index 9030cf2de6..eae130405d 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -302,6 +302,7 @@
post 'publisher_admin/:id', to: 'journal_organization_admin#edit', as: 'publisher_edit'
# admin_datasets, aka "Curator Dashboard"
+ match 'admin_dashboard', to: 'admin_dashboard#index', via: %i[get post]
get 'ds_admin', to: 'admin_datasets#index'
get 'ds_admin/:id/create_salesforce_case', to: 'admin_datasets#create_salesforce_case', as: 'create_salesforce_case'
get 'ds_admin/:id/activity_log', to: 'admin_datasets#activity_log', as: 'activity_log'
diff --git a/spec/models/stash/repo/merritt_oaidc_builder_spec.rb b/spec/models/stash/repo/merritt_oaidc_builder_spec.rb
index 01f602a176..a9807479b5 100644
--- a/spec/models/stash/repo/merritt_oaidc_builder_spec.rb
+++ b/spec/models/stash/repo/merritt_oaidc_builder_spec.rb
@@ -6,19 +6,15 @@ module Builders
attr_reader :tenant
before(:each) do
- user = StashEngine::User.create(
- email: 'lmuckenhaupt@example.edu',
- tenant_id: 'dataone'
- )
+ user = create(:user, email: 'lmuckenhaupt@example.edu', tenant_id: 'dataone')
dc4_xml = File.read('spec/data/stash-merritt/mrt-datacite.xml')
dcs_resource = Datacite::Mapping::Resource.parse_xml(dc4_xml)
stash_wrapper_xml = File.read('spec/data/stash-merritt/stash-wrapper.xml')
stash_wrapper = Stash::Wrapper::StashWrapper.parse_xml(stash_wrapper_xml)
- @tenant = double(StashEngine::Tenant)
- allow(tenant).to receive(:short_name).and_return('DataONE')
- allow(StashEngine::Tenant).to receive(:find).with('dataone').and_return(tenant)
+ @tenant = StashEngine::Tenant.find('dataone')
+ @tenant.update(short_name: 'DataONE')
@resource = StashDatacite::ResourceBuilder.new(
user_id: user.id,
diff --git a/spec/models/stash_datacite/affiliation_spec.rb b/spec/models/stash_datacite/affiliation_spec.rb
index a976e663bd..399ed70c9a 100644
--- a/spec/models/stash_datacite/affiliation_spec.rb
+++ b/spec/models/stash_datacite/affiliation_spec.rb
@@ -56,11 +56,10 @@ module StashDatacite
before(:each) do
@affil = StashDatacite::Affiliation.create(long_name: 'Bertelsmann Music Group', ror_id: '12345')
@ror_org = StashEngine::RorOrg.new(ror_id: '12345', name: 'Bertelsmann Music Group')
- allow(StashEngine::RorOrg).to receive(:find_by_ror_id).and_return(@ror_org)
end
it 'returns the correct country_name when given a country object' do
- @ror_org.country = 'East Timor'
+ @ror_org.update(country: 'East Timor')
expect(@affil.country_name).to eql('East Timor')
end
end
From b4c15a823b87c89f2517186d2ebeedb9aef92dc9 Mon Sep 17 00:00:00 2001
From: Audrey Hamelers
Date: Thu, 13 Jun 2024 17:21:00 +0200
Subject: [PATCH 2/8] search select combobox
---
.../javascripts/stash_engine/combobox.js | 108 ++++++++++++++++++
app/assets/stylesheets/scss/_select.scss | 51 +++++++++
.../stash_datacite/contributors_controller.rb | 20 +---
.../admin_dashboard_controller.rb | 27 +++--
.../stash_engine/user_admin_controller.rb | 4 +-
.../admin_dashboard/_filters.html.erb | 37 +++++-
.../shared/_search_select.html.erb | 55 +++++++++
.../user_admin/_admin_role_fieldset.html.erb | 7 +-
.../user_admin/_admin_role_form.html.erb | 32 +++++-
spec/features/stash_engine/user_admin_spec.rb | 4 +-
10 files changed, 305 insertions(+), 40 deletions(-)
create mode 100644 app/assets/javascripts/stash_engine/combobox.js
create mode 100644 app/views/stash_engine/shared/_search_select.html.erb
diff --git a/app/assets/javascripts/stash_engine/combobox.js b/app/assets/javascripts/stash_engine/combobox.js
new file mode 100644
index 0000000000..ed54dc4e54
--- /dev/null
+++ b/app/assets/javascripts/stash_engine/combobox.js
@@ -0,0 +1,108 @@
+'use strict';
+
+class ComboboxAutocomplete {
+ constructor(combobox, textbox, list, fill, selection) {
+ this.combobox = combobox
+ this.textbox = textbox
+ this.list = list
+ this.fill = fill
+ this.selection = selection
+
+ this.textbox.addEventListener('beforeinput', this.open.bind(this))
+ this.textbox.addEventListener('input', this.enter.bind(this))
+
+ this.textbox.addEventListener('blur', this.unFocus.bind(this), true)
+ this.list.addEventListener('blur', this.unFocus.bind(this), true)
+ this.combobox.addEventListener('blur', this.unFocus.bind(this), true)
+
+ this.textbox.addEventListener('keydown', this.keyPressed.bind(this))
+ this.list.addEventListener('keydown', this.listPressed.bind(this), true)
+
+ this.list.addEventListener('click', this.saveOption.bind(this), true)
+ }
+ open() {
+ this.fill()
+ this.list.removeAttribute('hidden')
+ this.textbox.setAttribute('aria-expanded', true)
+ }
+ enter() {
+ this.fill()
+ if (this.textbox.value.length == 0) {
+ this.selection({value: '', label: ''})
+ }
+ }
+ close(){
+ this.list.setAttribute('hidden', true)
+ this.textbox.setAttribute('aria-expanded', false)
+ }
+ unFocus(e) {
+ if (!this.combobox.contains(e.relatedTarget)) {
+ this.close()
+ }
+ }
+ selectOption({value, label}) {
+ const [prev] = this.list.getElementsByClassName('selected-option')
+ if (prev) {
+ prev.classList.remove('selected-option')
+ prev.setAttribute('aria-selected', false)
+ }
+ const selected = this.list.querySelector(`[data-value="${value}"]`)
+ selected.classList.add('selected-option')
+ selected.setAttribute('aria-selected', true)
+ this.selection({value, label})
+ this.textbox.value = label
+ }
+ saveOption(e) {
+ e.preventDefault()
+ e.stopPropagation()
+ this.selectOption(e.target.dataset)
+ this.textbox.focus()
+ this.close()
+ }
+ listPressed(e) {
+ this.keyPressed(e)
+ }
+ keyPressed(e, option) {
+ switch (e.key) {
+ case 'Enter':
+ if (e.target.hasAttribute('data-value')) this.saveOption(e)
+ else if (e.target.id === this.textbox.id) {
+ this.open()
+ e.preventDefault()
+ }
+ break
+ case 'ArrowDown':
+ case 'ArrowRight':
+ if (e.target.id === this.textbox.id) {
+ if (this.list.hasAttribute('hidden')) this.open()
+ this.list.firstChild.focus()
+ e.preventDefault()
+ } else if (e.target.nextSibling) {
+ e.target.nextSibling.focus()
+ e.preventDefault()
+ }
+ break
+ case 'ArrowUp':
+ if (e.target.id !== this.textbox.id && e.target.previousSibling) {
+ e.target.previousSibling.focus()
+ e.preventDefault()
+ }
+ break
+ case 'Home':
+ if (e.target.id !== this.textbox.id) {
+ this.list.firstChild.focus()
+ e.preventDefault()
+ }
+ break
+ case 'End':
+ if (e.target.id !== this.textbox.id) {
+ this.list.lastChild.focus()
+ e.preventDefault()
+ }
+ break
+ default:
+ break
+ }
+ }
+
+}
diff --git a/app/assets/stylesheets/scss/_select.scss b/app/assets/stylesheets/scss/_select.scss
index 6948fa1cdc..8c4c303300 100644
--- a/app/assets/stylesheets/scss/_select.scss
+++ b/app/assets/stylesheets/scss/_select.scss
@@ -3,3 +3,54 @@
.o-select {
margin: 0 0 20px;
}
+
+.searchselect {
+ position: relative;
+ flex-grow: 2;
+
+ & > input {
+ width: 100%;
+ }
+
+ &:focus-within input {
+ outline: 2px solid $design-medium-blue-color;
+ }
+
+ ul {
+ margin: 0;
+ padding: 2px 0;
+ list-style-type: none;
+ position: absolute;
+ top: 32px;
+ right: -2px;
+ left: -2px;
+ z-index: 10;
+ width: calc(100% + 4px);
+ max-height: 50vh;
+ overflow-y: auto;
+ background-color: $design-extra-light-gray-color;
+
+ li {
+ padding: 6px 10px;
+ cursor: default;
+ &[role='option'] {
+ padding-left: 3ch;
+ &:before {
+ content: '';
+ display: inline-block;
+ width: 2.5ch;
+ margin-left: -2.5ch;
+ font-family: FontAwesome;
+ }
+ &.selected-option:before {
+ content: '\f00c';
+ }
+ &:hover,
+ &:focus {
+ color: $design-white-color;
+ background-color: $design-dark-blue-color;
+ }
+ }
+ }
+ }
+}
diff --git a/app/controllers/stash_datacite/contributors_controller.rb b/app/controllers/stash_datacite/contributors_controller.rb
index 68e3f6c29e..23d7cde3ca 100644
--- a/app/controllers/stash_datacite/contributors_controller.rb
+++ b/app/controllers/stash_datacite/contributors_controller.rb
@@ -64,24 +64,16 @@ def reorder
end
end
- # GET /contributors/autocomplete?term={query_term}
+ # GET /contributors/autocomplete?query={query_term}
def autocomplete
- return if params.blank?
-
- partial_term = params['term']
+ partial_term = params['query']
if partial_term.blank?
render json: nil
else
- # clean the partial_term of unwanted characters so it doesn't cause errors when calling the CrossRef API
- partial_term.gsub!(%r{[/\-\\()~!@%&"\[\]\^:]}, ' ')
- response = HTTParty.get('https://api.crossref.org/funders',
- query: { query: partial_term },
- headers: { 'Content-Type' => 'application/json' })
- return if response.parsed_response.blank?
- return if response.parsed_response['message'].blank?
-
- result_list = response.parsed_response['message']['items']
- render json: bubble_up_exact_matches(result_list: result_list, term: partial_term)
+ @affiliations = StashEngine::RorOrg.distinct.joins(
+ "inner join dcs_contributors on identifier_type = 'ror' and contributor_type = 'funder' and name_identifier_id = ror_id"
+ ).find_by_ror_name(partial_term)
+ render json: @affiliations
end
end
diff --git a/app/controllers/stash_engine/admin_dashboard_controller.rb b/app/controllers/stash_engine/admin_dashboard_controller.rb
index b31ff77485..98bfb4659e 100644
--- a/app/controllers/stash_engine/admin_dashboard_controller.rb
+++ b/app/controllers/stash_engine/admin_dashboard_controller.rb
@@ -100,21 +100,19 @@ def add_fields
# rubocop:disable Style/MultilineIfModifier
def add_filters
+ if @filters[:member].present? || @filters.dig(:funder, :value).present? || @filters.dig(:affiliation, :value).present?
+ @datasets = @datasets.left_outer_joins(authors: :affiliations).left_outer_joins(:funders)
+ end
+ tenant_filter
@datasets = @datasets.where('stash_engine_curation_activities.status': @filters[:status]) if @filters[:status].present?
@datasets = @datasets.where(
'curator.id': Integer(@filters[:curator], exception: false) ? @filters[:curator] : nil
) if @filters[:curator].present?
+ @datasets = @datasets.where('stash_engine_journals.id': @filters.dig(:journal, :value)) if @filters.dig(:journal, :value).present?
@datasets = @datasets.where('stash_engine_journals.sponsor_id': @filters[:sponsor]) if @filters[:sponsor].present?
@datasets = @datasets.where("MATCH(stash_engine_identifiers.search_words) AGAINST('#{@search_string}') > 0") unless @search_string.blank?
- return unless @filters[:member].present? && StashEngine::Tenant.find_by(id: @filters[:member]).present?
-
- tenant_orgs = StashEngine::Tenant.find(@filters[:member]).ror_ids
- @datasets = @datasets.left_outer_joins(authors: :affiliations).left_outer_joins(:funders)
- @datasets = @datasets.where(
- 'stash_engine_resources.tenant_id = ? or stash_engine_identifiers.payment_id = ?
- or dcs_affiliations.ror_id in (?) or dcs_contributors.name_identifier_id in (?)',
- @filters[:member], @filters[:member], tenant_orgs, tenant_orgs
- )
+ @datasets = @datasets.where('dcs_contributors.name_identifier_id': @filters.dig(:funder, :value)) if @filters.dig(:funder, :value).present?
+ @datasets = @datasets.where('dcs_affiliations.ror_id': @filters.dig(:affiliation, :value)) if @filters.dig(:affiliation, :value).present?
end
def date_filters
@@ -130,6 +128,17 @@ def date_filters
end
# rubocop:enable Style/MultilineIfModifier
+ def tenant_filter
+ return unless @filters[:member].present? && StashEngine::Tenant.find_by(id: @filters[:member]).present?
+
+ tenant_orgs = StashEngine::Tenant.find(@filters[:member]).ror_ids
+ @datasets = @datasets.where(
+ 'stash_engine_resources.tenant_id = ? or stash_engine_identifiers.payment_id = ?
+ or dcs_affiliations.ror_id in (?) or dcs_contributors.name_identifier_id in (?)',
+ @filters[:member], @filters[:member], tenant_orgs, tenant_orgs
+ )
+ end
+
def date_string(date_hash)
from = date_hash[:start_date]
to = date_hash[:end_date]
diff --git a/app/controllers/stash_engine/user_admin_controller.rb b/app/controllers/stash_engine/user_admin_controller.rb
index b38fa18efe..77e3d5611e 100644
--- a/app/controllers/stash_engine/user_admin_controller.rb
+++ b/app/controllers/stash_engine/user_admin_controller.rb
@@ -93,7 +93,7 @@ def set_role
# set publisher role
save_role(role_params[:publisher_role], @publisher_role, StashEngine::JournalOrganization.find_by(id: role_params[:publisher]))
# set journal role
- save_role(role_params[:journal_role], @journal_role, StashEngine::Journal.find_by(id: role_params[:journal]))
+ save_role(role_params[:journal_role], @journal_role, StashEngine::Journal.find_by(id: role_params.dig(:journal, :value)))
# set funder role
save_role(role_params[:funder_role], @funder_role, StashEngine::Funder.find_by(id: role_params[:funder]))
# reload roles
@@ -192,7 +192,7 @@ def edit_params
end
def role_params
- params.permit(:role, :tenant_role, :publisher, :publisher_role, :journal, :journal_role, :funder, :funder_role)
+ params.permit(:role, :tenant_role, :publisher, :publisher_role, :funder, :funder_role, :journal_role, journal: %i[value label])
end
end
end
diff --git a/app/views/stash_engine/admin_dashboard/_filters.html.erb b/app/views/stash_engine/admin_dashboard/_filters.html.erb
index 833c2ceed2..a69e9f01af 100644
--- a/app/views/stash_engine/admin_dashboard/_filters.html.erb
+++ b/app/views/stash_engine/admin_dashboard/_filters.html.erb
@@ -15,10 +15,41 @@ end
<%= selecter('member', [['', '']] + institution_select).html_safe %>
<%= selecter('status', [['', '']] + status_select).html_safe %>
<% if current_user.min_curator? %><%= selecter('curator', [['', ''], ['Unassigned', 'unassigned']] + editor_select).html_safe %><% end %>
- <%# journal search select %>
+ <% # locals: field_name, id, selected, options_path, placeholder %>
+
+ <%= render partial: 'stash_engine/shared/search_select', locals: {
+ id: 'journal',
+ label: 'Journal',
+ field_name: 'filters[journal]',
+ options_path: '/stash_datacite/publications/autocomplete?term=',
+ options_label: 'title',
+ options_value: 'id',
+ selected: @filters.dig(:journal)
+ } %>
+
<%= selecter('sponsor', [['', '']] + sponsor_select).html_safe %>
- <%# funder search select %>
- <%# affiliation search select %>
+
+ <%= render partial: 'stash_engine/shared/search_select', locals: {
+ id: 'funder',
+ label: 'Funder',
+ field_name: 'filters[funder]',
+ options_path: '/stash_datacite/contributors/autocomplete?query=',
+ options_label: 'name',
+ options_value: 'id',
+ selected: @filters.dig(:funder)
+ } %>
+
+
+ <%= render partial: 'stash_engine/shared/search_select', locals: {
+ id: 'affiliation',
+ label: 'Affiliation',
+ field_name: 'filters[affiliation]',
+ options_path: '/stash_datacite/affiliations/autocomplete?query=',
+ options_label: 'name',
+ options_value: 'id',
+ selected: @filters.dig(:affiliation)
+ } %>
+
<%= render partial: 'filter_date', locals: { id: 'updated_at', label: 'Last modified' } %>
<%= render partial: 'filter_date', locals: { id: 'submit_date', label: 'Submitted' } %>
diff --git a/app/views/stash_engine/shared/_search_select.html.erb b/app/views/stash_engine/shared/_search_select.html.erb
new file mode 100644
index 0000000000..acaf48623c
--- /dev/null
+++ b/app/views/stash_engine/shared/_search_select.html.erb
@@ -0,0 +1,55 @@
+<% # locals: field_name, label, id, selected, options_path, options_label, options_value %>
+<%= label %>:
+
+
+
+ <%= text_field_tag(nil, selected&.dig(:label), id: "searchselect-#{id}__input", class: 'c-input__select', placeholder: 'Find as you type...', 'aria-autocomplete': 'both', 'aria-controls': "searchselect-#{id}__list", 'aria-expanded': 'false', role: 'combobox') %>
+
+
+
+
\ No newline at end of file
diff --git a/app/views/stash_engine/user_admin/_admin_role_fieldset.html.erb b/app/views/stash_engine/user_admin/_admin_role_fieldset.html.erb
index e7955e7134..4ba3485af0 100644
--- a/app/views/stash_engine/user_admin/_admin_role_fieldset.html.erb
+++ b/app/views/stash_engine/user_admin/_admin_role_fieldset.html.erb
@@ -6,12 +6,7 @@
<%= select_label %> <%= @user.tenant.short_name %>
<% else %>
<%= label_tag(label, select_label, class: 'c-input__label') %>
- <% if options.length < 10 %>
- <%= select_tag(label, options_for_select(options, user_role&.role_object_id), class: 'c-input__select') %>
- <% else %>
- <%= text_field_tag(label, options.find {|o| o[1].to_i == user_role&.role_object_id&.to_i }&.first, list: "#{label}-options", class: 'c-input__select', placeholder: 'Begin typing...') %>
- <%= options_for_select(options, user_role&.role_object_id) %>
- <% end %>
+ <%= select_tag(label, options_for_select(options, user_role&.role_object_id), class: 'c-input__select') %>
<% end %>
diff --git a/app/views/stash_engine/user_admin/_admin_role_form.html.erb b/app/views/stash_engine/user_admin/_admin_role_form.html.erb
index 0ea9c40c32..a7bae42e5f 100644
--- a/app/views/stash_engine/user_admin/_admin_role_form.html.erb
+++ b/app/views/stash_engine/user_admin/_admin_role_form.html.erb
@@ -47,20 +47,24 @@ funder_roles = [
flex-shrink: 0;
flex-grow: 1;
}
- #role_form_flex fieldset p {
+ #role_form_flex fieldset p,
+ #role_form_flex fieldset div {
margin: 0 auto 1rem;
}
- #role_form_flex fieldset ul {
+ #role_form_flex fieldset div > div {
+ display: inline-block;
+ }
+ #role_form_flex fieldset > ul {
margin: 0;
padding: 0;
list-style-type: none;
}
- #role_form_flex fieldset ul li {
+ #role_form_flex fieldset > ul li {
padding-left: 2ch;
text-indent: -2ch;
margin-bottom: .3rem;
}
- #role_form_flex fieldset ul li span {
+ #role_form_flex fieldset > ul li span {
font-size: .9em;
margin-left: .5ch;
padding-left: .75ch;
@@ -91,7 +95,25 @@ funder_roles = [
<% unless @journal_role %>
Add a journal role
<% end %>
- <%= render partial: 'admin_role_fieldset', locals: { user_role: @journal_role, label: 'journal', select_label: 'For journal:', roles: journal_roles, options: StashEngine::Journal.all.collect { |j| [ j.title, j.id ] } } %>
+ >
+ Journal roles:
+
+ <%= render partial: 'stash_engine/shared/search_select', locals: {
+ id: 'journal',
+ label: 'For journal',
+ field_name: 'journal',
+ options_path: '/stash_datacite/publications/autocomplete?term=',
+ options_label: 'title',
+ options_value: 'id',
+ selected: {value: @journal_role&.role_object_id, label: @user&.journals&.map(&:title)}
+ } %>
+
+
+ <% journal_roles.each do |role| %>
+ <%= radio_button_tag("journal_role", role.first, @journal_role&.role == role.first) %> "><%= role[1].html_safe %>
+ <% end %>
+
+
<% unless @funder_role %>
Add a funder role
<% end %>
diff --git a/spec/features/stash_engine/user_admin_spec.rb b/spec/features/stash_engine/user_admin_spec.rb
index e1489553e4..1d0ac2177a 100644
--- a/spec/features/stash_engine/user_admin_spec.rb
+++ b/spec/features/stash_engine/user_admin_spec.rb
@@ -172,7 +172,7 @@
before(:each) do
create(:tenant_dryad)
create(:journal_organization)
- create(:journal)
+ @journal = create(:journal)
create(:funder)
@user.update(tenant_id: 'dryad')
visit "#{stash_url_helpers.user_admin_profile_path(@user.id)}#edit_roles"
@@ -218,6 +218,8 @@
end
it 'allows the superuser to set the journal role' do
find_button('Add a journal role').click
+ fill_in 'searchselect-journal__input', with: @journal.title
+ find("li[data-value='#{@journal.id}']").click
find('#journal_role_admin').set(true)
find('input[name=commit]').click
expect(page.find("#user_role_#{@user.id}")).to have_text('Journal admin')
From 23f7764ad5683d8e90364e9a3a80ad0293d7003e Mon Sep 17 00:00:00 2001
From: Audrey Hamelers
Date: Fri, 14 Jun 2024 00:57:05 +0200
Subject: [PATCH 3/8] beautification and help text
---
.../stylesheets/scss/_admin-layout.scss | 100 ++++++++++++++++
.../admin_dashboard/_fields.html.erb | 112 ++++++++++++------
.../admin_dashboard/_filters.html.erb | 10 +-
.../admin_dashboard/index.html.erb | 22 +++-
4 files changed, 197 insertions(+), 47 deletions(-)
diff --git a/app/assets/stylesheets/scss/_admin-layout.scss b/app/assets/stylesheets/scss/_admin-layout.scss
index 18af269a09..551c69e9a4 100644
--- a/app/assets/stylesheets/scss/_admin-layout.scss
+++ b/app/assets/stylesheets/scss/_admin-layout.scss
@@ -96,3 +96,103 @@
margin-right: .5ch;
}
}
+
+.admin-dashboard-form {
+ margin-bottom: 2rem;
+
+ #edit_fields-form,
+ #edit_filters-form {
+ border: none;
+ background-color: $design-lightest-blue-color;
+ padding: 20px;
+ max-width: 100%;
+ margin-bottom: 1.5rem;
+ }
+
+ #edit_fields-form {
+ columns: 6;
+ @media (max-width: 920px) { columns: 3 }
+ @media (max-width: 420px) { columns: 2 }
+
+ div {
+ margin-bottom: 4px;
+ }
+
+ &.show-help {
+ columns: 3;
+ @media (max-width: 620px) { columns: 2 }
+ div {
+ display: table;
+
+ }
+ .help {
+ margin: .2rem 0 1rem 1.1rem;
+ }
+ }
+ }
+
+ #edit_filters-form {
+ display: flex;
+ flex-wrap: wrap;
+ row-gap: 2ch;
+ column-gap: 2ch;
+
+ .help {
+ margin: .5rem auto 1rem;
+ }
+ }
+
+ .help {
+ display: none;
+ font-size: .9em;
+ }
+
+ .show-help .help {
+ display: block;
+ }
+
+ .field-pair {
+ display: flex;
+ align-items: baseline;
+ flex-wrap: nowrap;
+ gap: .5ch;
+ }
+
+ select{
+ max-width: 80vw;
+ text-overflow: ellipsis;
+ }
+
+ .c-input__select {
+ background-color: $design-white-color;
+ }
+
+ #search-string {
+ flex-basis: 500px;
+ flex-grow: 2;
+ }
+}
+
+.admin-dashboard-buttons {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ column-gap: 1ch;
+ row-gap: .5ch;
+ flex-wrap: wrap;
+ margin: .8rem auto .2rem;
+
+ *[role='heading'] {
+ font-size: 1.15em;
+ }
+}
+
+.admin-dashboard-count {
+ margin-bottom: -1rem;
+ text-align: right;
+ font-size: .9em;
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ column-gap: 2ch;
+}
diff --git a/app/views/stash_engine/admin_dashboard/_fields.html.erb b/app/views/stash_engine/admin_dashboard/_fields.html.erb
index 9dbc22b3c2..056d748942 100644
--- a/app/views/stash_engine/admin_dashboard/_fields.html.erb
+++ b/app/views/stash_engine/admin_dashboard/_fields.html.erb
@@ -4,45 +4,85 @@ def check(id)
label = h[id.to_sym]
label ||= id.length < 4 ? id.upcase : id.capitalize
- '' +
- check_box_tag('fields[]', id, @fields.include?(id), {id: id}) +
+ '' +
+ check_box_tag('fields[]', id, @fields.include?(id), {id: id, 'aria-describedby': "#{id}-desc"}) +
label_tag('fields[]', label, {for: id})+
- '
'
+ ''
end
%>
-
-
- Fields
-
-
- Description:
- <%= check('doi').html_safe %>
- <%= check('keywords').html_safe %>
-
-
- <%= check('authors').html_safe %>
- <%= check('affiliations').html_safe %>
-
<%= check('countries').html_safe %>
-
-
- <%= check('status').html_safe %>
- <%= check('metrics').html_safe %>
- <%= check('size').html_safe %>
-
-
- <%= check('journal').html_safe %>
- <%= check('sponsor').html_safe %>
- <%= check('dpc').html_safe %>
-
-
- <%= check('funders').html_safe %>
-
<%= check('awards').html_safe %>
- <% if current_user.min_curator? %><%= check('curator').html_safe %><% end %>
-
-
- <%= check('updated_at').html_safe %>
- <%= check('submit_date').html_safe %>
- <%= check('publication_date').html_safe %>
+
+ Display fields
+ Display field help
+
+
+
+ <%= check('doi').html_safe %>
+
Identifier assigned to the dataset. Appears under Description.
+
+
+ <%= check('keywords').html_safe %>
+ Subjects entered by the submitter. Appear under Description.
+
+
+ <%= check('authors').html_safe %>
+ Authors list provided for the dataset.
+
+
+ <%= check('affiliations').html_safe %>
+
Author affiliations matched with a ROR ID .
+
+
+ <%= check('countries').html_safe %>
+ Countries associated with author affiliations.
+
+
+ <%= check('status').html_safe %>
+ Status of the submission in Dryad processing.
+
+
+ <%= check('metrics').html_safe %>
+ View, download, and citation statistics.
+
+
+ <%= check('size').html_safe %>
+ Total size of the uploaded data files.
+
+
+ <%= check('journal').html_safe %>
+ Journal of the manuscript or primary article associated with the dataset.
+
+
+ <%= check('sponsor').html_safe %>
+
+
+
+
+ <%= check('funders').html_safe %>
+
Funding organizations matched with a ROR ID .
+
+
+ <%= check('awards').html_safe %>
+ Grant and other funder identifiers.
+
+ <% if current_user.min_curator? %>
+
+ <%= check('curator').html_safe %>
+ The curator currently editing the dataset.
+ <% end %>
+
+ <%= check('updated_at').html_safe %>
+ Date of the last edit made.
+
+
+ <%= check('submit_date').html_safe %>
+ Date submitted for curation or peer review.
+
+
+ <%= check('publication_date').html_safe %>
+ Publication date of the dataset.
\ No newline at end of file
diff --git a/app/views/stash_engine/admin_dashboard/_filters.html.erb b/app/views/stash_engine/admin_dashboard/_filters.html.erb
index a69e9f01af..0de62686b5 100644
--- a/app/views/stash_engine/admin_dashboard/_filters.html.erb
+++ b/app/views/stash_engine/admin_dashboard/_filters.html.erb
@@ -9,13 +9,13 @@ def selecter(id, options)
'
'
end
%>
-
-
Filter
-
+
+ Search filters
+
+
<%= selecter('member', [['', '']] + institution_select).html_safe %>
<%= selecter('status', [['', '']] + status_select).html_safe %>
<% if current_user.min_curator? %><%= selecter('curator', [['', ''], ['Unassigned', 'unassigned']] + editor_select).html_safe %><% end %>
- <% # locals: field_name, id, selected, options_path, placeholder %>
<%= render partial: 'stash_engine/shared/search_select', locals: {
id: 'journal',
@@ -54,4 +54,4 @@ end
<%= render partial: 'filter_date', locals: { id: 'updated_at', label: 'Last modified' } %>
<%= render partial: 'filter_date', locals: { id: 'submit_date', label: 'Submitted' } %>
<%= render partial: 'filter_date', locals: { id: 'publication_date', label: 'Published' } %>
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/views/stash_engine/admin_dashboard/index.html.erb b/app/views/stash_engine/admin_dashboard/index.html.erb
index 98f04bd35e..f314629eef 100644
--- a/app/views/stash_engine/admin_dashboard/index.html.erb
+++ b/app/views/stash_engine/admin_dashboard/index.html.erb
@@ -2,18 +2,28 @@
Admin dashboard
-<%= form_with(url: stash_url_helpers.admin_dashboard_path, method: 'post', id: 'search_form') do %>
+<%= form_with(url: stash_url_helpers.admin_dashboard_path, method: 'post', id: 'search_form', class: 'admin-dashboard-form') do %>
<%= render partial: 'fields' %>
<%= render partial: 'filters' %>
-
- Search terms:
- <%= search_field_tag(:q, @search_string, class: 'c-input__text', id: 'search-string' ) %>
+
<% end %>
-
- <%= @datasets.total_count %> results
+
+
+
+ <%= number_with_delimiter(@datasets.total_count) %> results
<%= link_to 'Export all as CSV', stash_url_helpers.admin_dashboard_path(request.parameters.except(:action, :controller, :fields, :authenticity_token, :fields, :filters).merge(format: :csv)), class: 'o-link__buttonlink' %>
From 6d5b6b37f256e91eebb26bbd6aad0856787bbb25 Mon Sep 17 00:00:00 2001
From: Audrey Hamelers
Date: Fri, 14 Jun 2024 20:32:59 +0200
Subject: [PATCH 4/8] user role permissions & switcher
---
.../stylesheets/scss/_admin-layout.scss | 9 ++
.../admin_dashboard_controller.rb | 86 +++++++++++++++----
.../stash_engine/user_admin_controller.rb | 2 +-
app/models/stash_engine/user.rb | 4 +-
app/policies/stash_engine/resource_policy.rb | 4 +
.../admin_dashboard/_filters.html.erb | 47 ++++++----
.../admin_dashboard/_role_select.html.erb | 18 ++++
.../admin_dashboard/index.html.erb | 5 +-
8 files changed, 140 insertions(+), 35 deletions(-)
create mode 100644 app/views/stash_engine/admin_dashboard/_role_select.html.erb
diff --git a/app/assets/stylesheets/scss/_admin-layout.scss b/app/assets/stylesheets/scss/_admin-layout.scss
index 551c69e9a4..981d184319 100644
--- a/app/assets/stylesheets/scss/_admin-layout.scss
+++ b/app/assets/stylesheets/scss/_admin-layout.scss
@@ -173,6 +173,15 @@
}
}
+.admin-dashboard-header {
+ display: flex;
+ align-items: baseline;
+ justify-content: space-between;
+ column-gap: 1ch;
+ row-gap: .5ch;
+ flex-wrap: wrap;
+}
+
.admin-dashboard-buttons {
display: flex;
align-items: center;
diff --git a/app/controllers/stash_engine/admin_dashboard_controller.rb b/app/controllers/stash_engine/admin_dashboard_controller.rb
index 98bfb4659e..13c3d06bd4 100644
--- a/app/controllers/stash_engine/admin_dashboard_controller.rb
+++ b/app/controllers/stash_engine/admin_dashboard_controller.rb
@@ -3,12 +3,13 @@ class AdminDashboardController < ApplicationController
helper SortableTableHelper
before_action :require_user_login
before_action :setup_paging, only: :index
+ before_action :setup_limits, only: :index
before_action :setup_search, only: :index
# before_action :load, only: %i[popup note_popup edit]
# rubocop:disable Metrics/MethodLength
def index
- @datasets = StashEngine::Resource.latest_per_dataset
+ @datasets = authorize StashEngine::Resource.latest_per_dataset
.left_outer_joins(identifier: %i[counter_stat internal_data])
.preload(identifier: %i[counter_stat internal_data])
.left_outer_joins(:last_curation_activity)
@@ -41,7 +42,6 @@ def index
add_fields
add_filters
- date_filters
order_string = 'relevance desc'
if params[:sort].present?
@@ -79,6 +79,7 @@ def setup_paging
end
end
+ # rubocop:disable Style/MultilineIfModifier
def setup_search
@search_string = params[:q] || ''
@filters = params[:filters] || session[:admin_search_filters] || {}
@@ -87,32 +88,53 @@ def setup_search
session[:admin_search_fields] = params[:fields] if params[:fields].present?
return unless @fields.blank?
- @fields = %w[doi keywords authors status metrics submit_date publication_date]
+ @fields = %w[doi authors status metrics submit_date publication_date]
+ @fields << 'journal' if @sponsor_limit
+ @fields << 'affiliations' if @role_object.is_a?(StashEngine::Tenant)
+ @fields << 'awards' if @role_object.is_a?(StashEngine::Funder)
@fields << 'curator' if current_user.min_curator?
end
+ def setup_limits
+ session[:admin_search_role] = params[:user_role] if params[:user_role].present?
+ user_role = current_user.roles.find_by(id: session[:admin_search_role]) || current_user.roles.first
+ @role_object = user_role.role_object
+ @tenant_limit = @role_object.is_a?(StashEngine::Tenant) ? policy_scope(StashEngine::Tenant) : StashEngine::Tenant.enabled
+ if @role_object.is_a?(StashEngine::JournalOrganization)
+ sponsor_limit = [@role_object]
+ sponsor_limit += @role_object.orgs_included if @role_object.orgs_included
+ end
+ @sponsor_limit = sponsor_limit || []
+ journal_limit = @role_object.journals_sponsored_deep if @sponsor_limit.present?
+ journal_limit = [@role_object] if @role_object.is_a?(StashEngine::Journal)
+ @journal_limit = journal_limit || []
+ end
+
def add_fields
@datasets = @datasets.preload(:funders) if @fields.include?('funders')
@datasets = @datasets.preload(:subjects) if @fields.include?('keywords')
@datasets = @datasets.preload(authors: :affiliations).preload(:tenant) if @fields.include?('affiliations')
@datasets = @datasets.preload(tenant: :ror_orgs).preload(authors: { affiliations: :ror_org }) if @fields.include?('countries')
+ return unless @filters[:member].present? || @filters.dig(:funder, :value).present? || @filters.dig(:affiliation, :value).present? ||
+ @role_object.is_a?(StashEngine::Tenant) || @role_object.is_a?(StashEngine::Funder)
+
+ @datasets = @datasets.left_outer_joins(authors: :affiliations).left_outer_joins(:funders)
end
- # rubocop:disable Style/MultilineIfModifier
def add_filters
- if @filters[:member].present? || @filters.dig(:funder, :value).present? || @filters.dig(:affiliation, :value).present?
- @datasets = @datasets.left_outer_joins(authors: :affiliations).left_outer_joins(:funders)
- end
tenant_filter
+ journal_filter
+ sponsor_filter
+ funder_filter
+
@datasets = @datasets.where('stash_engine_curation_activities.status': @filters[:status]) if @filters[:status].present?
+ @datasets = @datasets.where('dcs_affiliations.ror_id': @filters.dig(:affiliation, :value)) if @filters.dig(:affiliation, :value).present?
@datasets = @datasets.where(
'curator.id': Integer(@filters[:curator], exception: false) ? @filters[:curator] : nil
- ) if @filters[:curator].present?
- @datasets = @datasets.where('stash_engine_journals.id': @filters.dig(:journal, :value)) if @filters.dig(:journal, :value).present?
- @datasets = @datasets.where('stash_engine_journals.sponsor_id': @filters[:sponsor]) if @filters[:sponsor].present?
+ ) if @filters[:curator].present? && current_user.min_curator?
@datasets = @datasets.where("MATCH(stash_engine_identifiers.search_words) AGAINST('#{@search_string}') > 0") unless @search_string.blank?
- @datasets = @datasets.where('dcs_contributors.name_identifier_id': @filters.dig(:funder, :value)) if @filters.dig(:funder, :value).present?
- @datasets = @datasets.where('dcs_affiliations.ror_id': @filters.dig(:affiliation, :value)) if @filters.dig(:affiliation, :value).present?
+
+ date_filters
end
def date_filters
@@ -129,16 +151,48 @@ def date_filters
# rubocop:enable Style/MultilineIfModifier
def tenant_filter
- return unless @filters[:member].present? && StashEngine::Tenant.find_by(id: @filters[:member]).present?
+ return unless @role_object.is_a?(StashEngine::Tenant) || @filters[:member].present?
+
+ tenant_limit = @tenant_limit
+ tenant_orgs = @role_object.ror_ids if @role_object.is_a?(StashEngine::Tenant)
+
+ if @filters[:member].present? && tenant_limit.find_by(id: @filters[:member])
+ tenant_limit = tenant_limit.where(id: @filters[:member])
+ tenant_orgs = StashEngine::Tenant.find(@filters[:member]).ror_ids
+ end
- tenant_orgs = StashEngine::Tenant.find(@filters[:member]).ror_ids
@datasets = @datasets.where(
- 'stash_engine_resources.tenant_id = ? or stash_engine_identifiers.payment_id = ?
+ 'stash_engine_resources.tenant_id in (?) or stash_engine_identifiers.payment_id in (?)
or dcs_affiliations.ror_id in (?) or dcs_contributors.name_identifier_id in (?)',
- @filters[:member], @filters[:member], tenant_orgs, tenant_orgs
+ tenant_limit.map(&:id), tenant_limit.map(&:id), tenant_orgs, tenant_orgs
)
end
+ def journal_filter
+ return unless @journal_limit.present? || @filters.dig(:journal, :value).present?
+
+ journal_ids = @filters.dig(:journal, :value)
+ journal_ids = (@journal_limit.map(&:id).include?(journal_ids) ? journal_ids : @journal_limit.map(&:id)) if @journal_limit.present?
+
+ @datasets = @datasets.where('stash_engine_journals.id': journal_ids) if journal_ids.present?
+ end
+
+ def sponsor_filter
+ return unless @sponsor_limit.present? || @filters[:sponsor].present?
+
+ sponsor_ids = @filters[:sponsor]
+ sponsor_ids = (@sponsor_limit.map(&:id).include?(sponsor_ids) ? sponsor_ids : @sponsor_limit.map(&:id)) if @sponsor_limit.present?
+
+ @datasets = @datasets.where('stash_engine_journals.sponsor_id': sponsor_ids) if sponsor_ids.present?
+ end
+
+ def funder_filter
+ return unless @role_object.is_a?(StashEngine::Funder) || @filters.dig(:funder, :value).present?
+
+ funder_ror = @role_object.ror_id || @filters.dig(:funder, :value)
+ @datasets = @datasets.where('dcs_contributors.name_identifier_id': funder_ror)
+ end
+
def date_string(date_hash)
from = date_hash[:start_date]
to = date_hash[:end_date]
diff --git a/app/controllers/stash_engine/user_admin_controller.rb b/app/controllers/stash_engine/user_admin_controller.rb
index 77e3d5611e..dd1b3d6af9 100644
--- a/app/controllers/stash_engine/user_admin_controller.rb
+++ b/app/controllers/stash_engine/user_admin_controller.rb
@@ -141,7 +141,7 @@ def save_role(role, existing, object = nil)
if role.blank?
existing.delete if existing
elsif existing
- existing.update(role: role)
+ existing.update(role: role, role_object: object)
else
StashEngine::Role.create(user: @user, role: role, role_object: object)
end
diff --git a/app/models/stash_engine/user.rb b/app/models/stash_engine/user.rb
index fb404a61ba..e2990af5dc 100644
--- a/app/models/stash_engine/user.rb
+++ b/app/models/stash_engine/user.rb
@@ -29,7 +29,7 @@ module StashEngine
class User < ApplicationRecord
self.table_name = 'stash_engine_users'
has_many :resources
- has_many :roles, dependent: :destroy
+ has_many :roles, -> { order(role_object_type: :asc) }, dependent: :destroy
has_many :journals, through: :roles, source: :role_object, source_type: 'StashEngine::Journal'
has_many :journal_organizations, through: :roles, source: :role_object, source_type: 'StashEngine::JournalOrganization'
has_many :funders, through: :roles, source: :role_object, source_type: 'StashEngine::Funder'
@@ -119,7 +119,7 @@ def min_curator?
end
def journals_as_admin
- admin_org_journals = journal_organizations.map(&:journals_sponsored).flatten
+ admin_org_journals = journal_organizations.map(&:journals_sponsored_deep).flatten
(journals + admin_org_journals).uniq
end
diff --git a/app/policies/stash_engine/resource_policy.rb b/app/policies/stash_engine/resource_policy.rb
index d486471ecc..d3f3ca2436 100644
--- a/app/policies/stash_engine/resource_policy.rb
+++ b/app/policies/stash_engine/resource_policy.rb
@@ -1,6 +1,10 @@
module StashEngine
class ResourcePolicy < ApplicationPolicy
+ def index?
+ @user.min_admin?
+ end
+
def create?
@user.present?
end
diff --git a/app/views/stash_engine/admin_dashboard/_filters.html.erb b/app/views/stash_engine/admin_dashboard/_filters.html.erb
index 0de62686b5..dac03a5ea9 100644
--- a/app/views/stash_engine/admin_dashboard/_filters.html.erb
+++ b/app/views/stash_engine/admin_dashboard/_filters.html.erb
@@ -12,22 +12,39 @@ end
Search filters
-
- <%= selecter('member', [['', '']] + institution_select).html_safe %>
+
+ <%= selecter('member', [['', '']] + @tenant_limit.map { |t| [t.short_name, t.id] }).html_safe %>
<%= selecter('status', [['', '']] + status_select).html_safe %>
- <% if current_user.min_curator? %><%= selecter('curator', [['', ''], ['Unassigned', 'unassigned']] + editor_select).html_safe %><% end %>
-
- <%= render partial: 'stash_engine/shared/search_select', locals: {
- id: 'journal',
- label: 'Journal',
- field_name: 'filters[journal]',
- options_path: '/stash_datacite/publications/autocomplete?term=',
- options_label: 'title',
- options_value: 'id',
- selected: @filters.dig(:journal)
- } %>
-
- <%= selecter('sponsor', [['', '']] + sponsor_select).html_safe %>
+ <% if current_user.min_curator? %>
+ <%= selecter('curator', [['', ''], ['Unassigned', 'unassigned']] + editor_select).html_safe %>
+ <% end %>
+ <% if @journal_limit.present? %>
+ <% if @journal_limit.length > 1 %>
+
+ Journal:
+ <%= select_tag("filters[journal][value]", options_for_select([['', '']] + @journal_limit.map { |j| [j.title, j.id] }, @filters.dig(:journal, :value)), id: "filter-journal", class: 'c-input__select') %>
+
+ <% end %>
+ <% else %>
+
+ <%= render partial: 'stash_engine/shared/search_select', locals: {
+ id: 'journal',
+ label: 'Journal',
+ field_name: 'filters[journal]',
+ options_path: '/stash_datacite/publications/autocomplete?term=',
+ options_label: 'title',
+ options_value: 'id',
+ selected: @filters.dig(:journal)
+ } %>
+
+ <% end %>
+ <% unless @journal_limit.present? && @sponsor_limit.empty? %>
+ <% if @journal_limit.length > 1 %>
+ <%= selecter('sponsor', [['', '']] + @sponsor_limit.map { |item| [item.name, item.id] }).html_safe %>
+ <% else %>
+ <%= selecter('sponsor', [['', '']] + sponsor_select).html_safe %>
+ <% end %>
+ <% end %>
<%= check('authors').html_safe %>
- Authors list provided for the dataset.
+ The first 6 listed authors of the dataset.
<%= check('affiliations').html_safe %>
-
Author affiliations matched with a ROR ID .
+
Up to 6 author affiliations matched with a ROR ID .
<%= check('countries').html_safe %>
- Countries associated with author affiliations.
+ Up to 6 countries associated with author affiliations.
+
+
+ <%= check('submitter').html_safe %>
+
The submitting user of the dataset linked to their ORCID .
<%= check('status').html_safe %>
diff --git a/app/views/stash_engine/admin_dashboard/_table_header.html.erb b/app/views/stash_engine/admin_dashboard/_table_header.html.erb
index 82fa050fa2..3f98ab8078 100644
--- a/app/views/stash_engine/admin_dashboard/_table_header.html.erb
+++ b/app/views/stash_engine/admin_dashboard/_table_header.html.erb
@@ -11,6 +11,9 @@
<% if @fields.include?('affiliations') || @fields.include?('countries') %>
<%= @fields.include?('affiliations') ? 'Affiliations' : 'Countries' %>
<% end %>
+ <% if @fields.include?('submitter') %>
+
Submitter
+ <% end %>
<% if @fields.include?('status') %>
<%= sortable_column_head sort_field: 'status', title: 'Status' %>
diff --git a/app/views/stash_engine/admin_dashboard/_table_row.html.erb b/app/views/stash_engine/admin_dashboard/_table_row.html.erb
index 77c104e0e9..928ea66d79 100644
--- a/app/views/stash_engine/admin_dashboard/_table_row.html.erb
+++ b/app/views/stash_engine/admin_dashboard/_table_row.html.erb
@@ -22,7 +22,14 @@
arr << a
end
arr }.map { |aff| ([@fields.include?('affiliations') ? aff.smart_name : nil] + [@fields.include?('countries') ? aff.country_name : nil]).reject(&:blank?).join(', ') }%>
- <%= ([dataset.tenant_id == 'dryad' ? nil : ([@fields.include?('affiliations') ? dataset.tenant&.short_name : nil] + [@fields.include?('countries') ? dataset.tenant&.country_name : nil]).reject(&:blank?).join(', ')].flatten).concat(affs).uniq.reject(&:blank?).join('; ') %>
+ <%= ([dataset.tenant_id == 'dryad' ? nil : ([@fields.include?('affiliations') ? dataset.tenant&.short_name : nil] + [@fields.include?('countries') ? dataset.tenant&.country_name : nil]).reject(&:blank?).join(', ')].flatten).concat(affs).uniq.reject(&:blank?).first(6).join('; ') %>
+ <% end %>
+ <% if @fields.include?('submitter') %>
+
+ <% if dataset.user.orcid %><% end %>
+ <%= "#{dataset.user.first_name} #{dataset.user.last_name}" %>
+ <% if dataset.user.orcid %> <% end %>
+
<% end %>
<% if @fields.include?('status') %>
<%= StashEngine::CurationActivity.readable_status(dataset.status) %>
diff --git a/app/views/stash_engine/admin_dashboard/index.csv.erb b/app/views/stash_engine/admin_dashboard/index.csv.erb
index 364bb56028..e319e4ea5e 100644
--- a/app/views/stash_engine/admin_dashboard/index.csv.erb
+++ b/app/views/stash_engine/admin_dashboard/index.csv.erb
@@ -6,6 +6,7 @@ head += ',Authors' if @fields.include?('authors')
if @fields.include?('affiliations') || @fields.include?('countries')
head += @fields.include?('affiliations') ? ',Affiliations' : ',Countries'
end
+head += ',Submitter' if @fields.include?('submitter')
head += ',Status' if @fields.include?('status')
head += ',Size' if @fields.include?('size')
head += ',Metrics' if @fields.include?('metrics')
@@ -32,7 +33,10 @@ head += ',Published' if @fields.include?('publication_date')
arr << a
end
arr }.map { |aff| ([@fields.include?('affiliations') ? aff.smart_name : nil] + [@fields.include?('countries')? aff.country_name : nil]).reject(&:blank?).join(', ') }
- row << ([dataset.tenant_id == 'dryad' ? nil : ([@fields.include?('affiliations') ? dataset.tenant&.short_name : nil] + [@fields.include?('countries') ? dataset.tenant&.country_name : nil]).reject(&:blank?).join(', ')].flatten).concat(affs).uniq.reject(&:blank?).join('; ')
+ row << ([dataset.tenant_id == 'dryad' ? nil : ([@fields.include?('affiliations') ? dataset.tenant&.short_name : nil] + [@fields.include?('countries') ? dataset.tenant&.country_name : nil]).reject(&:blank?).join(', ')].flatten).concat(affs).uniq.reject(&:blank?).first(6).join('; ')
+ end
+ if @fields.include?('submitter')
+ row << "#{dataset.user.first_name} #{dataset.user.last_name}#{dataset.user.orcid ? " ORCID: #{dataset.user.orcid}" : ''}"
end
row << StashEngine::CurationActivity.readable_status(dataset.status) if @fields.include?('status')
row << filesize(dataset.total_file_size) if @fields.include?('size')
diff --git a/app/views/stash_engine/user_account/index.html.erb b/app/views/stash_engine/user_account/index.html.erb
index 65319e99ce..e46b24b855 100644
--- a/app/views/stash_engine/user_account/index.html.erb
+++ b/app/views/stash_engine/user_account/index.html.erb
@@ -9,7 +9,7 @@
<%= form_with(url: edit_account_path, method: :post, id: 'user_edit_form', local: false) do |f| %>
diff --git a/app/views/stash_engine/user_admin/user_profile.html.erb b/app/views/stash_engine/user_admin/user_profile.html.erb
index 7f7efc5bf9..c3a586f872 100644
--- a/app/views/stash_engine/user_admin/user_profile.html.erb
+++ b/app/views/stash_engine/user_admin/user_profile.html.erb
@@ -29,7 +29,7 @@
From 103c67bd3e4db9788a58eee3feda17a7aec9339d Mon Sep 17 00:00:00 2001
From: Audrey Hamelers
Date: Sun, 16 Jun 2024 18:47:07 +0200
Subject: [PATCH 6/8] saved searches
---
.../stylesheets/scss/_admin-layout.scss | 40 ++++++---
app/assets/stylesheets/scss/_footer.scss | 7 +-
app/assets/stylesheets/scss/_list.scss | 58 +++++++++++++
app/assets/stylesheets/scss/_table.scss | 10 ---
.../admin_dashboard_controller.rb | 63 ++++++++++----
.../stash_engine/saved_searches_controller.rb | 48 +++++++++++
app/models/stash_engine/admin_search.rb | 49 +++++++++++
app/models/stash_engine/saved_search.rb | 34 ++++++++
app/models/stash_engine/user.rb | 2 +-
.../stash_engine/admin_search_policy.rb | 13 +++
.../stash_engine/saved_search_policy.rb | 25 ++++++
.../admin_dashboard/_fields.html.erb | 2 +-
.../admin_dashboard/_filters.html.erb | 2 +-
.../admin_dashboard/_new_search.html.erb | 38 +++++++++
.../admin_dashboard/index.html.erb | 82 ++++++++++++------
.../admin_dashboard/new_search.js.erb | 11 +++
.../admin_dashboard/save_search.js.erb | 15 ++++
.../user_account/_change_tenant.html.erb | 48 +++++++++++
.../user_account/_edit_search.html.erb | 14 ++++
.../user_account/_saved_search.html.erb | 11 +++
.../user_account/_user_profile.html.erb | 27 ++++++
.../stash_engine/user_account/index.html.erb | 84 ++++---------------
.../user_account/search_form.js.erb | 7 ++
config/routes.rb | 13 ++-
.../20240607114655_add_saved_searches.rb | 16 ++++
25 files changed, 579 insertions(+), 140 deletions(-)
create mode 100644 app/controllers/stash_engine/saved_searches_controller.rb
create mode 100644 app/models/stash_engine/admin_search.rb
create mode 100644 app/models/stash_engine/saved_search.rb
create mode 100644 app/policies/stash_engine/admin_search_policy.rb
create mode 100644 app/policies/stash_engine/saved_search_policy.rb
create mode 100644 app/views/stash_engine/admin_dashboard/_new_search.html.erb
create mode 100644 app/views/stash_engine/admin_dashboard/new_search.js.erb
create mode 100644 app/views/stash_engine/admin_dashboard/save_search.js.erb
create mode 100644 app/views/stash_engine/user_account/_change_tenant.html.erb
create mode 100644 app/views/stash_engine/user_account/_edit_search.html.erb
create mode 100644 app/views/stash_engine/user_account/_saved_search.html.erb
create mode 100644 app/views/stash_engine/user_account/_user_profile.html.erb
create mode 100644 app/views/stash_engine/user_account/search_form.js.erb
create mode 100644 db/migrate/20240607114655_add_saved_searches.rb
diff --git a/app/assets/stylesheets/scss/_admin-layout.scss b/app/assets/stylesheets/scss/_admin-layout.scss
index 981d184319..213d7d7edc 100644
--- a/app/assets/stylesheets/scss/_admin-layout.scss
+++ b/app/assets/stylesheets/scss/_admin-layout.scss
@@ -97,6 +97,16 @@
}
}
+button.c-admin-edit-icon {
+ border: none;
+ color: $design-medium-blue-color;
+ background-color: transparent;
+
+ &:hover {
+ color: $design-orange-color;
+ }
+}
+
.admin-dashboard-form {
margin-bottom: 2rem;
@@ -166,11 +176,6 @@
.c-input__select {
background-color: $design-white-color;
}
-
- #search-string {
- flex-basis: 500px;
- flex-grow: 2;
- }
}
.admin-dashboard-header {
@@ -180,6 +185,11 @@
column-gap: 1ch;
row-gap: .5ch;
flex-wrap: wrap;
+ margin-bottom: 1.1rem;
+
+ h1 {
+ margin-bottom: 0;
+ }
}
.admin-dashboard-buttons {
@@ -192,16 +202,22 @@
margin: .8rem auto .2rem;
*[role='heading'] {
- font-size: 1.15em;
+ &[aria-level='2'] {
+ font-size: 1.35em;
+ }
+ &[aria-level='3'] {
+ font-size: 1.1em;
+ }
+ }
+
+ button i {
+ margin-right: .5ch;
}
}
-.admin-dashboard-count {
- margin-bottom: -1rem;
- text-align: right;
- font-size: .9em;
+.admin-dashboard-results {
display: flex;
- align-items: center;
- justify-content: flex-end;
+ align-items: baseline;
+ justify-content: flex-start;
column-gap: 2ch;
}
diff --git a/app/assets/stylesheets/scss/_footer.scss b/app/assets/stylesheets/scss/_footer.scss
index e08816f98c..0e08ae9b47 100644
--- a/app/assets/stylesheets/scss/_footer.scss
+++ b/app/assets/stylesheets/scss/_footer.scss
@@ -1,9 +1,14 @@
// ##### Footer Component ##### //
+#maincontent {
+ min-height: 79vh;
+ @media (max-width: 1060px) { min-height: 75vh; }
+ @media (max-width: 900px) { min-height: 70vh; }
+}
+
.c-footer {
@extend %pull-borders;
margin-top: 2em;
- margin-bottom: $spacing-md;
padding-top: 10px;
clear: both;
border-top: thin solid $design-dark-gray-color;
diff --git a/app/assets/stylesheets/scss/_list.scss b/app/assets/stylesheets/scss/_list.scss
index a659c81ae4..31c8589725 100644
--- a/app/assets/stylesheets/scss/_list.scss
+++ b/app/assets/stylesheets/scss/_list.scss
@@ -26,3 +26,61 @@
}
}
+
+.saved_searches_list {
+ counter-reset: li;
+ padding-left: 0;
+ display: grid;
+ grid-template-columns: 16px 20px 1fr 3fr 40px 40px;
+
+ &.with_form {
+ grid-template-columns: 16px 60px 2fr 3fr 40px 40px;
+ }
+
+ li:before {
+ counter-increment: li;
+ content: counter(li);
+ padding: 5px 0 5px 3px;
+ }
+
+ li {
+ display: grid;
+ grid-template-columns: subgrid;
+ grid-column-start: 1;
+ grid-column-end: 7;
+
+ &:nth-child(odd) {
+ background-color: #f6f6f6;
+ }
+
+ & > * {
+ padding: 5px;
+ }
+
+ & > *:nth-child(1):not(.saved_search_form) {
+ grid-column: 2; padding: 5px 0; text-align: center;
+ }
+ & > *:nth-child(2) { grid-column: 3}
+ & > *:nth-child(3) { grid-column: 4}
+ & > *:nth-child(4) { grid-column: 5}
+ & > *:nth-child(5) { grid-column: 6}
+ }
+
+ .saved_search_form {
+ margin-top: -2.5rem;
+ display: grid;
+ grid-template-columns: subgrid;
+ grid-column-start: 1;
+ grid-column-end: 7;
+
+ & > * {
+ padding: 5px 10px;
+ }
+
+ & > *:nth-child(3) { grid-column: 2; padding: 5px; text-align: center;}
+ & > *:nth-child(4) { grid-column: 3}
+ & > *:nth-child(5) { grid-column: 4}
+ & > *:nth-child(6) { grid-column: 5}
+ & > *:nth-child(7) { grid-column: 6}
+ }
+}
\ No newline at end of file
diff --git a/app/assets/stylesheets/scss/_table.scss b/app/assets/stylesheets/scss/_table.scss
index e2fcbfd508..02d52304de 100644
--- a/app/assets/stylesheets/scss/_table.scss
+++ b/app/assets/stylesheets/scss/_table.scss
@@ -107,16 +107,6 @@ td, th {
& > *:first-child {
margin-bottom: $spacing-base;
}
-
- button {
- border: none;
- color: $design-medium-blue-color;
- background-color: transparent;
-
- &:hover {
- color: $design-orange-color;
- }
- }
}
.c-user-datasets-table {
diff --git a/app/controllers/stash_engine/admin_dashboard_controller.rb b/app/controllers/stash_engine/admin_dashboard_controller.rb
index 7bf58d14a8..64a5218ad2 100644
--- a/app/controllers/stash_engine/admin_dashboard_controller.rb
+++ b/app/controllers/stash_engine/admin_dashboard_controller.rb
@@ -1,10 +1,11 @@
module StashEngine
class AdminDashboardController < ApplicationController
helper SortableTableHelper
- before_action :require_user_login
+ before_action :require_admin
before_action :setup_paging, only: :index
- before_action :setup_limits, only: :index
- before_action :setup_search, only: :index
+ before_action :setup_limits, only: %i[index new_search save_search]
+ before_action :setup_search, only: %i[index new_search save_search]
+ before_action :collect_properties, only: %i[new_search save_search]
# before_action :load, only: %i[popup note_popup edit]
# rubocop:disable Metrics/MethodLength
@@ -63,6 +64,18 @@ def index
end
# rubocop:enable Metrics/MethodLength
+ def new_search
+ respond_to(&:js)
+ end
+
+ def save_search
+ existing = authorize StashEngine::AdminSearch.find_by(id: params[:id])
+ return unless existing
+
+ existing.update(properties: @properties)
+ respond_to(&:js)
+ end
+
private
def setup_paging
@@ -80,21 +93,6 @@ def setup_paging
end
# rubocop:disable Style/MultilineIfModifier
- def setup_search
- @search_string = params[:q] || ''
- @filters = params[:filters] || session[:admin_search_filters] || {}
- session[:admin_search_filters] = params[:filters] if params[:filters].present?
- @fields = params[:fields] || session[:admin_search_fields]
- session[:admin_search_fields] = params[:fields] if params[:fields].present?
- return unless @fields.blank?
-
- @fields = %w[doi authors status metrics submit_date publication_date]
- @fields << 'journal' if @sponsor_limit
- @fields << 'affiliations' if @role_object.is_a?(StashEngine::Tenant)
- @fields << 'awards' if @role_object.is_a?(StashEngine::Funder)
- @fields << 'curator' if current_user.min_curator?
- end
-
def setup_limits
session[:admin_search_role] = params[:user_role] if params[:user_role].present?
user_role = current_user.roles.find_by(id: session[:admin_search_role]) || current_user.roles.first
@@ -110,6 +108,35 @@ def setup_limits
@journal_limit = journal_limit || []
end
+ # rubocop:disable Metrics/AbcSize
+ def setup_search
+ @saved_search = current_user.admin_searches[params[:search].to_i - 1] if params[:search]
+ @saved_search ||= current_user.admin_searches.find_by(default: true)
+
+ @search_string = params[:q] || @saved_search&.search_string || session[:admin_search_string]
+ @filters = params[:filters] || @saved_search&.filters || session[:admin_search_filters]
+ @fields = params[:fields] || @saved_search&.fields || session[:admin_search_fields]
+
+ session[:admin_search_filters] = params[:filters] if params[:filters].present?
+ session[:admin_search_fields] = params[:fields] if params[:fields].present?
+ session[:admin_search_string] = params[:q] if params[:q].present?
+
+ return unless @fields.blank?
+
+ @search_string = ''
+ @filters = {}
+ @fields = %w[doi authors status metrics submit_date publication_date]
+ @fields << 'journal' if @sponsor_limit
+ @fields << 'affiliations' if @role_object.is_a?(StashEngine::Tenant)
+ @fields << 'awards' if @role_object.is_a?(StashEngine::Funder)
+ @fields << 'curator' if current_user.min_curator?
+ end
+ # rubocop:enable Metrics/AbcSize
+
+ def collect_properties
+ @properties = { fields: @fields, filters: @filters, search_string: @search_string }.to_json
+ end
+
def add_fields
@datasets = @datasets.preload(:user) if @fields.include?('submitter')
@datasets = @datasets.preload(:funders) if @fields.include?('funders')
diff --git a/app/controllers/stash_engine/saved_searches_controller.rb b/app/controllers/stash_engine/saved_searches_controller.rb
new file mode 100644
index 0000000000..757bec7f72
--- /dev/null
+++ b/app/controllers/stash_engine/saved_searches_controller.rb
@@ -0,0 +1,48 @@
+module StashEngine
+ class SavedSearchesController < ApplicationController
+ def index?; end
+
+ def create
+ @saved_search = StashEngine::SavedSearch.create(create_params)
+ @index = current_user.admin_searches.length
+ return unless params[:type] == 'StashEngine::AdminSearch'
+
+ render template: 'stash_engine/admin_dashboard/save_search'
+ end
+
+ def edit
+ @saved_search = StashEngine::SavedSearch.find_by(id: params[:id])
+ @index = current_user.admin_searches.map(&:id).index(@saved_search.id)
+ return unless @saved_search&.type == 'StashEngine::AdminSearch'
+
+ render template: 'stash_engine/user_account/search_form'
+ end
+
+ def update
+ @saved_search = StashEngine::SavedSearch.find_by(id: params[:id])
+ return unless @saved_search&.type == 'StashEngine::AdminSearch'
+
+ @saved_search.update(update_params)
+ render template: 'stash_engine/user_account/edit'
+ end
+
+ def destroy
+ existing = StashEngine::SavedSearch.find_by(id: params[:id])
+ return unless existing&.type == 'StashEngine::AdminSearch'
+
+ existing.destroy!
+ render template: 'stash_engine/user_account/edit'
+ end
+
+ private
+
+ def create_params
+ params.permit(:type, :user_id, :default, :title, :description, :properties)
+ end
+
+ def update_params
+ params.permit(:default, :title, :description)
+ end
+
+ end
+end
diff --git a/app/models/stash_engine/admin_search.rb b/app/models/stash_engine/admin_search.rb
new file mode 100644
index 0000000000..f34b48394c
--- /dev/null
+++ b/app/models/stash_engine/admin_search.rb
@@ -0,0 +1,49 @@
+# == Schema Information
+#
+# Table name: stash_engine_saved_searches
+#
+# id :bigint not null, primary key
+# default :boolean
+# description :string(191)
+# properties :json
+# share_code :string(191)
+# title :string(191)
+# type :string(191)
+# created_at :datetime not null
+# updated_at :datetime not null
+# user_id :integer
+#
+# Indexes
+#
+# index_stash_engine_saved_searches_on_user_id_and_type (user_id,type)
+#
+# Foreign Keys
+#
+# fk_rails_... (user_id => stash_engine_users.id)
+#
+module StashEngine
+ class AdminSearch < SavedSearch
+ before_create :only_one_default
+ before_update :only_one_default
+
+ def fields
+ properties['fields']
+ end
+
+ def filters
+ properties['filters']
+ end
+
+ def search_string
+ properties['search_string']
+ end
+
+ private
+
+ def only_one_default
+ return unless default?
+
+ StashEngine::User.find(user_id).admin_searches.update_all(default: false)
+ end
+ end
+end
diff --git a/app/models/stash_engine/saved_search.rb b/app/models/stash_engine/saved_search.rb
new file mode 100644
index 0000000000..e546276b10
--- /dev/null
+++ b/app/models/stash_engine/saved_search.rb
@@ -0,0 +1,34 @@
+# == Schema Information
+#
+# Table name: stash_engine_saved_searches
+#
+# id :bigint not null, primary key
+# default :boolean
+# description :string(191)
+# properties :json
+# share_code :string(191)
+# title :string(191)
+# type :string(191)
+# created_at :datetime not null
+# updated_at :datetime not null
+# user_id :integer
+#
+# Indexes
+#
+# index_stash_engine_saved_searches_on_user_id_and_type (user_id,type)
+#
+# Foreign Keys
+#
+# fk_rails_... (user_id => stash_engine_users.id)
+#
+module StashEngine
+ class SavedSearch < ApplicationRecord
+ self.table_name = 'stash_engine_saved_searches'
+ belongs_to :user, class_name: 'StashEngine::User'
+
+ def properties
+ JSON.parse(super)
+ end
+
+ end
+end
diff --git a/app/models/stash_engine/user.rb b/app/models/stash_engine/user.rb
index 686afba9f3..bb4ed6d3a3 100644
--- a/app/models/stash_engine/user.rb
+++ b/app/models/stash_engine/user.rb
@@ -36,7 +36,7 @@ class User < ApplicationRecord
has_many :tenants, through: :roles, source: :role_object, source_type: 'StashEngine::Tenant'
belongs_to :affiliation, class_name: 'StashDatacite::Affiliation', optional: true
belongs_to :tenant, class_name: 'StashEngine::Tenant', optional: true
-
+ has_many :admin_searches, class_name: 'StashEngine::AdminSearch', dependent: :destroy
has_many :access_grants,
class_name: 'Doorkeeper::AccessGrant',
foreign_key: :resource_owner_id,
diff --git a/app/policies/stash_engine/admin_search_policy.rb b/app/policies/stash_engine/admin_search_policy.rb
new file mode 100644
index 0000000000..1f80466a19
--- /dev/null
+++ b/app/policies/stash_engine/admin_search_policy.rb
@@ -0,0 +1,13 @@
+module StashEngine
+ class AdminSearchPolicy < ApplicationPolicy
+
+ def new_search?
+ @user.min_admin?
+ end
+
+ def save_search?
+ @user.id == @resource.user_id
+ end
+
+ end
+end
diff --git a/app/policies/stash_engine/saved_search_policy.rb b/app/policies/stash_engine/saved_search_policy.rb
new file mode 100644
index 0000000000..2e99ceeb8c
--- /dev/null
+++ b/app/policies/stash_engine/saved_search_policy.rb
@@ -0,0 +1,25 @@
+module StashEngine
+ class SavedSearchPolicy < ApplicationPolicy
+
+ def index?
+ @user.id == @resource.user_id
+ end
+
+ def create?
+ @user.present?
+ end
+
+ def edit?
+ index?
+ end
+
+ def update
+ index?
+ end
+
+ def destroy
+ index?
+ end
+
+ end
+end
diff --git a/app/views/stash_engine/admin_dashboard/_fields.html.erb b/app/views/stash_engine/admin_dashboard/_fields.html.erb
index 3fec8de91c..f1fe5dcd37 100644
--- a/app/views/stash_engine/admin_dashboard/_fields.html.erb
+++ b/app/views/stash_engine/admin_dashboard/_fields.html.erb
@@ -11,7 +11,7 @@ def check(id)
end
%>
- Display fields
+ Display fields
Display field help
diff --git a/app/views/stash_engine/admin_dashboard/_filters.html.erb b/app/views/stash_engine/admin_dashboard/_filters.html.erb
index dac03a5ea9..0a0737367c 100644
--- a/app/views/stash_engine/admin_dashboard/_filters.html.erb
+++ b/app/views/stash_engine/admin_dashboard/_filters.html.erb
@@ -10,7 +10,7 @@ def selecter(id, options)
end
%>
- Search filters
+ Search filters
<%= selecter('member', [['', '']] + @tenant_limit.map { |t| [t.short_name, t.id] }).html_safe %>
diff --git a/app/views/stash_engine/admin_dashboard/_new_search.html.erb b/app/views/stash_engine/admin_dashboard/_new_search.html.erb
new file mode 100644
index 0000000000..735d21888a
--- /dev/null
+++ b/app/views/stash_engine/admin_dashboard/_new_search.html.erb
@@ -0,0 +1,38 @@
+<%
+# id :bigint not null, primary key
+# default :boolean
+# description :string(191)
+# properties :json
+# share_code :string(191)
+# title :string(191)
+# type :string(191)
+# created_at :datetime not null
+# updated_at :datetime not null
+# user_id :integer
+%>
+Save this search
+<% if current_user.admin_searches.count == 10 %>
+ You have reached the limit on saved searches! Please modify or delete one of your existing saved searches .
+<% else %>
+ <%= form_with(model: StashEngine::AdminSearch, url: stash_url_helpers.saved_search_path, method: :post, local: false ) do %>
+
+
+
+
+ Name
+ <%= text_field_tag(:title, '', class: 'c-input__text', maxlength: '50', required: true) %>
+
+
+ Description
+ <%= text_area_tag(:description, '', class: 'c-input__textarea', style: 'width: 100%') %>
+
+
+ <%= check_box_tag(:default, true)%>
+ <%= label_tag(:default, 'Save as your default search') %>
+
+
+ <%= submit_tag 'Submit', class: 'o-button__plain-text2', id: 'popup_submit' %>
+ <%= button_tag 'Cancel', type: 'button', id: 'cancel_dialog', class: 'o-button__plain-text7' %>
+
+ <% end %>
+<% end %>
\ No newline at end of file
diff --git a/app/views/stash_engine/admin_dashboard/index.html.erb b/app/views/stash_engine/admin_dashboard/index.html.erb
index 7c258a57e5..351e2a49d5 100644
--- a/app/views/stash_engine/admin_dashboard/index.html.erb
+++ b/app/views/stash_engine/admin_dashboard/index.html.erb
@@ -1,34 +1,51 @@
<% @page_title = "Admin dashboard" %>
-
+<% keepopen = params[:filters].present? || params[:fields].present? || params[:search_string].present? %>
+<% unaltered = @saved_search&.filters == @filters && @saved_search&.fields == @fields && (@saved_search&.search_string == @search_string || (@saved_search&.search_string.blank? && @search_string.blank?)) %>
-<%= form_with(url: stash_url_helpers.admin_dashboard_path, method: 'post', id: 'search_form', class: 'admin-dashboard-form') do %>
- <%= render partial: 'fields' %>
- <%= render partial: 'filters' %>
-