diff --git a/app/assets/javascripts/projects/index.js b/app/assets/javascripts/projects/index.js index c1afc5ec5..2317ce6c3 100644 --- a/app/assets/javascripts/projects/index.js +++ b/app/assets/javascripts/projects/index.js @@ -561,17 +561,16 @@ var ProjectsIndex = (function() { function initProjectsViewModeSwitch() { let projectsPageSelector = '.projects-index'; - // list/cards switch - $(projectsPageSelector).on('click', '.cards-switch', function() { - let $btn = $(this); - $('.cards-switch').removeClass('active'); - if ($btn.hasClass('view-switch-cards')) { - $(cardsWrapper).removeClass('list'); - } else if ($btn.hasClass('view-switch-list')) { - $(cardsWrapper).addClass('list'); - } - $btn.addClass('active'); - }); + $(projectsPageSelector) + .on('ajax:success', '.change-projects-view-type-form', function(ev, data) { + $(cardsWrapper).removeClass('list').addClass(data.cards_view_type_class); + $(projectsPageSelector).find('.cards-switch .button-to').removeClass('selected'); + $(ev.target).find('.button-to').addClass('selected'); + $(ev.target).parents('.dropdown.view-switch').removeClass('open'); + }) + .on('ajax:error', '.change-projects-view-type-form', function(ev, data) { + HelperModule.flashAlertMsg(data.responseJSON.flash, 'danger'); + }); // Active/Archived switch // We have different sorting, filters for active/archived views. diff --git a/app/assets/javascripts/projects/show.js b/app/assets/javascripts/projects/show.js index d9737ab57..aaa41398b 100644 --- a/app/assets/javascripts/projects/show.js +++ b/app/assets/javascripts/projects/show.js @@ -51,16 +51,16 @@ } function initProjectsViewModeSwitch() { - $(experimentsPage).on('click', '.cards-switch', function() { - let $btn = $(this); - $('.cards-switch').removeClass('active'); - if ($btn.hasClass('view-switch-cards')) { - $(cardsWrapper).removeClass('list'); - } else if ($btn.hasClass('view-switch-list')) { - $(cardsWrapper).addClass('list'); - } - $btn.addClass('active'); - }); + $(experimentsPage) + .on('ajax:success', '.change-experiments-view-type-form', function(ev, data) { + $(cardsWrapper).removeClass('list').addClass(data.cards_view_type_class); + $(experimentsPage).find('.cards-switch .button-to').removeClass('selected'); + $(ev.target).find('.button-to').addClass('selected'); + $(ev.target).parents('.dropdown.view-switch').removeClass('open'); + }) + .on('ajax:error', '.change-projects-view-type-form', function(ev, data) { + HelperModule.flashAlertMsg(data.responseJSON.flash, 'danger'); + }); $(experimentsPage).on('click', '.archive-switch', function() { Turbolinks.visit($(this).data('url')); diff --git a/app/assets/stylesheets/shared_styles/elements/dropdown.scss b/app/assets/stylesheets/shared_styles/elements/dropdown.scss index e6ca29478..c76aa898a 100644 --- a/app/assets/stylesheets/shared_styles/elements/dropdown.scss +++ b/app/assets/stylesheets/shared_styles/elements/dropdown.scss @@ -41,15 +41,40 @@ .dropdown-menu { .form-dropdown-item { + padding: 0 !important; + button { border-radius: 0; color: $color-black !important; padding-left: .9em; text-align: left; width: 100%; + } - &:hover { - background-color: $color-dd-hover !important; + .project-archive-restore-form { + .button-to { + &:hover { + background-color: $color-dd-hover !important; + } + } + } + + .change-projects-view-type-form, + .change-experiments-view-type-form { + .button-to { + float: unset !important; + height: 48px; + margin: 0; + + &:active { + background-color: inherit; + } + + &.selected::after { + @include font-awesome; + content: $font-fas-check; + float: right; + } } } } diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index bb5d14f51..6637d02cb 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -5,20 +5,21 @@ class ProjectsController < ApplicationController include TeamsHelper include InputSanitizeHelper include ProjectsHelper + include CardsViewHelper include ExperimentsHelper attr_reader :current_folder helper_method :current_folder before_action :switch_team_with_param, only: :index - before_action :load_vars, only: %i(show edit update notifications sidebar experiments_cards) + before_action :load_vars, only: %i(show edit update notifications sidebar experiments_cards view_type) before_action :load_current_folder, only: %i(index cards new show) - before_action :check_view_permissions, only: %i(show notifications sidebar experiments_cards) + before_action :check_view_permissions, only: %i(show notifications sidebar experiments_cards view_type) before_action :check_create_permissions, only: %i(new create) before_action :check_manage_permissions, only: :edit before_action :set_inline_name_editing, only: %i(show) - before_action :load_exp_sort_var, only: %i(show) - before_action :reset_invalid_view_state, only: %i(index cards) + before_action :load_exp_sort_var, only: :show + before_action :reset_invalid_view_state, only: %i(index cards show) layout 'fluid' @@ -26,6 +27,7 @@ class ProjectsController < ApplicationController if current_team view_state = current_team.current_view_state(current_user) @current_sort = view_state.state.dig('projects', projects_view_mode, 'sort') || 'atoz' + @current_view_type = view_state.state.dig('projects', 'view_type') end end @@ -261,6 +263,7 @@ class ProjectsController < ApplicationController view_state = @project.current_view_state(current_user) @current_sort = view_state.state.dig('experiments', experiments_view_mode(@project), 'sort') || 'atoz' + @current_view_type = view_state.state.dig('experiments', 'view_type') end def experiments_cards @@ -298,12 +301,24 @@ class ProjectsController < ApplicationController render json: users, status: :ok end + def view_type + view_state = @project.current_view_state(current_user) + view_state.state['experiments']['view_type'] = view_type_params + view_state.save! + + render json: { cards_view_type_class: cards_view_type_class(view_type_params) }, status: :ok + end + private def project_params params.require(:project).permit(:name, :team_id, :visibility, :archived, :project_folder_id) end + def view_type_params + params.require(:project).require(:view_type) + end + def load_vars @project = Project.find_by(id: params[:id]) @@ -357,7 +372,12 @@ class ProjectsController < ApplicationController end def reset_invalid_view_state - view_state = current_team.current_view_state(current_user) + view_state = if action_name == 'show' + @project.current_view_state(current_user) + else + current_team.current_view_state(current_user) + end + view_state.destroy unless view_state.valid? end diff --git a/app/controllers/teams_controller.rb b/app/controllers/teams_controller.rb index 912498920..2df8ca91a 100644 --- a/app/controllers/teams_controller.rb +++ b/app/controllers/teams_controller.rb @@ -2,13 +2,13 @@ class TeamsController < ApplicationController include ProjectsHelper - + include CardsViewHelper attr_reader :current_folder helper_method :current_folder before_action :load_vars, only: %i(sidebar export_projects export_projects_modal) before_action :load_current_folder, only: :sidebar - before_action :check_read_permissions + before_action :check_read_permissions, except: :view_type before_action :check_export_projects_permissions, only: %i(export_projects_modal export_projects) def sidebar @@ -77,6 +77,14 @@ class TeamsController < ApplicationController redirect_to root_path end + def view_type + view_state = current_team.current_view_state(current_user) + view_state.state['projects']['view_type'] = view_type_params + view_state.save! + + render json: { cards_view_type_class: cards_view_type_class(view_type_params) }, status: :ok + end + private def load_vars @@ -88,6 +96,10 @@ class TeamsController < ApplicationController params.permit(:id, project_ids: [], project_folder_ids: []) end + def view_type_params + params.require(:projects).require(:view_type) + end + def check_read_permissions render_403 unless can_read_team?(@team) end diff --git a/app/helpers/cards_view_helper.rb b/app/helpers/cards_view_helper.rb new file mode 100644 index 000000000..5dde2f9d6 --- /dev/null +++ b/app/helpers/cards_view_helper.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +module CardsViewHelper + def cards_view_type_class(view_type) + view_type == 'table' ? 'list' : '' + end +end diff --git a/app/models/project.rb b/app/models/project.rb index 329416f1b..020063bb7 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -155,13 +155,15 @@ class Project < ApplicationRecord { experiments: { active: { sort: 'new' }, - archived: { sort: 'new' } + archived: { sort: 'new' }, + view_type: 'cards' } } end def validate_view_state(view_state) - if %w(new old atoz ztoa).exclude?(view_state.state.dig('experiments', 'active', 'sort')) || + if %w(cards table).exclude?(view_state.state.dig('experiments', 'view_type')) || + %w(new old atoz ztoa).exclude?(view_state.state.dig('experiments', 'active', 'sort')) || %w(new old atoz ztoa archived_new archived_old).exclude?(view_state.state.dig('experiments', 'archived', 'sort')) view_state.errors.add(:state, :wrong_state) end diff --git a/app/models/team.rb b/app/models/team.rb index 48a94f40b..caeaaaa67 100644 --- a/app/models/team.rb +++ b/app/models/team.rb @@ -48,14 +48,16 @@ class Team < ApplicationRecord { projects: { active: { sort: 'new' }, - archived: { sort: 'new' } + archived: { sort: 'new' }, + view_type: 'cards' } } end def validate_view_state(view_state) if %w(new old atoz ztoa).exclude?(view_state.state.dig('projects', 'active', 'sort')) || - %w(new old atoz ztoa archived_new archived_old).exclude?(view_state.state.dig('projects', 'archived', 'sort')) + %w(new old atoz ztoa archived_new archived_old).exclude?(view_state.state.dig('projects', 'archived', 'sort')) || + %w(cards table).exclude?(view_state.state.dig('projects', 'view_type')) view_state.errors.add(:state, :wrong_state) end end diff --git a/app/views/projects/index.html.erb b/app/views/projects/index.html.erb index 312eb8476..41344a0e8 100644 --- a/app/views/projects/index.html.erb +++ b/app/views/projects/index.html.erb @@ -17,7 +17,9 @@ <%= render partial: 'projects/index/modals/export_projects' %>