Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 63 additions & 19 deletions htdocs/js/ProblemSetList/problemsetlist.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
for (const id of ids) elements.push(document.getElementById(id));
for (const element of elements) {
if (element?.id.endsWith('_err_msg')) {
element?.classList.remove('d-none');
} else {
element?.classList.add('is-invalid');
element.classList.remove('d-none');
} else if (element) {
element.classList.add('is-invalid');
if (!(element.id in event_listeners)) {
event_listeners[element.id] = hide_errors([], elements);
element?.addEventListener('change', event_listeners[element.id]);
element.addEventListener('change', event_listeners[element.id]);
}
}
}
Expand All @@ -23,17 +23,17 @@
for (const id of ids) elements.push(document.getElementById(id));
for (const element of elements) {
if (element?.id.endsWith('_err_msg')) {
element?.classList.add('d-none');
element.classList.add('d-none');
if (element.id === 'select_set_err_msg' && 'set_table_id' in event_listeners) {
document
.getElementById('set_table_id')
?.removeEventListener('change', event_listeners.set_table_id);
delete event_listeners.set_table_id;
}
} else {
element?.classList.remove('is-invalid');
} else if (element) {
element.classList.remove('is-invalid');
if (element.id in event_listeners) {
element?.removeEventListener('change', event_listeners[element.id]);
element.removeEventListener('change', event_listeners[element.id]);
delete event_listeners[element.id];
}
}
Expand Down Expand Up @@ -174,10 +174,15 @@
'zh-HK': 'yyyy/L/d ah:mm'
};

// Initialize the date/time picker for the import form.
// Initialize the date/time picker for the import form and common date editor.
const dateInputs = [];
const importDateShift = document.getElementById('import_date_shift');
if (importDateShift) {
luxon.Settings.defaultLocale = importDateShift.dataset.locale ?? 'en';
if (importDateShift) dateInputs.push(importDateShift);
const commonDateInput = document.getElementById('common-date');
if (commonDateInput) dateInputs.push(commonDateInput);

for (const dateInput of dateInputs) {
luxon.Settings.defaultLocale = dateInput.dataset.locale ?? 'en';

// Compute the time difference between a time in the browser timezone and the same time in the course timezone.
// flatpickr gives the time in the browser's timezone, and this is used to adjust to the course timezone.
Expand All @@ -189,17 +194,17 @@
new Date(dateTime.toLocaleString('en-US')).getTime() -
new Date(
dateTime.toLocaleString('en-US', {
timeZone: importDateShift.dataset.timezone ?? 'America/New_York'
timeZone: dateInput.dataset.timezone ?? 'America/New_York'
})
).getTime()
);
};

let fallbackDate = importDateShift.value
? new Date(parseInt(importDateShift.value) * 1000 - timezoneAdjustment(parseInt(importDateShift.value)))
let fallbackDate = dateInput.value
? new Date(parseInt(dateInput.value) * 1000 - timezoneAdjustment(parseInt(dateInput.value)))
: new Date();

