Merge pull request #1851 from okriuchykhin/ok_SCI_3436

Implement saving of attachments ordering on steps [SCI-3436]
This commit is contained in:
Alex Kriuchykhin 2019-06-20 11:31:15 +02:00 committed by GitHub
commit db7c6435b3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 94 additions and 21 deletions

View file

@ -667,13 +667,20 @@
} }
// Reorder attachments // Reorder attachments
global.reorderAttachments = function reorderAtt(stepId, sortType) { global.reorderAttachments = function reorderAtt(elem, stepId, sortType) {
var label_value = $("#dd-att-step-" + stepId + "> .dropdown-menu > li > a[data-order=" + sortType + "]").html(); var label_value = $("#dd-att-step-" + stepId + "> .dropdown-menu > li > a[data-order=" + sortType + "]").html();
$("#dd-att-step-" + stepId + "-label").html(label_value); $("#dd-att-step-" + stepId + "-label").html(label_value);
$('#att-' + stepId + ' a.file-preview-link').each(function(){ $('#att-' + stepId + ' a.file-preview-link').each(function(){
var elm = $(this) var elm = $(this)
elm.parent().css('order', elm.attr('data-order-' + sortType)); elm.parent().css('order', elm.attr('data-order-' + sortType));
}) });
$.post(
$(elem).closest('.dropdown-menu').data('stateSavePath'),
{ assets: { order: sortType } },
null,
'json',
);
} }
// On init // On init

View file

@ -3,12 +3,11 @@ class StepsController < ApplicationController
include ApplicationHelper include ApplicationHelper
include StepsActions include StepsActions
before_action :load_vars, only: %i(edit update destroy show toggle_step_state before_action :load_vars, only: %i(edit update destroy show toggle_step_state checklistitem_state update_view_state)
checklistitem_state)
before_action :load_vars_nested, only: [:new, :create] before_action :load_vars_nested, only: [:new, :create]
before_action :convert_table_contents_to_utf8, only: [:create, :update] before_action :convert_table_contents_to_utf8, only: [:create, :update]
before_action :check_view_permissions, only: [:show] before_action :check_view_permissions, only: %i(show update_view_state)
before_action :check_manage_permissions, only: %i(new create edit update before_action :check_manage_permissions, only: %i(new create edit update
destroy) destroy)
before_action :check_complete_and_checkbox_permissions, only: before_action :check_complete_and_checkbox_permissions, only:
@ -190,6 +189,17 @@ class StepsController < ApplicationController
end end
end end
def update_view_state
view_state = @step.current_view_state(current_user)
view_state.state['assets']['sort'] = params.require(:assets).require(:order)
view_state.save! if view_state.changed?
respond_to do |format|
format.json do
render json: {}, status: :ok
end
end
end
def destroy def destroy
if @step.can_destroy? if @step.can_destroy?
# Update position on other steps of this module # Update position on other steps of this module

View file

@ -8,11 +8,19 @@ module MyModulesHelper
end end
def ordered_assets(step) def ordered_assets(step)
step.assets.order(:file_updated_at) view_state = step.current_view_state(current_user)
sort = case view_state.state.dig('assets', 'sort')
when 'old' then { created_at: :asc }
when 'atoz' then { file_file_name: :asc }
when 'ztoa' then { file_file_name: :desc }
else { created_at: :desc }
end end
def az_ordered_assets_index(step, asset_id) step.assets.order(sort)
step.assets.order('LOWER(file_file_name)').pluck(:id).index(asset_id) end
def az_ordered_assets_index(assets, asset_id)
assets.sort_by(&:file_file_name).map(&:id).index(asset_id)
end end
def number_of_samples(my_module) def number_of_samples(my_module)

View file

@ -7,6 +7,18 @@ module ViewableModel
has_many :view_states, as: :viewable, dependent: :destroy has_many :view_states, as: :viewable, dependent: :destroy
end end
# This module requres that the class which includes it implements these methods:
# => default_view_state, returning hash with default state representation
# => validate_view_state(view_state), custom validator for the state hash
def default_view_state
raise NotImplementedError, 'default_view_state should be implemented!'
end
def validate_view_state(_view_state)
raise NotImplementedError, 'validate_view_state(view_state) should be implemented!'
end
def current_view_state(user) def current_view_state(user)
state = view_states.where(user: user).take state = view_states.where(user: user).take
state || view_states.create!(user: user, state: default_view_state) state || view_states.create!(user: user, state: default_view_state)

View file

@ -2,6 +2,7 @@ class Step < ApplicationRecord
include SearchableModel include SearchableModel
include SearchableByNameModel include SearchableByNameModel
include TinyMceImages include TinyMceImages
include ViewableModel
auto_strip_attributes :name, :description, nullify: false auto_strip_attributes :name, :description, nullify: false
validates :name, validates :name,
@ -65,6 +66,16 @@ class Step < ApplicationRecord
end end
end end
def default_view_state
{ 'assets' => { 'sort' => 'new' } }
end
def validate_view_state(view_state)
unless %w(new old atoz ztoa).include?(view_state.state.dig('assets', 'sort'))
view_state.errors.add(:state, :wrong_state)
end
end
def destroy(current_user) def destroy(current_user)
@current_user = current_user @current_user = current_user

View file

