diff --git a/app/components/samples/table_component.html.erb b/app/components/samples/table_component.html.erb index 554a966377..57ef0bb553 100644 --- a/app/components/samples/table_component.html.erb +++ b/app/components/samples/table_component.html.erb @@ -33,27 +33,15 @@ "w-4 h-4 mr-2.5 text-primary-600 bg-slate-100 border-slate-300 rounded focus:ring-primary-500 dark:focus:ring-primary-600 dark:ring-offset-slate-800 focus:ring-2 dark:bg-slate-700 dark:border-slate-600" %> <% end %> - <% if column == :attachments_updated_at %> - <%= render Ransack::SortComponent.new( - ransack_obj: @q, - label: t(".#{column}"), - url: helpers.sorting_url(@q, :attachments_updated_at_nulls_last), - field: :attachments_updated_at_nulls_last, - data: { - turbo_action: "replace", - }, - ) %> - <% else %> - <%= render Ransack::SortComponent.new( - ransack_obj: @q, - label: t(".#{column}"), - url: helpers.sorting_url(@q, column), - field: column, - data: { - turbo_action: "replace", - }, - ) %> - <% end %> + <%= render SortComponent.new( + sort: @search_params[:sort], + label: t(".#{column}"), + url: sort_url(column), + field: column, + data: { + turbo_action: "replace", + }, + ) %> <% end %> <% end %> <% @metadata_fields.each do |field| %> @@ -62,11 +50,10 @@ scope: 'col', classes: class_names('px-3 py-3') ) do %> - <%= render Ransack::SortComponent.new( - ransack_obj: @q, + <%= render SortComponent.new( + sort: @search_params[:sort], label: field, - url: - helpers.sorting_url(@q, URI.encode_www_form_component("metadata_#{field}")), + url: sort_url("metadata_#{field}"), field: "metadata_#{field}", data: { turbo_action: "replace", @@ -127,7 +114,7 @@ ) %> <% end %> - <% elsif column == :project %> + <% elsif column == :project_id %> <%= link_to sample.project.puid, project_samples_path(sample.project), data: { diff --git a/app/components/samples/table_component.rb b/app/components/samples/table_component.rb index 70d0160c39..ce81d1a6b8 100644 --- a/app/components/samples/table_component.rb +++ b/app/components/samples/table_component.rb @@ -7,12 +7,11 @@ module Samples class TableComponent < Component include Ransack::Helpers::FormHelper - # rubocop:disable Naming/MethodParameterName,Metrics/ParameterLists + # rubocop:disable Metrics/ParameterLists def initialize( samples, namespace, pagy, - q, has_samples: true, abilities: {}, metadata_fields: [], @@ -24,7 +23,6 @@ def initialize( @samples = samples @namespace = namespace @pagy = pagy - @q = q @has_samples = has_samples @abilities = abilities @metadata_fields = metadata_fields @@ -34,9 +32,11 @@ def initialize( @renders_row_actions = @row_actions.select { |_key, value| value }.count.positive? @system_arguments = system_arguments + @sort_key, @sort_direction = search_params[:sort].split + @columns = columns end - # rubocop:enable Naming/MethodParameterName,Metrics/ParameterLists + # rubocop:enable Metrics/ParameterLists def system_arguments { tag: 'div' }.deep_merge(@system_arguments).tap do |args| @@ -77,11 +77,25 @@ def select_samples_url(**) end end + def sort_url(field) + sort_string = if field.to_s == @sort_key && @sort_direction == 'asc' + "#{field} desc" + else + "#{field} asc" + end + + if @namespace.type == 'Group' + group_samples_url(@namespace, q: { sort: sort_string }) + else + namespace_project_samples_url(@namespace.parent, @namespace.project, q: { sort: sort_string }) + end + end + private def columns columns = %i[puid name] - columns << :project if @namespace.type == 'Group' + columns << :project_id if @namespace.type == 'Group' columns += %i[created_at updated_at attachments_updated_at] columns end diff --git a/app/components/sort_component.html.erb b/app/components/sort_component.html.erb new file mode 100644 index 0000000000..cf2fcdf610 --- /dev/null +++ b/app/components/sort_component.html.erb @@ -0,0 +1,4 @@ +<%= render Viral::BaseComponent.new(tag: 'a', href: url, type: "button", classes: class_names('inline-flex items-center'), **system_arguments) do %> + <%= label %> + <%= viral_icon(name: icon, classes: "w-4 h-4 inline-block") %> +<% end %> diff --git a/app/components/sort_component.rb b/app/components/sort_component.rb new file mode 100644 index 0000000000..b1f9a918c8 --- /dev/null +++ b/app/components/sort_component.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +# Component for sort in table headers +class SortComponent < Component + attr_reader :label, :field, :url, :system_arguments + + def initialize(sort:, label:, url:, field:, **system_arguments) + @sort_key, @sort_direction = sort.split + @label = label + @url = url + @field = field + @system_arguments = system_arguments + end + + def icon + return unless @sort_key.to_s == URI.encode_www_form_component(@field.to_s) + + @sort_direction == 'asc' ? 'arrow_up' : 'arrow_down' + end +end diff --git a/app/controllers/concerns/storable.rb b/app/controllers/concerns/storable.rb index 4b7bf38704..36a49bb79f 100644 --- a/app/controllers/concerns/storable.rb +++ b/app/controllers/concerns/storable.rb @@ -18,6 +18,7 @@ def store(session_key, value) # @param value [Hash] the value to merge with the existing value in the session. def update_store(search_key, value) session[search_key] = (session[search_key] || {}).merge(value) + session[search_key].reject! { |_, val| val.blank? || val == [''] } get_store(search_key) end diff --git a/app/controllers/groups/samples_controller.rb b/app/controllers/groups/samples_controller.rb index 6dd9a3bc62..27c8b4a9f5 100644 --- a/app/controllers/groups/samples_controller.rb +++ b/app/controllers/groups/samples_controller.rb @@ -7,12 +7,11 @@ class SamplesController < Groups::ApplicationController include Storable before_action :group, :current_page - before_action :process_samples, only: %i[index search select] - include Sortable + before_action :query, only: %i[index search select] def index @timestamp = DateTime.current - @pagy, @samples = pagy_with_metadata_sort(@q.result) + @pagy, @samples = pagy(@query.results) @has_samples = authorized_samples.count.positive? end @@ -27,7 +26,7 @@ def select respond_to do |format| format.turbo_stream do if params[:select].present? - @selected_sample_ids = @q.result.where(updated_at: ..params[:timestamp].to_datetime).select(:id).pluck(:id) + @selected_sample_ids = @query.results.where(updated_at: ..params[:timestamp].to_datetime).select(:id).pluck(:id) end end end @@ -39,6 +38,10 @@ def group @group = Group.find_by_full_path(params[:group_id]) # rubocop:disable Rails/DynamicFindBy end + def authorized_projects + authorized_scope(Project, type: :relation, as: :group_projects, scope_options: { group: @group }) + end + def authorized_samples authorized_scope(Sample, type: :relation, as: :namespace_samples, scope_options: { namespace: @group }).includes(project: { namespace: [{ parent: :route }, @@ -71,20 +74,21 @@ def set_metadata_fields fields_for_namespace(namespace: @group, show_fields: @search_params && @search_params[:metadata].to_i == 1) end - def process_samples + def query authorize! @group, to: :sample_listing? + @search_params = search_params set_metadata_fields - query_parser = Irida::SearchSyntax::Ransack.new(text: :name_or_puid_cont, metadata_fields: @fields) - @parsed_params = query_parser.parse(@search_params.fetch(:name_or_puid_cont, nil)) - @q = authorized_samples.ransack(@search_params.except(:name_or_puid_cont).merge(@parsed_params)) + + @query = Sample::Query.new(@search_params.except(:metadata).merge({ project_ids: authorized_projects.select(:id) })) end def search_params updated_params = update_store(search_key, params[:q].present? ? params[:q].to_unsafe_h : {}) - if updated_params[:metadata].to_i.zero? && updated_params[:s].present? && updated_params[:s].match?(/metadata_/) - updated_params[:s] = default_sort + if !updated_params.key?(:sort) || + (updated_params[:metadata].to_i.zero? && updated_params[:sort]&.match?(/metadata_/)) + updated_params[:sort] = 'updated_at desc' update_store(search_key, updated_params) end updated_params diff --git a/app/controllers/projects/samples_controller.rb b/app/controllers/projects/samples_controller.rb index 985b2e5fff..b0876e9819 100644 --- a/app/controllers/projects/samples_controller.rb +++ b/app/controllers/projects/samples_controller.rb @@ -9,12 +9,11 @@ class SamplesController < Projects::ApplicationController # rubocop:disable Metr before_action :sample, only: %i[show edit update view_history_version] before_action :current_page - before_action :process_samples, only: %i[index search select] - include Sortable + before_action :query, only: %i[index search select] def index @timestamp = DateTime.current - @pagy, @samples = pagy_with_metadata_sort(@q.result) + @pagy, @samples = pagy(@query.results) @has_samples = @project.samples.size.positive? end @@ -84,7 +83,7 @@ def select respond_to do |format| format.turbo_stream do if params[:select].present? - @samples = @q.result.where(updated_at: ..params[:timestamp].to_datetime).select(:id) + @samples = @query.results.where(updated_at: ..params[:timestamp].to_datetime).select(:id) end end end @@ -138,22 +137,22 @@ def search_key :"#{controller_name}_#{@project.id}_search_params" end - def process_samples + def query authorize! @project, to: :sample_listing? @search_params = search_params - set_metadata_fields - query_parser = Irida::SearchSyntax::Ransack.new(text: :name_or_puid_cont, metadata_fields: @fields) - @parsed_params = query_parser.parse(@search_params.fetch(:name_or_puid_cont, nil)) - @q = load_samples.ransack(@search_params.except(:name_or_puid_cont).merge(@parsed_params)) + + @query = Sample::Query.new(@search_params.except(:metadata).merge({ project_ids: [@project.id] })) end def search_params - updated_params = update_store(search_key, params[:q].present? ? params[:q].to_unsafe_h : {}) + updated_params = update_store(search_key, + params[:q].present? ? params[:q].to_unsafe_h : {}).with_indifferent_access - if updated_params[:metadata].to_i.zero? && updated_params[:s].present? && updated_params[:s].match?(/metadata_/) - updated_params[:s] = default_sort + if !updated_params.key?(:sort) || + (updated_params[:metadata].to_i.zero? && updated_params[:sort]&.match?(/metadata_/)) + updated_params[:sort] = 'updated_at desc' update_store(search_key, updated_params) end updated_params diff --git a/app/models/sample/query.rb b/app/models/sample/query.rb new file mode 100644 index 0000000000..fcd5a03468 --- /dev/null +++ b/app/models/sample/query.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +# model to represent sample search form +class Sample::Query # rubocop:disable Style/ClassAndModuleChildren + include ActiveModel::Model + include ActiveModel::Attributes + + attribute :column, :string + attribute :direction, :string + attribute :name_or_puid_cont, :string + attribute :name_or_puid_in, default: -> { [] } + attribute :project_ids, default: -> { [] } + attribute :sort, :string, default: 'updated_at desc' + + validates :direction, inclusion: { in: %w[asc desc] } + validates :project_ids, length: { minimum: 1 } + + def initialize(...) + super + self.sort = sort + end + + def sort=(value) + super + column, direction = sort.split + assign_attributes(column:, direction:) + end + + def results + return Sample.none unless valid? + + sort_samples.ransack(ransack_params).result + end + + private + + def ransack_params + { + name_or_puid_cont: name_or_puid_cont, + name_or_puid_in: name_or_puid_in + }.compact + end + + def sort_samples(scope = Sample.where(project_id: project_ids)) + if column.starts_with? 'metadata_' + field = column.gsub('metadata_', '') + scope.order(Sample.metadata_sort(field, dir)) + else + scope.order("#{column} #{direction}") + end + end +end diff --git a/app/views/groups/samples/_table.html.erb b/app/views/groups/samples/_table.html.erb index b4e51550cf..d97389a312 100644 --- a/app/views/groups/samples/_table.html.erb +++ b/app/views/groups/samples/_table.html.erb @@ -3,7 +3,6 @@ samples, group, pagy, - @q, has_samples: @has_samples, abilities: { select_samples: @@ -11,7 +10,7 @@ allowed_to?(:export_data?, group), }, metadata_fields: @fields, - search_params: @parsed_params, + search_params: @search_params, empty: { title: t(:"groups.samples.table.no_samples"), description: t(:"groups.samples.table.no_associated_samples"), diff --git a/app/views/groups/samples/index.html.erb b/app/views/groups/samples/index.html.erb index e4cecee418..31306d1a11 100644 --- a/app/views/groups/samples/index.html.erb +++ b/app/views/groups/samples/index.html.erb @@ -64,10 +64,10 @@
<% if allowed_to?(:submit_workflow?, @group) || allowed_to?(:export_data?, @group) %> - <%= search_form_for( - @q, - url: select_group_samples_url(**request.query_parameters), - html: { id: "select-all-form" }, + <%= form_with( + url: select_group_samples_url, + method: :get, + id: "select-all-form", class: "filters align-middle" ) do |f| %> @@ -81,10 +81,10 @@ <%= f.submit t(".select_all_button"), class: "button button--state-default button--size-default" %> <% end %> - <%= search_form_for( - @q, - url: select_group_samples_url(**request.query_parameters), - html: { id: "deselect-all-form" }, + <%= form_with( + url: select_group_samples_url, + method: :get, + id: "deselect-all-form", class: "filters align-middle" ) do |f| %> @@ -94,18 +94,26 @@ <% end %>
- <%= render SearchComponent.new( - query: @q, - url: search_group_samples_url, - value: @search_params[:name_or_puid_cont], - search_attribute: :name_or_puid_cont, - placeholder: t(".search.placeholder"), - html: { method: :post }, - ) do %> - - - <% end %> - <%= search_form_for @q, url: search_group_samples_url, html: { method: :post, "data-controller": "filters metadata-toggle", "data-turbo-action": "replace", "data-metadata-toggle-page-value": @pagy.page, "data-filters-selection-outlet": "#samples-table", class: "filters inline-flex items-center space-x-2" } do |f| %> + <%= form_with model: @query, scope: :q, url: search_group_samples_url, method: :post, data: { controller: "filters selection metadata-toggle", "metadata-toggle-page-value": @pagy.page, "filters-selection-outlet": "#samples-table" }, class: "filters inline-flex items-center space-x-2" do |f| %> + <%= f.label :name_or_puid_cont, t(".search.placeholder"), class: "sr-only" %> +
+ <%= f.search_field :name_or_puid_cont, + **{ value: @search_params[:name_or_puid_cont] }.compact, + "data-action": "selection#clear", + class: + "t-search-component block w-full p-2.5 pl-10 text-sm text-slate-900 border border-slate-300 rounded-lg bg-slate-50 focus:ring-primary-500 focus:border-primary-500 dark:bg-slate-700 dark:border-slate-600 dark:placeholder-slate-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500", + placeholder: t(".search.placeholder") %> +
+ <%= viral_icon( + name: "magnifying_glass", + classes: "w-4 h-4 text-slate-500 dark:text-slate-400", + ) %> +
+
@@ -114,7 +122,7 @@ filters: @search_params[:name_or_puid_in], ) %> <%= render partial: "projects/samples/shared/metadata_toggle", locals: { form: f } %> - + <%= f.submit hidden: true %> <% end %>
diff --git a/app/views/projects/samples/_table.html.erb b/app/views/projects/samples/_table.html.erb index a21ec3456a..1dc9438313 100644 --- a/app/views/projects/samples/_table.html.erb +++ b/app/views/projects/samples/_table.html.erb @@ -3,7 +3,6 @@ samples, project.namespace, pagy, - @q, has_samples: @has_samples, abilities: { select_samples: @@ -13,7 +12,7 @@ allowed_to?(:export_data?, project), }, metadata_fields: @fields, - search_params: @parsed_params, + search_params: @search_params, row_actions: { destroy: allowed_to?(:destroy_sample?, project), edit: allowed_to?(:update_sample?, project), diff --git a/app/views/projects/samples/index.html.erb b/app/views/projects/samples/index.html.erb index 043b954465..b1378dc701 100644 --- a/app/views/projects/samples/index.html.erb +++ b/app/views/projects/samples/index.html.erb @@ -116,10 +116,10 @@
<% if allowed_to?(:submit_workflow?, @project) || allowed_to?(:clone_sample?, @project) || allowed_to?(:transfer_sample?, @project) || allowed_to?(:export_data?, @project) %> - <%= search_form_for( - @q, - url: select_namespace_project_samples_url(**request.query_parameters), - html: { id: "select-all-form" }, + <%= form_with( + url: select_namespace_project_samples_url, + method: :get, + id: "select-all-form", ) do |f| %> @@ -132,10 +132,10 @@ <%= f.submit t(".select_all_button"), class: "button button--state-default button--size-default" %> <% end %> - <%= search_form_for( - @q, - url: select_namespace_project_samples_url(**request.query_parameters), - html: { id: "deselect-all-form" }, + <%= form_with( + url: select_namespace_project_samples_url, + method: :get, + id: "deselect-all-form" , ) do |f| %> <%= f.submit t(".deselect_all_button"), @@ -144,19 +144,25 @@ <% end %>
- <%= render SearchComponent.new( - query: @q, - url: search_namespace_project_samples_url, - value: @search_params[:name_or_puid_cont], - search_attribute: :name_or_puid_cont, - placeholder: t(".search.placeholder"), - html: { method: :post, "data-controller": "filters"}, - ) do %> - - - <% end %> - <%= search_form_for @q, url: search_namespace_project_samples_url, html: { method: :post, "data-controller": "filters metadata-toggle", "data-turbo-action": "replace", "data-metadata-toggle-page-value": @pagy.page, "data-filters-selection-outlet": "#samples-table" }, class: - "inline-flex items-center space-x-2 filters" do |f| %> + <%= form_with model: @query, scope: :q, url: search_namespace_project_samples_url, method: :post, data: { controller: "filters selection metadata-toggle", "metadata-toggle-page-value": @pagy.page, "filters-selection-outlet": "#samples-table" }, class: "filters inline-flex items-center space-x-2" do |f| %> + <%= f.label :name_or_puid_cont, t(".search.placeholder"), class: "sr-only" %> +
+ <%= f.search_field :name_or_puid_cont, + "data-action": "selection#clear", + class: + "t-search-component block w-full p-2.5 pl-10 text-sm text-slate-900 border border-slate-300 rounded-lg bg-slate-50 focus:ring-primary-500 focus:border-primary-500 dark:bg-slate-700 dark:border-slate-600 dark:placeholder-slate-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500", + placeholder: t(".search.placeholder") %> +
+ <%= viral_icon( + name: "magnifying_glass", + classes: "w-4 h-4 text-slate-500 dark:text-slate-400", + ) %> +
+
<%= render ListFilterComponent.new( @@ -164,7 +170,7 @@ filters: @search_params[:name_or_puid_in], ) %> <%= render partial: "projects/samples/shared/metadata_toggle", locals: { form: f } %> - + <%= f.submit hidden: true %> <% end %>
diff --git a/config/locales/en.yml b/config/locales/en.yml index 5f7a274e3e..140eee48b5 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -285,7 +285,7 @@ en: title: Delete File new_attachment_component: files: Files - files_ignored: 'fasta and fastq files must be compressed. The following file(s) cannot be uploaded:' + files_ignored: "fasta and fastq files must be compressed. The following file(s) cannot be uploaded:" upload: Upload upload_files: Upload Files uploading: Uploading @@ -339,7 +339,7 @@ en: transferred_by: "%{type} transferred by %{user}" history_version: current_version: Current (Version %{version}) - keys_deleted: 'Keys deleted:' + keys_deleted: "Keys deleted:" previous_version: Previous (Version %{version}) list_filter: apply: Apply filter @@ -377,7 +377,7 @@ en: concerns: attachment_actions: destroy: - error: 'File %{filename} was not removed due to the following errors: %{errors}' + error: "File %{filename} was not removed due to the following errors: %{errors}" bot_actions: create: success: Bot account was successfully added to the group @@ -390,7 +390,7 @@ en: success: Successfully revoked personal access token %{pat_name} file_import_actions: create: - error: 'Metadata was not successfully imported for the following samples:' + error: "Metadata was not successfully imported for the following samples:" success: Sample metadata was successfully imported. membership_actions: create: @@ -482,14 +482,14 @@ en: status: Status title: Data Exports list_workflow_execution: - id: 'ID:' - name: 'Name:' - run_id: 'Run ID:' - workflow: 'Workflow:' + id: "ID:" + name: "Name:" + run_id: "Run ID:" + workflow: "Workflow:" new: after_submission_description_html: After submission, you will be redirected to the export page. While the export status is processing, the contents of the export are being created. Once the export status is ready, it will be available for download. You will have 3 business days to download the export before it's deleted. email_label: Receive an email notification when your export is ready to download? - name_label: 'Export Name (Optional):' + name_label: "Export Name (Optional):" sample_description: plural: This export will contain COUNT_PLACEHOLDER samples. singular: This export will contain 1 sample. @@ -556,7 +556,7 @@ en: errors: messages: not_saved: - one: '1 error prohibited this %{resource} from being saved:' + one: "1 error prohibited this %{resource} from being saved:" other: "%{count} errors prohibited this %{resource} from being saved:" general: default_sidebar: @@ -586,7 +586,7 @@ en: title: Group activity attachments: create: - failure: 'File %{filename} was not uploaded due to the following errors: %{errors}' + failure: "File %{filename} was not uploaded due to the following errors: %{errors}" success: File %{filename} was successfully uploaded. destroy: success: File %{filename} was successfully removed. @@ -684,18 +684,18 @@ en: transfer: confirm: points_html: - - To prevent accidental actions we ask you to confirm your intention. - - Please type %{group} to proceed or close this dialog to cancel. + - To prevent accidental actions we ask you to confirm your intention. + - Please type %{group} to proceed or close this dialog to cancel. warning_html: You are going to transfer %{group_name} to another namespace. Once you confirm, this action cannot be undone. descriptions: - - Transfer group to another parent group. + - Transfer group to another parent group. empty_state: No namespaces are associated with that name or ID new_namespace_id: Select a parent group no_available_namespaces: You do not have access to any destination namespaces to transfer this group points: - - Be careful. Changing a group's parent can have unintended side effects. - - You can only transfer groups where you have sufficient permissions. - - If the parent group's visibility is lower than the group's current visibility, visibility levels for subgroups and projects will be changed to match the new parent group's visibility. + - Be careful. Changing a group's parent can have unintended side effects. + - You can only transfer groups where you have sufficient permissions. + - If the parent group's visibility is lower than the group's current visibility, visibility levels for subgroups and projects will be changed to match the new parent group's visibility. select_group: Select Group submit: Transfer group title: Transfer group @@ -844,10 +844,10 @@ en: export_ready: download_before_html: Please download the export before %{date} as it will be deleted after this time. ready_for_download_html: Your export %{name} is ready for download. - view_exports: 'To view and download the export, click the button below:' + view_exports: "To view and download the export, click the button below:" email_template: automated_message: This email was sent from an automated system. Please do not reply to this email. - copy_paste: 'Or copy and paste this link into your browser:' + copy_paste: "Or copy and paste this link into your browser:" greeting: Hello, greeting_with_name: Hello %{name}, thanks: Thanks, @@ -855,11 +855,11 @@ en: access_granted_manager_email: body_html: Group '%{group_name}' has been granted access to %{namespace_type} '%{namespace_name}' subject: Group '%{group_name}' has been granted access to %{namespace_type} '%{namespace_name}' - view_details: 'To view all the members of %{namespace_type} ''%{namespace_name}'', click the button below:' + view_details: "To view all the members of %{namespace_type} '%{namespace_name}', click the button below:" access_granted_user_email: body_html: Group '%{group_name}' has been granted access to %{namespace_type} '%{namespace_name}' subject: Group '%{group_name}' has been granted access to %{namespace_type} '%{namespace_name}' - view_details: 'To view %{namespace_type} ''%{namespace_name}'', click the button below:' + view_details: "To view %{namespace_type} '%{namespace_name}', click the button below:" access_revoked_manager_email: body_html: "%{namespace_type} '%{namespace_name}' access has been revoked for Group '%{group_name}'" subject: "%{namespace_type} '%{namespace_name}' access has been revoked for Group '%{group_name}'" @@ -870,15 +870,15 @@ en: access_granted_manager_email: body_html: User '%{first_name} %{last_name}' (%{email}) has been granted access to %{type} '%{name}' subject: User '%{first_name} %{last_name}' (%{email}) has been granted access to %{type} '%{name}' - view_details: 'To view all the members of %{type} ''%{name}'', click the button below:' + view_details: "To view all the members of %{type} '%{name}', click the button below:" access_granted_user_email: body_html: You have been granted access to %{type} '%{name}' subject: You have been granted access to %{type} '%{name}' - view_details: 'To view %{type} ''%{name}'', click the button below:' + view_details: "To view %{type} '%{name}', click the button below:" access_revoked_manager_email: body_html: "%{type} '%{name}' access has been revoked for User '%{first_name} %{last_name}' (%{email})" subject: "%{type} '%{name}' access has been revoked for User '%{first_name} %{last_name}' (%{email})" - view_details: 'To view all the members of %{type} ''%{name}'', click the button below:' + view_details: "To view all the members of %{type} '%{name}', click the button below:" access_revoked_user_email: body_html: Your access to %{type} '%{name}' has been revoked subject: Your access to %{type} '%{name}' has been revoked @@ -895,7 +895,7 @@ en: error_user_email: body_html: Your pipeline %{id} did not complete successfully due to errors. subject: Pipeline Errored - %{id} - view_details: 'To view the pipeline summary, click the button below:' + view_details: "To view the pipeline summary, click the button below:" members: access_levels: level_0: No Access @@ -946,8 +946,8 @@ en: confirm: Are you sure you want to delete your account? description: Deleting your account has the following effects effects: - - All projects and groups you own will be deleted - - All projects and groups you are a member of will be left intact + - All projects and groups you own will be deleted + - All projects and groups you are a member of will be left intact passwords: update: forgot: Forgot your password? @@ -1015,7 +1015,7 @@ en: title: Project Activity attachments: create: - failure: 'File %{filename} was not uploaded due to the following errors: %{errors}' + failure: "File %{filename} was not uploaded due to the following errors: %{errors}" success: File %{filename} was successfully uploaded. destroy: success: File %{filename} was successfully removed. @@ -1043,7 +1043,7 @@ en: edit: error: Cannot edit automated pipeline %{workflow_name} edit_dialog: - title: 'Editing Automated Pipeline: %{workflow_name}' + title: "Editing Automated Pipeline: %{workflow_name}" index: add_new_automated_workflow_execution: New automated pipeline subtitle: Setup automated pipelines to launch when paired-end data is uploaded to the project @@ -1145,20 +1145,20 @@ en: transfer: confirm: points_html: - - To prevent accidental actions we ask you to confirm your intention. - - Please type %{project} to proceed or close this dialog to cancel + - To prevent accidental actions we ask you to confirm your intention. + - Please type %{project} to proceed or close this dialog to cancel warning_html: You are going to transfer %{project_name} to another namespace. Once you confirm, this action cannot be undone. descriptions: - - Transfer your project into another namespace. - - When you transfer your project to a group, you can easily manage multiple projects and users. - - 'Things to be aware of before transferring your project:' + - Transfer your project into another namespace. + - When you transfer your project to a group, you can easily manage multiple projects and users. + - "Things to be aware of before transferring your project:" empty_state: No namespaces are associated with that name or ID new_namespace_id: Select a new namespace no_available_namespaces: You do not have access to any destination namespaces to transfer this project points: - - Be careful! Changing a project's namespace can have unintended side effects. - - You can only transfer projects to groups where you have sufficient permissions. - - Project visibility level will change to match namespace rules when transferring to a group. + - Be careful! Changing a project's namespace can have unintended side effects. + - You can only transfer projects to groups where you have sufficient permissions. + - Project visibility level will change to match namespace rules when transferring to a group. select_namespace: Select Namespace submit: Transfer project title: Transfer project @@ -1269,11 +1269,11 @@ en: success: Files were successfully concatenated. modal: basename: Filename - description: 'The following files were selected to be concatenated:' + description: "The following files were selected to be concatenated:" submit_button: Concatenate title: Concatenate Files create: - failure: 'File %{filename} was not uploaded due to the following errors: %{errors}' + failure: "File %{filename} was not uploaded due to the following errors: %{errors}" success: File %{filename} was successfully uploaded. delete_attachment_modal: description: Are you sure that you want to delete this file from the sample? @@ -1281,20 +1281,20 @@ en: title: Delete File deletions: destroy: - error: 'File %{filename} was not removed due to the following errors: %{errors}' + error: "File %{filename} was not removed due to the following errors: %{errors}" partial_success: Some files were successfully deleted. success: Files were successfully deleted. modal: - description: 'The following files were selected for deletion:' + description: "The following files were selected for deletion:" submit_button: Delete title: Delete Files destroy: - error: 'File %{filename} was not removed due to the following errors: %{errors}' + error: "File %{filename} was not removed due to the following errors: %{errors}" success: File %{filename} was successfully removed. clones: create: - error: 'The following list of samples failed to copy:' - no_samples_cloned_error: 'Samples were not copied for the following reasons:' + error: "The following list of samples failed to copy:" + no_samples_cloned_error: "Samples were not copied for the following reasons:" success: Samples were successfully copied. dialog: description: @@ -1325,8 +1325,8 @@ en: title: Delete Sample new_multiple_deletions_dialog: description: - plural: 'The following COUNT_PLACEHOLDER samples have been selected for deletion:' - singular: 'The following sample has been selected for deletion:' + plural: "The following COUNT_PLACEHOLDER samples have been selected for deletion:" + singular: "The following sample has been selected for deletion:" zero: No samples have been selected for deletion loading: Loading... samples: Samples @@ -1369,7 +1369,7 @@ en: multi_success: Metadata keys '%{deleted_keys}' were successfully deleted. single_success: Metadata key '%{deleted_key}' was successfully deleted. modal: - description: 'Metadata fields selected for deletion:' + description: "Metadata fields selected for deletion:" key_header: Key submit_button: Delete title: Delete Metadata @@ -1408,7 +1408,7 @@ en: delete_metadata_button: Delete Metadata edit_button: Edit this sample files: Files - files_ignored: 'fasta and fastq files must be compressed. The following file(s) cannot be uploaded:' + files_ignored: "fasta and fastq files must be compressed. The following file(s) cannot be uploaded:" history: modal: created_by: Sample created by %{user} @@ -1456,8 +1456,8 @@ en: uploading: Uploading transfers: create: - error: 'The following list of samples failed to transfer:' - no_samples_transferred_error: 'Samples were not transferred for the following reasons:' + error: "The following list of samples failed to transfer:" + no_samples_transferred_error: "Samples were not transferred for the following reasons:" success: Samples were successfully transferred. dialog: description: @@ -1547,7 +1547,7 @@ en: limit: item: Samples name: Sample Name - project: Project + project_id: Project puid: Sample ID select_page: Select / Deselect visible samples updated_at: Last Updated @@ -1609,8 +1609,8 @@ en: empty_new_project_id: Destination project was not selected. empty_sample_ids: The sample ids are empty. same_project: The source and destination projects are the same. Please select a different destination project. - sample_exists: 'Sample %{sample_puid}: Conflicts with sample named ''%{sample_name}'' in the target project' - samples_not_found: 'Samples with the following sample ids could not be copied as they were not found in the source project: %{sample_ids}' + sample_exists: "Sample %{sample_puid}: Conflicts with sample named '%{sample_name}' in the target project" + samples_not_found: "Samples with the following sample ids could not be copied as they were not found in the source project: %{sample_ids}" metadata: empty_metadata: No metadata was received to update sample '%{sample_name}' fields: @@ -1640,8 +1640,8 @@ en: empty_sample_ids: The sample ids are empty. maintainer_transfer_not_allowed: A maintainer can only transfer samples to other projects within the same hierarchy with a common ancestor same_project: The samples already exist in the project. Please select a different project. - sample_exists: 'Sample %{sample_puid}: Conflicts with sample named ''%{sample_name}'' in the target project' - samples_not_found: 'Samples with the following sample ids could not be transferred as they were not found in the source project: %{sample_ids}' + sample_exists: "Sample %{sample_puid}: Conflicts with sample named '%{sample_name}' in the target project" + samples_not_found: "Samples with the following sample ids could not be transferred as they were not found in the source project: %{sample_ids}" shared: alert: list: @@ -1659,7 +1659,7 @@ en: ignore_empty_values: description: If selected, any metadata fields without an associated value will be ignored and those metadata keys will not be removed from the sample if present. However, if this not selected, any samples with the metadata key and empty value will be deleted. namespace: - description: 'The spreadsheet is required to have a column that contains a sample identifier. ' + description: "The spreadsheet is required to have a column that contains a sample identifier. " group: description_html: The identifier is case-sensitive and must contain the sample ID. project: @@ -1669,7 +1669,7 @@ en: submit_button: Import Metadata title: Upload Sample Metadata errors: - description: 'The sample metadata import completed with the following errors:' + description: "The sample metadata import completed with the following errors:" ok_button: OK success: description: The metadata was imported successfully! diff --git a/config/locales/fr.yml b/config/locales/fr.yml index a5f61cc76c..bfc5b7af31 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -285,7 +285,7 @@ fr: title: Delete File new_attachment_component: files: Files - files_ignored: 'fasta and fastq files must be compressed. The following file(s) cannot be uploaded:' + files_ignored: "fasta and fastq files must be compressed. The following file(s) cannot be uploaded:" upload: Upload upload_files: Upload Files uploading: Uploading @@ -339,7 +339,7 @@ fr: transferred_by: "%{type} transferred by %{user}" history_version: current_version: Current (Version %{version}) - keys_deleted: 'Keys deleted:' + keys_deleted: "Keys deleted:" previous_version: Previous (Version %{version}) list_filter: apply: Apply filter @@ -377,7 +377,7 @@ fr: concerns: attachment_actions: destroy: - error: 'File %{filename} was not removed due to the following errors: %{errors}' + error: "File %{filename} was not removed due to the following errors: %{errors}" bot_actions: create: success: Bot account was successfully added to the group @@ -390,7 +390,7 @@ fr: success: Successfully revoked personal access token %{pat_name} file_import_actions: create: - error: 'Metadata was not successfully imported for the following samples:' + error: "Metadata was not successfully imported for the following samples:" success: Sample metadata was successfully imported. membership_actions: create: @@ -482,14 +482,14 @@ fr: status: Status title: Data Exports list_workflow_execution: - id: 'ID:' - name: 'Name:' - run_id: 'Run ID:' - workflow: 'Workflow:' + id: "ID:" + name: "Name:" + run_id: "Run ID:" + workflow: "Workflow:" new: after_submission_description_html: After submission, you will be redirected to the export page. While the export status is processing, the contents of the export are being created. Once the export status is ready, it will be available for download. You will have 3 business days to download the export before it's deleted. email_label: Receive an email notification when your export is ready to download? - name_label: 'Export Name (Optional):' + name_label: "Export Name (Optional):" sample_description: plural: This export will contain COUNT_PLACEHOLDER samples. singular: This export will contain 1 sample. @@ -556,7 +556,7 @@ fr: errors: messages: not_saved: - one: '1 error prohibited this %{resource} from being saved:' + one: "1 error prohibited this %{resource} from being saved:" other: "%{count} errors prohibited this %{resource} from being saved:" general: default_sidebar: @@ -586,7 +586,7 @@ fr: title: Group activity attachments: create: - failure: 'File %{filename} was not uploaded due to the following errors: %{errors}' + failure: "File %{filename} was not uploaded due to the following errors: %{errors}" success: File %{filename} was successfully uploaded. destroy: success: File %{filename} was successfully removed. @@ -684,18 +684,18 @@ fr: transfer: confirm: points_html: - - To prevent accidental actions we ask you to confirm your intention. - - Please type %{group} to proceed or close this dialog to cancel. + - To prevent accidental actions we ask you to confirm your intention. + - Please type %{group} to proceed or close this dialog to cancel. warning_html: You are going to transfer %{group_name} to another namespace. Once you confirm, this action cannot be undone. descriptions: - - Transfer group to another parent group. + - Transfer group to another parent group. empty_state: No namespaces are associated with that name or ID new_namespace_id: Select a parent group no_available_namespaces: You do not have access to any destination namespaces to transfer this group points: - - Be careful. Changing a group's parent can have unintended side effects. - - You can only transfer groups where you have sufficient permissions. - - If the parent group's visibility is lower than the group's current visibility, visibility levels for subgroups and projects will be changed to match the new parent group's visibility. + - Be careful. Changing a group's parent can have unintended side effects. + - You can only transfer groups where you have sufficient permissions. + - If the parent group's visibility is lower than the group's current visibility, visibility levels for subgroups and projects will be changed to match the new parent group's visibility. select_group: Select Group submit: Transfer group title: Transfer group @@ -844,10 +844,10 @@ fr: export_ready: download_before_html: Please download the export before %{date} as it will be deleted after this time. ready_for_download_html: Your export %{name} is ready for download. - view_exports: 'To view and download the export, click the button below:' + view_exports: "To view and download the export, click the button below:" email_template: automated_message: This email was sent from an automated system. Please do not reply to this email. - copy_paste: 'Or copy and paste this link into your browser:' + copy_paste: "Or copy and paste this link into your browser:" greeting: Bonjour, greeting_with_name: Bonjour %{name}, thanks: Merci, @@ -855,11 +855,11 @@ fr: access_granted_manager_email: body_html: Group '%{group_name}' has been granted access to %{namespace_type} '%{namespace_name}' subject: Group '%{group_name}' has been granted access to %{namespace_type} '%{namespace_name}' - view_details: 'To view all the members of %{namespace_type} ''%{namespace_name}'', click the button below:' + view_details: "To view all the members of %{namespace_type} '%{namespace_name}', click the button below:" access_granted_user_email: body_html: Group '%{group_name}' has been granted access to %{namespace_type} '%{namespace_name}' subject: Group '%{group_name}' has been granted access to %{namespace_type} '%{namespace_name}' - view_details: 'To view %{namespace_type} ''%{namespace_name}'', click the button below:' + view_details: "To view %{namespace_type} '%{namespace_name}', click the button below:" access_revoked_manager_email: body_html: "%{namespace_type} '%{namespace_name}' access has been revoked for Group '%{group_name}'" subject: "%{namespace_type} '%{namespace_name}' access has been revoked for Group '%{group_name}'" @@ -870,15 +870,15 @@ fr: access_granted_manager_email: body_html: User '%{first_name} %{last_name}' (%{email}) has been granted access to %{type} '%{name}' subject: User '%{first_name} %{last_name}' (%{email}) has been granted access to %{type} '%{name}' - view_details: 'To view all the members of %{type} ''%{name}'', click the button below:' + view_details: "To view all the members of %{type} '%{name}', click the button below:" access_granted_user_email: body_html: You have been granted access to %{type} '%{name}' subject: You have been granted access to %{type} '%{name}' - view_details: 'To view %{type} ''%{name}'', click the button below:' + view_details: "To view %{type} '%{name}', click the button below:" access_revoked_manager_email: body_html: "%{type} '%{name}' access has been revoked for User '%{first_name} %{last_name}' (%{email})" subject: "%{type} '%{name}' access has been revoked for User '%{first_name} %{last_name}' (%{email})" - view_details: 'To view all the members of %{type} ''%{name}'', click the button below:' + view_details: "To view all the members of %{type} '%{name}', click the button below:" access_revoked_user_email: body_html: Your access to %{type} '%{name}' has been revoked subject: Your access to %{type} '%{name}' has been revoked @@ -895,7 +895,7 @@ fr: error_user_email: body_html: Your pipeline %{id} did not complete successfully due to errors. subject: Pipeline Errored - %{id} - view_details: 'To view the pipeline summary, click the button below:' + view_details: "To view the pipeline summary, click the button below:" members: access_levels: level_0: No Access @@ -946,8 +946,8 @@ fr: confirm: Are you sure you want to delete your account? description: Deleting your account has the following effects effects: - - All projects and groups you own will be deleted - - All projects and groups you are a member of will be left intact + - All projects and groups you own will be deleted + - All projects and groups you are a member of will be left intact passwords: update: forgot: Forgot your password? @@ -1015,7 +1015,7 @@ fr: title: Project Activity attachments: create: - failure: 'File %{filename} was not uploaded due to the following errors: %{errors}' + failure: "File %{filename} was not uploaded due to the following errors: %{errors}" success: File %{filename} was successfully uploaded. destroy: success: File %{filename} was successfully removed. @@ -1043,7 +1043,7 @@ fr: edit: error: Cannot edit automated pipeline %{workflow_name} edit_dialog: - title: 'Editing Automated Pipeline: %{workflow_name}' + title: "Editing Automated Pipeline: %{workflow_name}" index: add_new_automated_workflow_execution: New automated pipeline subtitle: Setup automated pipelines to launch when paired-end data is uploaded to the project @@ -1145,20 +1145,20 @@ fr: transfer: confirm: points_html: - - To prevent accidental actions we ask you to confirm your intention. - - Please type %{project} to proceed or close this dialog to cancel + - To prevent accidental actions we ask you to confirm your intention. + - Please type %{project} to proceed or close this dialog to cancel warning_html: You are going to transfer %{project_name} to another namespace. Once you confirm, this action cannot be undone. descriptions: - - Transfer your project into another namespace. - - When you transfer your project to a group, you can easily manage multiple projects and users. - - 'Things to be aware of before transferring your project:' + - Transfer your project into another namespace. + - When you transfer your project to a group, you can easily manage multiple projects and users. + - "Things to be aware of before transferring your project:" empty_state: No namespaces are associated with that name or ID new_namespace_id: Select a new namespace no_available_namespaces: You do not have access to any destination namespaces to transfer this project points: - - Be careful! Changing a project's namespace can have unintended side effects. - - You can only transfer projects to groups where you have sufficient permissions. - - Project visibility level will change to match namespace rules when transferring to a group. + - Be careful! Changing a project's namespace can have unintended side effects. + - You can only transfer projects to groups where you have sufficient permissions. + - Project visibility level will change to match namespace rules when transferring to a group. select_namespace: Select Namespace submit: Transfer project title: Transfer project @@ -1269,11 +1269,11 @@ fr: success: Files were successfully concatenated. modal: basename: Filename - description: 'The following files were selected to be concatenated:' + description: "The following files were selected to be concatenated:" submit_button: Concatenate title: Concatenate Files create: - failure: 'File %{filename} was not uploaded due to the following errors: %{errors}' + failure: "File %{filename} was not uploaded due to the following errors: %{errors}" success: File %{filename} was successfully uploaded. delete_attachment_modal: description: Are you sure that you want to delete this file from the sample? @@ -1281,20 +1281,20 @@ fr: title: Delete File deletions: destroy: - error: 'File %{filename} was not removed due to the following errors: %{errors}' + error: "File %{filename} was not removed due to the following errors: %{errors}" partial_success: Some files were successfully deleted. success: Files were successfully deleted. modal: - description: 'The following files were selected for deletion:' + description: "The following files were selected for deletion:" submit_button: Delete title: Delete Files destroy: - error: 'File %{filename} was not removed due to the following errors: %{errors}' + error: "File %{filename} was not removed due to the following errors: %{errors}" success: File %{filename} was successfully removed. clones: create: - error: 'The following list of samples failed to copy:' - no_samples_cloned_error: 'Samples were not copied for the following reasons:' + error: "The following list of samples failed to copy:" + no_samples_cloned_error: "Samples were not copied for the following reasons:" success: Samples were successfully copied. dialog: description: @@ -1325,8 +1325,8 @@ fr: title: Delete Sample new_multiple_deletions_dialog: description: - plural: 'The following COUNT_PLACEHOLDER samples have been selected for deletion:' - singular: 'The following sample has been selected for deletion:' + plural: "The following COUNT_PLACEHOLDER samples have been selected for deletion:" + singular: "The following sample has been selected for deletion:" zero: No samples have been selected for deletion loading: Loading... samples: Samples @@ -1369,7 +1369,7 @@ fr: multi_success: Metadata keys '%{deleted_keys}' were successfully deleted. single_success: Metadata key '%{deleted_key}' was successfully deleted. modal: - description: 'Metadata fields selected for deletion:' + description: "Metadata fields selected for deletion:" key_header: Key submit_button: Delete title: Delete Metadata @@ -1408,7 +1408,7 @@ fr: delete_metadata_button: Delete Metadata edit_button: Edit this sample files: Files - files_ignored: 'fasta and fastq files must be compressed. The following file(s) cannot be uploaded:' + files_ignored: "fasta and fastq files must be compressed. The following file(s) cannot be uploaded:" history: modal: created_by: Sample created by %{user} @@ -1456,8 +1456,8 @@ fr: uploading: Uploading transfers: create: - error: 'The following list of samples failed to transfer:' - no_samples_transferred_error: 'Samples were not transferred for the following reasons:' + error: "The following list of samples failed to transfer:" + no_samples_transferred_error: "Samples were not transferred for the following reasons:" success: Samples were successfully transferred. dialog: description: @@ -1547,7 +1547,7 @@ fr: limit: item: Samples name: Sample Name - project: Project + project_id: Project puid: Sample ID select_page: Select / Deselect visible samples updated_at: Last Updated @@ -1609,8 +1609,8 @@ fr: empty_new_project_id: Destination project was not selected. empty_sample_ids: The sample ids are empty. same_project: The source and destination projects are the same. Please select a different destination project. - sample_exists: 'Sample %{sample_puid}: Conflicts with sample named ''%{sample_name}'' in the target project' - samples_not_found: 'Samples with the following sample ids could not be copied as they were not found in the source project: %{sample_ids}' + sample_exists: "Sample %{sample_puid}: Conflicts with sample named '%{sample_name}' in the target project" + samples_not_found: "Samples with the following sample ids could not be copied as they were not found in the source project: %{sample_ids}" metadata: empty_metadata: No metadata was received to update sample '%{sample_name}' fields: @@ -1640,8 +1640,8 @@ fr: empty_sample_ids: The sample ids are empty. maintainer_transfer_not_allowed: A maintainer can only transfer samples to other projects within the same hierarchy with a common ancestor same_project: The samples already exist in the project. Please select a different project. - sample_exists: 'Sample %{sample_puid}: Conflicts with sample named ''%{sample_name}'' in the target project' - samples_not_found: 'Samples with the following sample ids could not be transferred as they were not found in the source project: %{sample_ids}' + sample_exists: "Sample %{sample_puid}: Conflicts with sample named '%{sample_name}' in the target project" + samples_not_found: "Samples with the following sample ids could not be transferred as they were not found in the source project: %{sample_ids}" shared: alert: list: @@ -1659,7 +1659,7 @@ fr: ignore_empty_values: description: If selected, any metadata fields without an associated value will be ignored and those metadata keys will not be removed from the sample if present. However, if this not selected, any samples with the metadata key and empty value will be deleted. namespace: - description: 'The spreadsheet is required to have a column that contains a sample identifier. ' + description: "The spreadsheet is required to have a column that contains a sample identifier. " group: description_html: The identifier is case-sensitive and must contain the sample ID. project: @@ -1669,7 +1669,7 @@ fr: submit_button: Import Metadata title: Upload Sample Metadata errors: - description: 'The sample metadata import completed with the following errors:' + description: "The sample metadata import completed with the following errors:" ok_button: OK success: description: The metadata was imported successfully!