From 52493fd83418d3c1aaed7ed4085b64e07020faaa Mon Sep 17 00:00:00 2001 From: Mojca Lorber Date: Mon, 2 Mar 2020 16:11:14 +0100 Subject: [PATCH] Add pagination for current tasks on dashboard --- .../javascripts/dashboard/current_tasks.js | 94 ++++++++++++------- .../javascripts/sitewide/infinite_scroll.js | 68 ++++++++++++++ .../dashboard/current_tasks_controller.rb | 39 ++++---- app/views/dashboards/_current_tasks.html.erb | 2 +- config/initializers/constants.rb | 6 +- 5 files changed, 148 insertions(+), 61 deletions(-) create mode 100644 app/assets/javascripts/sitewide/infinite_scroll.js diff --git a/app/assets/javascripts/dashboard/current_tasks.js b/app/assets/javascripts/dashboard/current_tasks.js index 1ad0380b9..1266b5390 100644 --- a/app/assets/javascripts/dashboard/current_tasks.js +++ b/app/assets/javascripts/dashboard/current_tasks.js @@ -1,4 +1,4 @@ -/* global dropdownSelector I18n animateSpinner PerfectSb */ +/* global dropdownSelector I18n animateSpinner PerfectSb InfiniteScroll */ /* eslint-disable no-param-reassign */ var DasboardCurrentTasksWidget = (function() { @@ -12,7 +12,58 @@ var DasboardCurrentTasksWidget = (function() { `; - function loadCurrentTasksList() { + function generateTasksListHtml(json, container) { + $.each(json.data, (i, task) => { + var currentTaskItem; + var stepsPercentage = task.steps_state.percentage + '%'; + var stateText; + var dueDate = (task.due_date !== null) ? '' + + I18n.t('dashboard.current_tasks.due_date', { date: task.due_date }) : ''; + var overdue = (task.overdue) ? 'overdue' : ''; + if (task.state === 'completed') { + stateText = I18n.t('dashboard.current_tasks.progress_bar.completed'); + } else { + stateText = I18n.t('dashboard.current_tasks.progress_bar.in_progress'); + if (task.overdue) { stateText = I18n.t('dashboard.current_tasks.progress_bar.overdue'); } + if (task.steps_state.all_steps !== 0) { + stateText += I18n.t('dashboard.current_tasks.progress_bar.completed_steps', + { steps: task.steps_state.completed_steps, total_steps: task.steps_state.all_steps }); + } + } + currentTaskItem = ` +
${task.project}/${task.experiment}
+
+
${task.name}
+
${dueDate}
+
+
+
${stateText}
+
+
+
`; + $(container).append(currentTaskItem); + }); + } + + function initInfiniteScroll() { + InfiniteScroll.init('.current-tasks-list', { + url: $('.current-tasks-list').data('tasksListUrl'), + customResponse: (json, container) => { + generateTasksListHtml(json, container); + }, + customParams: (params) => { + params.project_id = dropdownSelector.getValues(projectFilter); + params.experiment_id = dropdownSelector.getValues(experimentFilter); + params.sort = dropdownSelector.getValues(sortFilter); + params.view = dropdownSelector.getValues(viewFilter); + params.query = $('.current-tasks-widget .task-search-field').val(); + params.mode = $('.current-tasks-navbar .active').data('mode'); + return params; + } + }); + } + + function loadCurrentTasksList(newList) { var $currentTasksList = $('.current-tasks-list'); var params = { project_id: dropdownSelector.getValues(projectFilter), @@ -24,44 +75,16 @@ var DasboardCurrentTasksWidget = (function() { animateSpinner($currentTasksList, true); $.get($currentTasksList.data('tasksListUrl'), params, function(data) { // Toggle empty state - if (data.tasks_list.length === 0) { + if (data.data.length === 0) { $currentTasksList.append(emptyState); } else { $currentTasksList.find('.no-tasks').remove(); } // Clear the list $currentTasksList.find('.current-task-item').remove(); - $.each(data.tasks_list, (i, task) => { - var currentTaskItem; - var stepsPercentage = task.steps_state.percentage + '%'; - var stateText; - var dueDate = (task.due_date !== null) ? '' - + I18n.t('dashboard.current_tasks.due_date', { date: task.due_date }) : ''; - var overdue = (task.overdue) ? 'overdue' : ''; - if (task.state === 'completed') { - stateText = I18n.t('dashboard.current_tasks.progress_bar.completed'); - } else { - stateText = I18n.t('dashboard.current_tasks.progress_bar.in_progress'); - if (task.overdue) { stateText = I18n.t('dashboard.current_tasks.progress_bar.overdue'); } - if (task.steps_state.all_steps !== 0) { - stateText += I18n.t('dashboard.current_tasks.progress_bar.completed_steps', - { steps: task.steps_state.completed_steps, total_steps: task.steps_state.all_steps }); - } - } - currentTaskItem = ` -
${task.project}/${task.experiment}
-
-
${task.name}
-
${dueDate}
-
-
-
${stateText}
-
-
-
`; - $currentTasksList.append(currentTaskItem); - }); + generateTasksListHtml(data, $currentTasksList); PerfectSb().update_all(); + if (newList) InfiniteScroll.resetScroll('.current-tasks-list'); animateSpinner($currentTasksList, false); }); } @@ -138,7 +161,7 @@ var DasboardCurrentTasksWidget = (function() { $('.curent-tasks-filters').dropdown('toggle'); e.stopPropagation(); e.preventDefault(); - loadCurrentTasksList(); + loadCurrentTasksList(true); }); } @@ -148,7 +171,7 @@ var DasboardCurrentTasksWidget = (function() { e.preventDefault(); $('.current-tasks-navbar').find('a').removeClass('active'); $(this).addClass('active'); - loadCurrentTasksList(); + loadCurrentTasksList(true); }); } @@ -158,6 +181,7 @@ var DasboardCurrentTasksWidget = (function() { initNavbar(); initFilters(); loadCurrentTasksList(); + initInfiniteScroll(); } } }; diff --git a/app/assets/javascripts/sitewide/infinite_scroll.js b/app/assets/javascripts/sitewide/infinite_scroll.js new file mode 100644 index 000000000..a062de0aa --- /dev/null +++ b/app/assets/javascripts/sitewide/infinite_scroll.js @@ -0,0 +1,68 @@ +/* eslint-disable no-unused-vars */ + +var InfiniteScroll = (function() { + function getScrollHeight($container) { + return $container[0].scrollHeight; + } + + function scrollNotVisible($container) { + return (getScrollHeight($container) - $container.height() - 150 <= 0); + } + + function loadData($container, page = 1) { + var customParams = $container.data('config').customParams; + var params = (customParams ? customParams({ page: page }) : { page: page }); + + if ($container.hasClass('loading') || $container.hasClass('last-page')) return; + $container.addClass('loading'); + + $.get($container.data('config').url, params, function(result) { + if ($container.data('config').customResponse) { + $container.data('config').customResponse(result, $container); + } else { + $(result.data).appendTo($container); + } + + if (result.next_page) { + $container.data('next-page', result.next_page); + } else { + $container.addClass('last-page'); + } + $container.removeClass('loading'); + + if (scrollNotVisible($container)) { + loadData($container, $container.data('next-page')); + } + }); + } + + function initScroll(object, config = {}) { + var $container = $(object); + $container.data('next-page', 2); + $container.data('config', config); + + if (config.loadFirstPage) { + loadData($container, 1); + } else if (scrollNotVisible($container)) { + loadData($container, $container.data('next-page')); + } + + $container.on('scroll', () => { + if ($container.scrollTop() + $container.height() > getScrollHeight($container) - 150 && !$container.hasClass('last-page')) { + loadData($container, $container.data('next-page')); + } + }); + } + + return { + init: (object, config) => { + initScroll(object, config); + }, + resetScroll: (object) => { + $(object).data('next-page', 2).removeClass('last-page'); + if (scrollNotVisible($(object))) { + loadData($(object), $(object).data('next-page')); + } + } + }; +}()); diff --git a/app/controllers/dashboard/current_tasks_controller.rb b/app/controllers/dashboard/current_tasks_controller.rb index 340b732c4..e1da0b1d4 100644 --- a/app/controllers/dashboard/current_tasks_controller.rb +++ b/app/controllers/dashboard/current_tasks_controller.rb @@ -34,28 +34,25 @@ module Dashboard tasks end - tasks = tasks.with_step_statistics.preload(experiment: :project) + page = (params[:page] || 1).to_i + tasks_per_page = tasks.page(page).per(Constants::INFINITE_SCROLL_LIMIT) - respond_to do |format| - format.json do - render json: { - tasks_list: tasks.map do |task| - { id: task.id, - link: protocols_my_module_path(task.id), - experiment: escape_input(task.experiment.name), - project: escape_input(task.experiment.project.name), - name: escape_input(task.name), - due_date: task.due_date.present? ? I18n.l(task.due_date, format: :full_with_comma) : nil, - overdue: task.is_overdue?, - state: task.state, - steps_state: { completed_steps: task.steps_completed, - all_steps: task.steps_total, - percentage: task.steps_completed_percentage } } - end, - status: :ok - } - end + tasks_per_page = tasks_per_page.with_step_statistics.preload(experiment: :project) + tasks_list = tasks_per_page.map do |task| + { id: task.id, + link: protocols_my_module_path(task.id), + experiment: escape_input(task.experiment.name), + project: escape_input(task.experiment.project.name), + name: escape_input(task.name), + due_date: task.due_date.present? ? I18n.l(task.due_date, format: :full_with_comma) : nil, + overdue: task.is_overdue?, + state: task.state, + steps_state: { completed_steps: task.steps_completed, + all_steps: task.steps_total, + percentage: task.steps_completed_percentage } } end + + render json: { data: tasks_list, next_page: tasks_per_page.next_page } end def project_filter @@ -83,7 +80,7 @@ module Dashboard private def task_filters - params.permit(:project_id, :experiment_id, :mode, :view, :sort) + params.permit(:project_id, :experiment_id, :mode, :view, :sort, :page) end def load_project diff --git a/app/views/dashboards/_current_tasks.html.erb b/app/views/dashboards/_current_tasks.html.erb index 08796cdc1..ff05d50eb 100644 --- a/app/views/dashboards/_current_tasks.html.erb +++ b/app/views/dashboards/_current_tasks.html.erb @@ -56,7 +56,7 @@
-
diff --git a/config/initializers/constants.rb b/config/initializers/constants.rb index 7cfaed03e..78827f417 100644 --- a/config/initializers/constants.rb +++ b/config/initializers/constants.rb @@ -56,16 +56,14 @@ class Constants COMMENTS_SEARCH_LIMIT = 10 # Activity limited query/display elements for pages ACTIVITY_AND_NOTIF_SEARCH_LIMIT = 20 - + # Infinite Scroll load limit (elements per page) + INFINITE_SCROLL_LIMIT = 20 # Maximum number of users that can be invited in a single action INVITE_USERS_LIMIT = 20 - # Maximum nr. of search results for atwho (smart annotations) ATWHO_SEARCH_LIMIT = 5 - # Max characters for repository name in Atwho modal ATWHO_REP_NAME_LIMIT = 16 - # Number of protocols in recent protocol dropdown RECENT_PROTOCOL_LIMIT = 14