@ -57,6 +57,13 @@ class Team < ApplicationRecord
'filter' => 'active' } } 'filter' => 'active' } }
end end
def validate_view_state(view_state)
unless %w(new old atoz ztoa).include?(view_state.state.dig('projects', 'cards', 'sort')) &&
%w(active archived).include?(view_state.state.dig('projects', 'filter'))
view_state.errors.add(:state, :wrong_state)
end
end
def search_users(query = nil) def search_users(query = nil)
a_query = "%#{query}%" a_query = "%#{query}%"
users.where.not(confirmed_at: nil) users.where.not(confirmed_at: nil)

View file

@ -8,4 +8,13 @@ class ViewState < ApplicationRecord
scope: %i(viewable_type user_id), scope: %i(viewable_type user_id),
message: :not_unique message: :not_unique
} }
validate :validate_state_content
private
def validate_state_content
return unless state.present?
viewable.validate_view_state(self)
end
end end

View file

@ -15,8 +15,8 @@
status: asset_status, status: asset_status,
'present-url': present_url, 'present-url': present_url,
'preview-url': asset_file_preview_path(asset), 'preview-url': asset_file_preview_path(asset),
'order-atoz': az_ordered_assets_index(step, asset.id), 'order-atoz': order_atoz,
'order-ztoa': assets_count - az_ordered_assets_index(step, asset.id), 'order-ztoa': order_ztoa,
'order-old': i, 'order-old': i,
'order-new': assets_count - i, 'order-new': assets_count - i,
} do %> } do %>

View file

@ -1,4 +1,4 @@
<% assets = ordered_assets step %> <% assets = ordered_assets(step) %>
<div class="col-xs-12"> <div class="col-xs-12">
<hr> <hr>
</div> </div>
@ -6,7 +6,7 @@
<div class="title"> <div class="title">
<h4> <h4>
<%= t('protocols.steps.files', count: assets.count) %> <%= t('protocols.steps.files', count: assets.length) %>
</h4> </h4>
</div> </div>
<div> <div>
@ -18,14 +18,17 @@
<% end %> <% end %>
<div class="dropdown attachments-order" id="dd-att-step-<%= step.id %>"> <div class="dropdown attachments-order" id="dd-att-step-<%= step.id %>">
<button class="btn btn-default dropdown-toggle" type="button" id="sortMenu" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true"> <button class="btn btn-default dropdown-toggle" type="button" id="sortMenu" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
<span id="dd-att-step-<%= step.id %>-label"><%= t('protocols.steps.attachments.sort_new').html_safe %></span> <span id="dd-att-step-<%= step.id %>-label">
<%= t("protocols.steps.attachments.sort.#{step.current_view_state(current_user).state.dig('assets', 'sort')}_html") %>
</span>
<span class="caret"></span> <span class="caret"></span>
</button> </button>
<ul class="dropdown-menu" aria-labelledby="sortMenu"> <ul class="dropdown-menu" aria-labelledby="sortMenu" data-state-save-path="<%= update_view_state_step_path(step.id) %>">
<% ['new', 'old', 'atoz', 'ztoa'].each do |sort| %> <% ['new', 'old', 'atoz', 'ztoa'].each do |sort| %>
<li> <li>
<a data-order="<%= sort %>" onClick="reorderAttachments('<%= step.id %>', '<%= sort %>')"> <a data-order="<%= sort %>" onClick="reorderAttachments(this, '<%= step.id %>', '<%= sort %>')">
<%= t('protocols.steps.attachments.sort_' + sort ).html_safe %></a> <%= t("protocols.steps.attachments.sort.#{sort}_html") %>
</a>
</li> </li>
<% end %> <% end %>
</ul> </ul>
@ -36,8 +39,10 @@
<div class="col-xs-12 attachments" id="att-<%= step.id %>"> <div class="col-xs-12 attachments" id="att-<%= step.id %>">
<% assets.each_with_index do |asset, i| %> <% assets.each_with_index do |asset, i| %>
<% order_atoz = az_ordered_assets_index(assets, asset.id) %>
<% order_ztoa = assets.length - az_ordered_assets_index(assets, asset.id) %>
<%= render partial: 'steps/attachments/item.html.erb', <%= render partial: 'steps/attachments/item.html.erb',
locals: { asset: asset, i: i, assets_count: assets.count, step: step } %> locals: { asset: asset, i: i, assets_count: assets.length, step: step, order_atoz: order_atoz, order_ztoa: order_ztoa } %>
<% end %> <% end %>
</div> </div>
<hr> <hr>

View file

@ -75,6 +75,8 @@ en:
attributes: attributes:
viewable_id: viewable_id:
not_unique: "State already exists for this user and parent object" not_unique: "State already exists for this user and parent object"
state:
wrong_state: "Wrong parameters"
helpers: helpers:
label: label:
@ -1761,10 +1763,11 @@ en:
complete_title: "Complete Step" complete_title: "Complete Step"
uncomplete_title: "Uncomplete Step" uncomplete_title: "Uncomplete Step"
attachments: attachments:
sort_new: "Newest first &#8595;" sort:
sort_old: "Oldest first &#8593;" new_html: "Newest first &#8595;"
sort_atoz: "Name &#8595;" old_html: "Oldest first &#8593;"
sort_ztoa: "Name &#8593;" atoz_html: "Name &#8595;"
ztoa_html: "Name &#8593;"
modified_label: "Modified:" modified_label: "Modified:"
new: new:
add_step_title: "Add new step" add_step_title: "Add new step"

View file

@ -435,6 +435,7 @@ Rails.application.routes.draw do
post 'toggle_step_state' post 'toggle_step_state'
get 'move_down' get 'move_down'
get 'move_up' get 'move_up'
post 'update_view_state'
end end
end end