Files
pulse-signage/src/web/public/js/data-sources/timetable-group-form.js
T
2026-08-05 19:38:16 +01:00

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);
}());