const fp = flatpickr(importDateShift.parentNode, {
const fp = flatpickr(dateInput.parentNode, {
allowInput: true,
enableTime: true,
minuteIncrement: 1,
Expand All @@ -216,15 +221,15 @@
disableMobile: true,
wrap: true,
plugins: [
new confirmDatePlugin({ confirmText: importDateShift.dataset.doneText, showAlways: true }),
new confirmDatePlugin({ confirmText: dateInput.dataset.doneText, showAlways: true }),
new ShortcutButtonsPlugin({
button: [
{
label: importDateShift.dataset.todayText ?? 'Today',
label: dateInput.dataset.todayText ?? 'Today',
attributes: { class: 'btn btn-sm btn-secondary ms-auto me-1 mb-1' }
},
{
label: importDateShift.dataset.nowText ?? 'Now',
label: dateInput.dataset.nowText ?? 'Now',
attributes: { class: 'btn btn-sm btn-secondary mx-auto mb-1' }
}
],
Expand All @@ -251,6 +256,10 @@

// Make the alternate input left-to-right even for right-to-left languages.
this.altInput.dir = 'ltr';

// Move the id of the now hidden input onto the added input so the labels still work.
this.altInput.id = this.input.id;
this.input.removeAttribute('id');
},
parseDate(datestr, format) {
// Deal with the case of a unix timestamp. The timezone needs to be adjusted back as this is for
Expand Down Expand Up @@ -278,11 +287,46 @@
}
});

importDateShift.nextElementSibling.addEventListener('keydown', (e) => {
dateInput.nextElementSibling.addEventListener('keydown', (e) => {
if (e.key === ' ' || e.key === 'Enter') {
e.preventDefault();
fp.open();
}
});
}

if (commonDateInput) {
document.getElementById('apply-common-date')?.addEventListener('click', () => {
const dateTypeInput = document.getElementById('set-date-choice');
if (!dateTypeInput?.value) {
show_errors(['choose_date_type_err_msg'], [dateTypeInput]);
return;
}

if (!commonDateInput.value) {
show_errors(
['choose_common_date_err_msg'],
[commonDateInput.parentNode?._flatpickr?.input, commonDateInput.parentNode?._flatpickr?.altInput]
);
return;
}

const selectedSets = Array.from(document.getElementsByName('apply_date_sets')).filter((c) => c.checked);
if (!selectedSets.length) {
show_errors(['select_set_err_msg'], []);
event_listeners.set_table_id = hide_errors(
['set_table_id'],
[document.getElementById('select_set_err_msg')]
);
document.getElementById('set_table_id')?.addEventListener('change', event_listeners.set_table_id);
}

for (const set of selectedSets) {
const inputPicker = document.getElementsByName(`set.${set.value}.${dateTypeInput.value}`)[0]?.parentNode
?._flatpickr;
inputPicker?.setDate(commonDateInput.value, true);
inputPicker?.close(); // The picker isn't actually open, but this triggers the onClose handler.
}
});
}
})();
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
<tr>
%
% if ($c->{editMode}) {
<td><%= check_box apply_date_sets => $set_id, class => 'form-check-input' =%></td>
<td dir="ltr">
% if ($iconClass) {
<i class="<%= $iconClass =%>" title="<%= $iconTitle =%>" alt="<%= $iconTitle =%>"></i>
Expand Down Expand Up @@ -71,9 +72,9 @@
% for my $field (@$fieldNames) {
% next unless defined $fieldTypes->{$field};
<td>
<span class="d-inline-block w-100 text-center text-nowrap <%= $visibleClass %>">
<div class="d-inline-block w-100 text-center text-nowrap <%= $visibleClass %>">
<%= include 'ContentGenerator/Instructor/ProblemSetList/set_list_field',
name => "set.$set_id.$field", value => $set->$field, type => $fieldTypes->{$field} =%>
</span>
</div>
</td>
% }
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,50 @@
% answer_date => maketext('Answer Date')
% );
%
% if ($c->{editMode}) {
<div class="row">
<label class="col-auto col-form-label col-form-label-sm" for="set-date-choice">Set</label>
<div class="col-auto">
<select class="form-select form-select-sm" id="set-date-choice">
<option value="" selected><%= maketext('Choose set date type') %></option>
<option value="open_date"><%= maketext('Open Date') %></option>
% if ($c->ce->{pg}{ansEvalDefaults}{enableReducedScoring}) {
<option value="reduced_scoring_date"><%= maketext('Reduced Scoring Date') %></option>
% }
<option value="due_date"><%= maketext('Close Date') %></option>
<option value="answer_date"><%= maketext('Answer Date') %></option>
</select>
</div>
<label class="col-auto col-form-label col-form-label-sm" for="common-date">to</label>
<div class="col-auto input-group input-group-sm flatpickr flex-nowrap" style="max-width: 200px">
<%= text_field 'common-date' => '',
id => 'common-date', class => 'form-control',
data => {
input => undef,
done_text => maketext('Done'),
today_text => maketext('Today'),
now_text => maketext('Now'),
locale => $ce->{language},
timezone => $ce->{siteDefaults}{timezone}
} =%>
<a class="btn btn-secondary btn-sm" data-toggle role="button" tabindex="0"
aria-label="<%= maketext('Pick date and time') =%>">
<i class="fas fa-calendar-alt"></i>
</a>
</div>
<div class="col-auto">
<button id="apply-common-date" type="button" class="btn btn-secondary btn-sm">
<%= maketext('Apply to Selected Sets') %>
</button>
</div>
</div>
<div id="choose_date_type_err_msg" class="alert alert-danger p-1 mb-0 mt-2 d-inline-flex d-none">
<%= maketext('Please choose a set date type.') %>
</div>
<div id="choose_common_date_err_msg" class="alert alert-danger p-1 mb-0 mt-2 d-inline-flex d-none">
<%= maketext('Please select a date.') %>
</div>
% }
<div id="select_set_err_msg" class="alert alert-danger p-1 mb-0 mt-2 d-inline-flex d-none">
<%= maketext('Please select at least one set.') %>
</div>
Expand All @@ -21,22 +65,20 @@
%
<thead class="table-group-divider">
<tr>
% if (!$c->{editMode}) {
<th>
<%= label_for 'select-all', begin =%>
<%= check_box 'select-all' => '', id => 'select-all',
class => 'select-all form-check-input set-id-tooltip',
'aria-label' => maketext('Select all sets'),
data => {
select_group => 'selected_sets',
bs_toggle => 'tooltip',
bs_placement => 'right',
bs_title => maketext('Select all sets')
} =%>
<i class="fa-solid fa-check-double" aria-hidden="true"></i>
<% end =%>
</th>
% }
<th>
<%= label_for 'select-all', begin =%>
<%= check_box 'select-all' => '', id => 'select-all',
class => 'select-all form-check-input set-id-tooltip',
'aria-label' => maketext('Select all sets'),
data => {
select_group => $c->{editMode} ? 'apply_date_sets' : 'selected_sets',
bs_toggle => 'tooltip',
bs_placement => 'right',
bs_title => maketext('Select all sets')
} =%>
<i class="fa-solid fa-check-double" aria-hidden="true"></i>
<% end =%>
</th>
% for (@$fieldNames) {
<th id="<%= $_ %>_header">
% if (!$c->{editMode} && $sortableFields->{$_}) {
Expand Down