Skip to content

Commit

Permalink
Add reverse sorting.
Browse files Browse the repository at this point in the history
  • Loading branch information
drgrice1 committed Oct 21, 2023
1 parent 2c1910a commit 44116b4
Show file tree
Hide file tree
Showing 4 changed files with 202 additions and 73 deletions.
15 changes: 14 additions & 1 deletion htdocs/js/JobManager/jobmanager.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
toggle_filter_elements();
}

// Submit the job list form when a sort header is clicked or enter or space is pressed when it has focus.
// Submit the form when a sort header is clicked or enter or space is pressed when it has focus.
const currentAction = document.getElementById('current_action');
if (currentAction) {
for (const header of document.querySelectorAll('.sort-header')) {
Expand All @@ -33,6 +33,19 @@
header.addEventListener('keydown', (e) => {
if (e.key === ' ' || e.key === 'Enter') submitSortMethod(e);
});

const orderToggleButton = header.parentElement.querySelector('button.sort-order');
orderToggleButton?.addEventListener('click', () => {
currentAction.value = 'sort';

const sortOrderInput = document.createElement('input');
sortOrderInput.name = 'labelSortOrder';
sortOrderInput.value = orderToggleButton.dataset.sortPriority;
sortOrderInput.type = 'hidden';
currentAction.form.append(sortOrderInput);

currentAction.form.submit();
});
}
}

Expand Down
72 changes: 52 additions & 20 deletions lib/WeBWorK/ContentGenerator/Instructor/JobManager.pm
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,13 @@ use constant FIELDS => [
];

use constant SORT_SUBS => {
id => \&byJobID,
courseID => \&byCourseID,
task => \&byTask,
created => \&byCreatedTime,
started => \&byStartedTime,
finished => \&byFinishedTime,
state => \&byState
id => { ASC => \&byJobID, DESC => \&byDescJobID },
courseID => { ASC => \&byCourseID, DESC => \&byDescCourseID },
task => { ASC => \&byTask, DESC => \&byDescTask },
created => { ASC => \&byCreatedTime, DESC => \&byDescCreatedTime },
started => { ASC => \&byStartedTime, DESC => \&byDescStartedTime },
finished => { ASC => \&byFinishedTime, DESC => \&byDescFinishedTime },
state => { ASC => \&byState, DESC => \&byDescState }
};

