New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enable org queues to use task pagination API #11213
Changes from 4 commits
e57d639
9849fdd
3a050cb
65769d3
e36abdb
420d027
e5cba68
6aca957
ef6c091
219cb22
3ea371d
0650e67
6d268dc
322402e
25090e4
99c52ba
f631fa5
8d0172b
38722a7
eccf405
217f45e
6d724d9
7e872f9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,6 +24,10 @@ def show_regional_office_in_queue? | |
false | ||
end | ||
|
||
def use_task_pages_api? | ||
false | ||
end | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. By default organizations will continue using front-end pagination. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. could we make this use the FeatureToggle rather than hardcoded bool?
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FeatureToggle's are enabled on a per-user basis and this flag allows us to enable it on a per-organization basis which gives us another level of control as we roll this out. When this code is release live nothing should change. No requests will use the task pages API. However, we can roll this out in a phased fashion as follows:
This function at the organization enables phase 2 which I think is fairly important. Team queues work for most teams right now, so I'd love to limit the pagination changes to just those teams that are experiencing team queue loading problems before rolling the changes out to everybody. |
||
|
||
def non_admins | ||
organizations_users.includes(:user).non_admin.map(&:user) | ||
end | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,4 +8,8 @@ def self.singleton | |
def next_assignee(options = {}) | ||
ColocatedTaskDistributor.new.next_assignee(options) | ||
end | ||
|
||
def use_task_pages_api? | ||
true | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same thing here about feature toggle. |
||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,10 @@ | ||
# frozen_string_literal: true | ||
|
||
class HearingsManagement < Organization | ||
def self.singleton | ||
HearingsManagement.first || HearingsManagement.create(name: "Hearings Management", url: "hearings-management") | ||
end | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Moving the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Totally agree. Going to postpone rearranging that class to a later PR so that this changeset stays small. |
||
def can_bulk_assign_tasks? | ||
true | ||
end | ||
|
@@ -9,7 +13,7 @@ def show_regional_office_in_queue? | |
true | ||
end | ||
|
||
def self.singleton | ||
HearingsManagement.first || HearingsManagement.create(name: "Hearings Management", url: "hearings-management") | ||
def use_task_pages_api? | ||
true | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. feature toggle? |
||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,7 @@ | ||
# frozen_string_literal: true | ||
|
||
class Vso < Representative; end | ||
class Vso < Representative | ||
def use_task_pages_api? | ||
true | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. feature toggle? |
||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,17 +17,22 @@ def initialize(args) | |
end | ||
|
||
def to_hash_for_user(user) | ||
serialized_tabs = tabs.each { |tab| tab[:tasks] = serialized_tasks_for_user(tab[:tasks], user) } | ||
|
||
{ | ||
table_title: format(COPY::ORGANIZATION_QUEUE_TABLE_TITLE, organization.name), | ||
active_tab: Constants.QUEUE_CONFIG.UNASSIGNED_TASKS_TAB_NAME, | ||
tabs: serialized_tabs | ||
tasks_per_page: TaskPager::TASKS_PER_PAGE, | ||
use_task_pages_api: use_task_pages_api?, | ||
tabs: tabs.map { |tab| attach_tasks_to_tab(tab, user) } | ||
} | ||
end | ||
|
||
private | ||
|
||
def use_task_pages_api? | ||
# return true | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My manual toggle for easy testing locally. Will remove before this is merged. |
||
FeatureToggle.enabled?(:use_task_pages_api) && organization.use_task_pages_api? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. +1 |
||
end | ||
|
||
def tabs | ||
[ | ||
include_tracking_tasks_tab? ? tracking_tasks_tab : nil, | ||
|
@@ -37,6 +42,22 @@ def tabs | |
].compact | ||
end | ||
|
||
def attach_tasks_to_tab(tab, user) | ||
task_pager = TaskPager.new(assignee: organization, tab_name: tab[:name]) | ||
|
||
# Only return tasks in the configuration if we are using it to populate the first page of results. | ||
# Otherwise avoid the overhead of the additional database requests. | ||
tasks = use_task_pages_api? ? serialized_tasks_for_user(task_pager.paged_tasks, user) : [] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Inverse of the optimization above. Don't make additional requests to the database if we are just going to be using the tasks we get from some other method. |
||
|
||
tab.merge( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nice! |
||
label: format(tab[:label], task_pager.total_task_count), | ||
tasks: tasks, | ||
task_page_count: task_pager.task_page_count, | ||
total_task_count: task_pager.total_task_count, | ||
task_page_endpoint_base_path: "#{organization.path}/task_pages?tab=#{tab[:name]}" | ||
) | ||
end | ||
|
||
def serialized_tasks_for_user(tasks, user) | ||
return [] if tasks.empty? | ||
|
||
|
@@ -54,33 +75,24 @@ def include_tracking_tasks_tab? | |
end | ||
|
||
def tracking_tasks_tab | ||
name = Constants.QUEUE_CONFIG.TRACKING_TASKS_TAB_NAME | ||
tasks = TaskPager.new(assignee: organization, tab_name: name).tasks_for_tab | ||
|
||
{ | ||
label: COPY::ALL_CASES_QUEUE_TABLE_TAB_TITLE, | ||
name: name, | ||
name: Constants.QUEUE_CONFIG.TRACKING_TASKS_TAB_NAME, | ||
description: format(COPY::ALL_CASES_QUEUE_TABLE_TAB_DESCRIPTION, organization.name), | ||
columns: [ | ||
Constants.QUEUE_CONFIG.CASE_DETAILS_LINK_COLUMN, | ||
Constants.QUEUE_CONFIG.ISSUE_COUNT_COLUMN, | ||
Constants.QUEUE_CONFIG.APPEAL_TYPE_COLUMN, | ||
Constants.QUEUE_CONFIG.DOCKET_NUMBER_COLUMN | ||
], | ||
task_group: Constants.QUEUE_CONFIG.TRACKING_TASKS_GROUP, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We never ended up using the |
||
tasks: tasks, | ||
allow_bulk_assign: false | ||
} | ||
end | ||
|
||
# rubocop:disable Metrics/AbcSize | ||
def unassigned_tasks_tab | ||
name = Constants.QUEUE_CONFIG.UNASSIGNED_TASKS_TAB_NAME | ||
tasks = TaskPager.new(assignee: organization, tab_name: name).tasks_for_tab | ||
|
||
{ | ||
label: format(COPY::ORGANIZATIONAL_QUEUE_PAGE_UNASSIGNED_TAB_TITLE, tasks.count), | ||
name: name, | ||
label: COPY::ORGANIZATIONAL_QUEUE_PAGE_UNASSIGNED_TAB_TITLE, | ||
name: Constants.QUEUE_CONFIG.UNASSIGNED_TASKS_TAB_NAME, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Was able to consolidate all this duplicated logic in the new |
||
description: format(COPY::ORGANIZATIONAL_QUEUE_PAGE_UNASSIGNED_TASKS_DESCRIPTION, organization.name), | ||
# Compact to account for the maybe absent regional office column | ||
columns: [ | ||
|
@@ -93,21 +105,14 @@ def unassigned_tasks_tab | |
Constants.QUEUE_CONFIG.DAYS_ON_HOLD_COLUMN, | ||
Constants.QUEUE_CONFIG.DOCUMENT_COUNT_READER_LINK_COLUMN | ||
].compact, | ||
task_group: Constants.QUEUE_CONFIG.UNASSIGNED_TASKS_GROUP, | ||
tasks: tasks, | ||
allow_bulk_assign: organization.can_bulk_assign_tasks? | ||
} | ||
end | ||
# rubocop:enable Metrics/AbcSize | ||
|
||
# rubocop:disable Metrics/AbcSize | ||
def assigned_tasks_tab | ||
name = Constants.QUEUE_CONFIG.ASSIGNED_TASKS_TAB_NAME | ||
tasks = TaskPager.new(assignee: organization, tab_name: name).tasks_for_tab | ||
|
||
{ | ||
label: format(COPY::QUEUE_PAGE_ASSIGNED_TAB_TITLE, tasks.count), | ||
name: name, | ||
label: COPY::QUEUE_PAGE_ASSIGNED_TAB_TITLE, | ||
name: Constants.QUEUE_CONFIG.ASSIGNED_TASKS_TAB_NAME, | ||
description: format(COPY::ORGANIZATIONAL_QUEUE_PAGE_ASSIGNED_TASKS_DESCRIPTION, organization.name), | ||
# Compact to account for the maybe absent regional office column | ||
columns: [ | ||
|
@@ -120,20 +125,14 @@ def assigned_tasks_tab | |
Constants.QUEUE_CONFIG.DOCKET_NUMBER_COLUMN, | ||
Constants.QUEUE_CONFIG.DAYS_ON_HOLD_COLUMN | ||
].compact, | ||
task_group: Constants.QUEUE_CONFIG.ASSIGNED_TASKS_GROUP, | ||
tasks: tasks, | ||
allow_bulk_assign: false | ||
} | ||
end | ||
# rubocop:enable Metrics/AbcSize | ||
|
||
def completed_tasks_tab | ||
name = Constants.QUEUE_CONFIG.COMPLETED_TASKS_TAB_NAME | ||
tasks = TaskPager.new(assignee: organization, tab_name: name).tasks_for_tab | ||
|
||
{ | ||
label: COPY::QUEUE_PAGE_COMPLETE_TAB_TITLE, | ||
name: name, | ||
name: Constants.QUEUE_CONFIG.COMPLETED_TASKS_TAB_NAME, | ||
description: format(COPY::QUEUE_PAGE_COMPLETE_TASKS_DESCRIPTION, organization.name), | ||
# Compact to account for the maybe absent regional office column | ||
columns: [ | ||
|
@@ -146,8 +145,6 @@ def completed_tasks_tab | |
Constants.QUEUE_CONFIG.DOCKET_NUMBER_COLUMN, | ||
Constants.QUEUE_CONFIG.DAYS_ON_HOLD_COLUMN | ||
].compact, | ||
task_group: Constants.QUEUE_CONFIG.COMPLETED_TASKS_GROUP, | ||
tasks: tasks, | ||
allow_bulk_assign: false | ||
} | ||
end | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -46,6 +46,14 @@ def paged_tasks | |
# tasks | ||
# end | ||
|
||
def task_page_count | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. does the kaminari |
||
(total_task_count / TASKS_PER_PAGE.to_f).ceil | ||
end | ||
|
||
def total_task_count | ||
@total_task_count ||= tasks_for_tab.count | ||
end | ||
|
||
def tasks_for_tab | ||
case tab_name | ||
when Constants.QUEUE_CONFIG.TRACKING_TASKS_TAB_NAME | ||
|
@@ -67,6 +75,7 @@ def tracking_tasks | |
TrackVeteranTask.includes(*task_includes).active.where(assigned_to: assignee) | ||
end | ||
|
||
# TODO: Rename this function so that it makes sense for users and organizations | ||
def unassigned_tasks | ||
Task.includes(*task_includes) | ||
.visible_in_queue_table_view.where(assigned_to: assignee).active | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -36,33 +36,28 @@ class TablePagination extends React.PureComponent { | |
|
||
render() { | ||
const { | ||
paginatedData, | ||
casesPerPage, | ||
currentPage, | ||
numberOfPages, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pass |
||
totalCasesCount | ||
} = this.props; | ||
const numberOfPages = paginatedData.length; | ||
|
||
// Render the pagination summary | ||
// Get previous number of cases so we can calculate the case range in | ||
// the pagination summary | ||
let previousCaseCount = 0; | ||
let beginningCaseNumber = 0; | ||
let endingCaseNumber = 0; | ||
|
||
for (let i = 0; i < currentPage; i += 1) { | ||
previousCaseCount += paginatedData[i] ? paginatedData[i].length : 0; | ||
if (totalCasesCount) { | ||
// Assume we are in one of the middle pages. | ||
beginningCaseNumber = 1 + (currentPage * casesPerPage); | ||
endingCaseNumber = (currentPage + 1) * casesPerPage; | ||
|
||
// Correct for us being on the last page. | ||
if (endingCaseNumber > totalCasesCount) { | ||
endingCaseNumber = totalCasesCount; | ||
} | ||
} | ||
// If there are no pages, there is no data, so the range should be 0-0. | ||
// Otherwise, the beginning of the range is the previous amount of cases + 1 | ||
const beginningCaseNumber = numberOfPages > 0 ? previousCaseCount + 1 : 0; | ||
// If there are no pages, there is no data, so the range should be 0-0. | ||
// Otherwise, the end of the range is the previous amount of cases + | ||
// the amount of data in the current page. | ||
const endingCaseNumber = numberOfPages > 0 && paginatedData[currentPage] ? | ||
previousCaseCount + paginatedData[currentPage].length : | ||
0; | ||
// Create the range | ||
let currentCaseRange = `${beginningCaseNumber}-${endingCaseNumber}`; | ||
// Create the entire summary | ||
const paginationSummary = `Viewing ${currentCaseRange} of ${totalCasesCount} total cases`; | ||
|
||
const paginationSummary = `Viewing ${beginningCaseNumber}-${endingCaseNumber} of ${totalCasesCount} total cases`; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No significant change here other than we were able to replace the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure if you looked at the Pagination component? I did something similar there for the /jobs pagination. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's fairly easy to replace the only place we use |
||
|
||
// Render the page buttons | ||
let pageButtons = []; | ||
let paginationButtons = []; | ||
|
@@ -137,7 +132,7 @@ class TablePagination extends React.PureComponent { | |
|
||
TablePagination.propTypes = { | ||
currentPage: PropTypes.number.isRequired, | ||
paginatedData: PropTypes.arrayOf(PropTypes.array).isRequired, | ||
numberOfPages: PropTypes.number.isRequired, | ||
totalCasesCount: PropTypes.number.isRequired, | ||
updatePage: PropTypes.func.isRequired | ||
}; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,7 @@ import { | |
getCompletedOrganizationalTasks, | ||
trackingTasksForOrganization | ||
} from './selectors'; | ||
import { tasksWithAppealsFromRawTasks } from './utils'; | ||
import { clearCaseSelectSearch } from '../reader/CaseSelect/CaseSelectActions'; | ||
import { fullWidth } from './constants'; | ||
import QUEUE_CONFIG from '../../constants/QUEUE_CONFIG.json'; | ||
|
@@ -86,19 +87,25 @@ class OrganizationQueue extends React.PureComponent { | |
return mapper[tabName]; | ||
} | ||
|
||
taskTableTabFactory = (tabConfig) => { | ||
const { label, description } = tabConfig; | ||
const cols = this.columnsFromConfig(tabConfig); | ||
const tasks = this.tasksForTab(tabConfig.name); | ||
taskTableTabFactory = (tabConfig, config) => { | ||
const tasks = config.use_task_pages_api ? | ||
tasksWithAppealsFromRawTasks(tabConfig.tasks) : | ||
this.tasksForTab(tabConfig.name); | ||
|
||
return { | ||
label, | ||
label: tabConfig.label, | ||
page: <React.Fragment> | ||
<p className="cf-margin-top-0">{description}</p> | ||
<p className="cf-margin-top-0">{tabConfig.description}</p> | ||
{ tabConfig.allow_bulk_assign && <BulkAssignButton /> } | ||
<TaskTable | ||
customColumns={cols} | ||
key={tabConfig.name} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Need to set a |
||
customColumns={this.columnsFromConfig(tabConfig)} | ||
tasks={tasks} | ||
useTaskPagesApi={config.use_task_pages_api} | ||
tasksPerPage={config.tasks_per_page} | ||
numberOfPages={tabConfig.task_page_count} | ||
totalTaskCount={tabConfig.total_task_count} | ||
taskPagesApiEndpoint={tabConfig.task_page_endpoint_base_path} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since we're adding 5 new arguments here maybe it makes sense to just make this a new component? What're y'all's thoughts? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we believe we will ever want to use the |
||
/> | ||
</React.Fragment> | ||
}; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we are using the back-end pagination API then we should not bother with returning the tasks here since we will use the ones from the queue config for the first page and the API for all subsequent pages.