127 lines
3.8 KiB
JavaScript
127 lines
3.8 KiB
JavaScript
// Minimal row editor for timetable groups.
|
|
|
|
(function () {
|
|
var form = document.getElementById('timetable-group-form');
|
|
var body = document.querySelector('[data-timetable-entries-body]');
|
|
var addButton = document.querySelector('[data-add-timetable-entry]');
|
|
var template = document.getElementById('timetable-entry-row-template');
|
|
|
|
if (!form || !body || !addButton || !template) {
|
|
return;
|
|
}
|
|
|
|
function markDirty() {
|
|
form.dataset.dirty = 'true';
|
|
}
|
|
|
|
function pad(value) {
|
|
return String(value).padStart(2, '0');
|
|
}
|
|
|
|
function formatDateTimeLocalValue(date) {
|
|
if (!(date instanceof Date) || Number.isNaN(date.getTime())) {
|
|
return '';
|
|
}
|
|
|
|
return [
|
|
String(date.getFullYear()).padStart(4, '0'),
|
|
'-',
|
|
pad(date.getMonth() + 1),
|
|
'-',
|
|
pad(date.getDate()),
|
|
'T',
|
|
pad(date.getHours()),
|
|
':',
|
|
pad(date.getMinutes())
|
|
].join('');
|
|
}
|
|
|
|
function parseUtcDateTimeLocalValue(value) {
|
|
var raw = String(value || '').trim();
|
|
if (!raw) {
|
|
return null;
|
|
}
|
|
|
|
var normalized = /(?:[zZ]|[+-]\d\d(?::?\d\d)?)$/.test(raw) ? raw : raw + 'Z';
|
|
var date = new Date(normalized);
|
|
return Number.isNaN(date.getTime()) ? null : date;
|
|
}
|
|
|
|
function toUtcDateTimeLocalValue(value) {
|
|
var localDate = new Date(String(value || '').trim());
|
|
return Number.isNaN(localDate.getTime()) ? '' : localDate.toISOString();
|
|
}
|
|
|
|
function syncRowValuesToLocal(row) {
|
|
if (!row) {
|
|
return;
|
|
}
|
|
|
|
['entry_start_datetime[]', 'entry_end_datetime[]'].forEach(function (name) {
|
|
var input = row.querySelector('[name="' + name + '"]');
|
|
if (!input || input.dataset.timetableTimezoneSynced === 'true') {
|
|
return;
|
|
}
|
|
|
|
var localValue = formatDateTimeLocalValue(parseUtcDateTimeLocalValue(input.value));
|
|
if (localValue) {
|
|
input.value = localValue;
|
|
}
|
|
input.dataset.timetableTimezoneSynced = 'true';
|
|
});
|
|
}
|
|
|
|
function syncFormDataToUtc(formData) {
|
|
var rows = body.querySelectorAll('[data-timetable-entry-row]');
|
|
['entry_id[]', 'entry_title[]', 'entry_short_description[]', 'entry_start_datetime[]', 'entry_end_datetime[]'].forEach(function (name) {
|
|
formData.delete(name);
|
|
});
|
|
|
|
rows.forEach(function (row) {
|
|
var idInput = row.querySelector('[name="entry_id[]"]');
|
|
var titleInput = row.querySelector('[name="entry_title[]"]');
|
|
var descriptionInput = row.querySelector('[name="entry_short_description[]"]');
|
|
var startInput = row.querySelector('[name="entry_start_datetime[]"]');
|
|
var endInput = row.querySelector('[name="entry_end_datetime[]"]');
|
|
|
|
formData.append('entry_id[]', idInput ? idInput.value : '');
|
|
formData.append('entry_title[]', titleInput ? titleInput.value : '');
|
|
formData.append('entry_short_description[]', descriptionInput ? descriptionInput.value : '');
|
|
formData.append('entry_start_datetime[]', startInput ? toUtcDateTimeLocalValue(startInput.value) : '');
|
|
formData.append('entry_end_datetime[]', endInput ? toUtcDateTimeLocalValue(endInput.value) : '');
|
|
});
|
|
}
|
|
|
|
function bindRemove(row) {
|
|
var button = row.querySelector('[data-remove-timetable-entry]');
|
|
if (!button) {
|
|
return;
|
|
}
|
|
button.addEventListener('click', function () {
|
|
row.remove();
|
|
markDirty();
|
|
});
|
|
}
|
|
|
|
function addRow() {
|
|
var fragment = template.content.cloneNode(true);
|
|
var row = fragment.querySelector('[data-timetable-entry-row]');
|
|
if (!row) {
|
|
return;
|
|
}
|
|
bindRemove(row);
|
|
body.appendChild(fragment);
|
|
markDirty();
|
|
}
|
|
|
|
body.querySelectorAll('[data-timetable-entry-row]').forEach(function (row) {
|
|
bindRemove(row);
|
|
syncRowValuesToLocal(row);
|
|
});
|
|
|
|
form.addEventListener('formdata', function (event) {
|
|
syncFormDataToUtc(event.formData);
|
|
});
|
|
|
|
addButton.addEventListener('click', addRow);
|
|
}()); |