sub initialize ($c) {
Expand All @@ -64,8 +64,11 @@ sub initialize ($c) {
$c->stash->{selectedJobs} = {};
$c->stash->{sortedJobs} = [];
$c->stash->{primarySortField} = $c->param('primarySortField') || 'created';
$c->stash->{primarySortOrder} = $c->param('primarySortOrder') || 'ASC';
$c->stash->{secondarySortField} = $c->param('secondarySortField') || 'task';
$c->stash->{secondarySortOrder} = $c->param('secondarySortOrder') || 'ASC';
$c->stash->{ternarySortField} = $c->param('ternarySortField') || 'state';
$c->stash->{ternarySortOrder} = $c->param('ternarySortOrder') || 'ASC';

return unless $c->authz->hasPermissions($c->param('user'), 'access_instructor_tools');

Expand Down Expand Up @@ -108,9 +111,9 @@ sub initialize ($c) {
}

# Sort jobs
my $primarySortSub = SORT_SUBS()->{ $c->stash->{primarySortField} };
my $secondarySortSub = SORT_SUBS()->{ $c->stash->{secondarySortField} };
my $ternarySortSub = SORT_SUBS()->{ $c->stash->{ternarySortField} };
my $primarySortSub = SORT_SUBS()->{ $c->stash->{primarySortField} }{ $c->stash->{primarySortOrder} };
my $secondarySortSub = SORT_SUBS()->{ $c->stash->{secondarySortField} }{ $c->stash->{secondarySortOrder} };
my $ternarySortSub = SORT_SUBS()->{ $c->stash->{ternarySortField} }{ $c->stash->{ternarySortOrder} };

# byJobID is included to ensure a definite sort order in case the
# first three sorts do not determine a proper order.
Expand Down Expand Up @@ -148,26 +151,47 @@ sub filter_handler ($c) {
}

sub sort_handler ($c) {
if (defined $c->param('labelSortMethod')) {
$c->stash->{ternarySortField} = $c->stash->{secondarySortField};
$c->stash->{secondarySortField} = $c->stash->{primarySortField};
$c->stash->{primarySortField} = $c->param('labelSortMethod');
$c->param('action.sort.primary', $c->stash->{primarySortField});
$c->param('action.sort.secondary', $c->stash->{secondarySortField});
$c->param('action.sort.ternary', $c->stash->{ternarySortField});
if (defined $c->param('labelSortMethod') || defined $c->param('labelSortOrder')) {
if (defined $c->param('labelSortOrder')) {
$c->stash->{ $c->param('labelSortOrder') . 'SortOrder' } =
$c->stash->{ $c->param('labelSortOrder') . 'SortOrder' } eq 'ASC' ? 'DESC' : 'ASC';
} elsif ($c->param('labelSortMethod') eq $c->stash->{primarySortField}) {
$c->stash->{primarySortOrder} = $c->stash->{primarySortOrder} eq 'ASC' ? 'DESC' : 'ASC';
} else {
$c->stash->{ternarySortField} = $c->stash->{secondarySortField};
$c->stash->{ternarySortOrder} = $c->stash->{secondarySortOrder};
$c->stash->{secondarySortField} = $c->stash->{primarySortField};
$c->stash->{secondarySortOrder} = $c->stash->{primarySortOrder};
$c->stash->{primarySortField} = $c->param('labelSortMethod');
$c->stash->{primarySortOrder} = 'ASC';
}

$c->param('action.sort.primary', $c->stash->{primarySortField});
$c->param('action.sort.primary.order', $c->stash->{primarySortOrder});
$c->param('action.sort.secondary', $c->stash->{secondarySortField});
$c->param('action.sort.secondary.order', $c->stash->{secondarySortOrder});
$c->param('action.sort.ternary', $c->stash->{ternarySortField});
$c->param('action.sort.ternary.order', $c->stash->{ternarySortOrder});
} else {
$c->stash->{primarySortField} = $c->param('action.sort.primary');
$c->stash->{primarySortOrder} = $c->param('action.sort.primary.order');
$c->stash->{secondarySortField} = $c->param('action.sort.secondary');
$c->stash->{secondarySortOrder} = $c->param('action.sort.secondary.order');
$c->stash->{ternarySortField} = $c->param('action.sort.ternary');
$c->stash->{ternarySortOrder} = $c->param('action.sort.ternary.order');
}

return $c->maketext(
'Users sorted by [_1], then by [_2], then by [_3]',
'Sets sorted by [_1] in [plural,_2,ascending,descending] order, '
. 'then by [_3] in [plural,_4,ascending,descending] order,'
. 'and then by [_5] in [plural,_6,ascending,descending] order.',
$c->maketext((grep { $_->[0] eq $c->stash->{primarySortField} } @{ FIELDS() })[0][1]),
$c->stash->{primarySortOrder} eq 'ASC' ? 1 : 2,
$c->maketext((grep { $_->[0] eq $c->stash->{secondarySortField} } @{ FIELDS() })[0][1]),
$c->maketext((grep { $_->[0] eq $c->stash->{ternarySortField} } @{ FIELDS() })[0][1])
$c->stash->{secondarySortOrder} eq 'ASC' ? 1 : 2,
$c->maketext((grep { $_->[0] eq $c->stash->{ternarySortField} } @{ FIELDS() })[0][1]),
$c->stash->{ternarySortOrder} eq 'ASC' ? 1 : 2
);

}

sub delete_handler ($c) {
Expand Down Expand Up @@ -202,4 +226,12 @@ sub byStartedTime { return ($a->{started} || 0) <=> ($b->{started} || 0) }
sub byFinishedTime { return ($a->{finished} || 0) <=> ($b->{finished} || 0) }
sub byState { return $a->{state} cmp $b->{state} }

sub byDescJobID { local ($b, $a) = ($a, $b); return byJobID(); }
sub byDescCourseID { local ($b, $a) = ($a, $b); return byCourseID(); }
sub byDescTask { local ($b, $a) = ($a, $b); return byTask(); }
sub byDescCreatedTime { local ($b, $a) = ($a, $b); return byCreatedTime(); }
sub byDescStartedTime { local ($b, $a) = ($a, $b); return byStartedTime(); }
sub byDescFinishedTime { local ($b, $a) = ($a, $b); return byFinishedTime(); }
sub byDescState { local ($b, $a) = ($a, $b); return byState(); }

1;
71 changes: 52 additions & 19 deletions templates/ContentGenerator/Instructor/JobManager.html.ep
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,11 @@
% }
%
<%= hidden_field primarySortField => $primarySortField =%>
<%= hidden_field primarySortOrder => $primarySortOrder =%>
<%= hidden_field secondarySortField => $secondarySortField =%>
<%= hidden_field secondarySortOrder => $secondarySortOrder =%>
<%= hidden_field ternarySortField => $ternarySortField =%>
<%= hidden_field ternarySortOrder => $ternarySortOrder =%>
%
% # Output action forms
% for my $form (@$actionForms) {
Expand Down Expand Up @@ -74,41 +77,71 @@
<table class="table table-sm table-bordered caption-top font-sm">
<thead class="table-group-divider">
<tr>
<th>
<%= check_box 'select-all' => 'on', id => 'select-all',
'aria-label' => maketext('Select all jobs'),
data => { select_group => 'selected_jobs' },
class => 'select-all form-check-input' =%>
<th class="text-nowrap">
<%= label_for 'select-all', begin =%>
<%= check_box 'select-all' => 'on', id => 'select-all',
class => 'select-all form-check-input set-id-tooltip',
'aria-label' => maketext('Select all jobs'),
data => {
select_group => 'selected_jobs',
bs_toggle => 'tooltip',
bs_placement => 'right',
bs_title => maketext('Select all jobs')
} =%>
<i class="fa-solid fa-check-double" aria-hidden="true"></i>
<% end =%>
</th>
<th>
<%= label_for 'select-all' =>
link_to maketext('Id') => '#', class => 'sort-header', data => { sort_field => 'id' } =%>
<div class="d-flex justify-content-between align-items-end gap-1">
<%= link_to maketext('Id') => '#', class => 'sort-header',
data => { sort_field => 'id' } =%>
<%= include 'ContentGenerator/Instructor/JobManager/sort_button', field => 'id' =%>
</div>
</th>
% if ($courseID eq 'admin') {
<th>
<%= link_to maketext('Course Id') => '#', class => 'sort-header',
data => { sort_field => 'courseID' } =%>
<div class="d-flex justify-content-between align-items-end gap-1">
<%= link_to maketext('Course Id') => '#', class => 'sort-header',
data => { sort_field => 'courseID' } =%>
<%= include 'ContentGenerator/Instructor/JobManager/sort_button',
field => 'course_id' =%>
</div>
</th>
% }
<th>
<%= link_to maketext('Task') => '#', class => 'sort-header',
data => { sort_field => 'task' } =%>
<div class="d-flex justify-content-between align-items-end gap-1">
<%= link_to maketext('Task') => '#', class => 'sort-header',
data => { sort_field => 'task' } =%>
<%= include 'ContentGenerator/Instructor/JobManager/sort_button', field => 'task' =%>
</div>
</th>
<th>
<%= link_to maketext('Created') => '#', class => 'sort-header',
data => { sort_field => 'created' } =%>
<div class="d-flex justify-content-between align-items-end gap-1">
<%= link_to maketext('Created') => '#', class => 'sort-header',
data => { sort_field => 'created' } =%>
<%= include 'ContentGenerator/Instructor/JobManager/sort_button', field => 'created' =%>
</div>
</th>
<th>
<%= link_to maketext('Started') => '#', class => 'sort-header',
data => { sort_field => 'started' } =%>
<div class="d-flex justify-content-between align-items-end gap-1">
<%= link_to maketext('Started') => '#', class => 'sort-header',
data => { sort_field => 'started' } =%>
<%= include 'ContentGenerator/Instructor/JobManager/sort_button', field => 'started' =%>
</div>
</th>
<th>
<%= link_to maketext('Finished') => '#', class => 'sort-header',
data => { sort_field => 'finished' } =%>
<div class="d-flex justify-content-between align-items-end gap-1">
<%= link_to maketext('Finished') => '#', class => 'sort-header',
data => { sort_field => 'finished' } =%>
<%= include 'ContentGenerator/Instructor/JobManager/sort_button', field => 'finished' =%>
</div>
</th>
<th>
<%= link_to maketext('State') => '#', class => 'sort-header',
data => { sort_field => 'state' } =%>
<div class="d-flex justify-content-between align-items-end gap-1">
<%= link_to maketext('State') => '#', class => 'sort-header',
data => { sort_field => 'state' } =%>
<%= include 'ContentGenerator/Instructor/JobManager/sort_button', field => 'state' =%>
</div>
</th>
</tr>
</thead>
Expand Down
117 changes: 84 additions & 33 deletions templates/ContentGenerator/Instructor/JobManager/sort_form.html.ep
Original file line number Diff line number Diff line change
@@ -1,41 +1,92 @@
<div>
<div class="row mb-2">
<%= label_for sort_select_1 => maketext('Sort by') . ':', class => 'col-form-label col-form-label-sm',
style => 'width:4.5rem' =%>
<div class="col-auto">
<%= select_field 'action.sort.primary' => [
map { [
maketext($_->[1]) => $_->[0],
$_->[0] eq 'created' ? (selected => undef) : ()
] } @$fields
],
id => 'sort_select_1', class => 'form-select form-select-sm' =%>
<div class="row">
<div class="col-md-auto col-sm-12">
<div class="row mb-2">
<%= label_for sort_select_1 => maketext('Sort by') . ':',
class => 'col-form-label col-form-label-sm', style => 'width:4.5rem' =%>
<div class="col-auto">
<%= select_field 'action.sort.primary' => [
map { [
maketext($_->[1]) => $_->[0],
$_->[0] eq 'created' ? (selected => undef) : ()
] } @$fields
],
id => 'sort_select_1', class => 'form-select form-select-sm' =%>
</div>
</div>
</div>
<div class="col-md-auto col-sm-12">
<div class="row mb-2">
<%= label_for sort_order_select_1 => maketext('Ordered') . ':',
class => 'col-form-label col-form-label-sm', style => 'width:4.5rem' =%>
<div class="col-auto">
<%= select_field 'action.sort.primary.order' => [
[ maketext('Ascending') => 'ASC' ],
[ maketext('Descending') => 'DESC' ],
],
id => 'sort_order_select_1', class => 'form-select form-select-sm' =%>
</div>
</div>
</div>
</div>
<div class="row mb-2">
<%= label_for sort_select_2 => maketext('Then by') . ':', class => 'col-form-label col-form-label-sm',
style => 'width:4.5rem' =%>
<div class="col-auto">
<%= select_field 'action.sort.secondary' => [
map { [
maketext($_->[1]) => $_->[0],
$_->[0] eq 'task' ? (selected => undef) : ()
] } @$fields
],
id => 'sort_select_2', class => 'form-select form-select-sm' =%>
<div class="row">
<div class="col-md-auto col-sm-12">
<div class="row mb-2">
<%= label_for sort_select_2 => maketext('Then by') . ':',
class => 'col-form-label col-form-label-sm', style => 'width:4.5rem' =%>
<div class="col-auto">
<%= select_field 'action.sort.secondary' => [
map { [
maketext($_->[1]) => $_->[0],
$_->[0] eq 'task' ? (selected => undef) : ()
] } @$fields
],
id => 'sort_select_2', class => 'form-select form-select-sm' =%>
</div>
</div>
</div>
<div class="col-md-auto col-sm-12">
<div class="row mb-2">
<%= label_for sort_order_select_2 => maketext('Ordered') . ':',
class => 'col-form-label col-form-label-sm', style => 'width:4.5rem' =%>
<div class="col-auto">
<%= select_field 'action.sort.secondary.order' => [
[ maketext('Ascending') => 'ASC' ],
[ maketext('Descending') => 'DESC' ],
],
id => 'sort_order_select_2', class => 'form-select form-select-sm' =%>
</div>
</div>
</div>
</div>
<div class="row mb-2">
<%= label_for sort_select_3 => maketext('Then by') . ':', class => 'col-form-label col-form-label-sm',
style => 'width:4.5rem' =%>
<div class="col-auto">
<%= select_field 'action.sort.ternary' => [
map { [
maketext($_->[1]) => $_->[0],
$_->[0] eq 'state' ? (selected => undef) : ()
] } @$fields
],
id => 'sort_select_3', class => 'form-select form-select-sm' =%>
<div class="row">
<div class="col-md-auto col-sm-12">
<div class="row mb-2">
<%= label_for sort_select_3 => maketext('Then by') . ':',
class => 'col-form-label col-form-label-sm', style => 'width:4.5rem' =%>
<div class="col-auto">
<%= select_field 'action.sort.ternary' => [
map { [
maketext($_->[1]) => $_->[0],
$_->[0] eq 'state' ? (selected => undef) : ()
] } @$fields
],
id => 'sort_select_3', class => 'form-select form-select-sm' =%>
</div>
</div>
</div>
<div class="col-md-auto col-sm-12">
<div class="row mb-2">
<%= label_for sort_order_select_3 => maketext('Ordered') . ':',
class => 'col-form-label col-form-label-sm', style => 'width:4.5rem' =%>
<div class="col-auto">
<%= select_field 'action.sort.ternary.order' => [
[ maketext('Ascending') => 'ASC' ],
[ maketext('Descending') => 'DESC' ],
],
id => 'sort_order_select_3', class => 'form-select form-select-sm' =%>
</div>
</div>
</div>
</div>
</div>

0 comments on commit 44116b4

Please sign in to comment.