Add timetable end-time validation

This commit is contained in:
2026-08-08 00:00:51 +01:00
parent 0b300d6ddb
commit bcae4bb318
5 changed files with 313 additions and 1 deletions
@@ -47,6 +47,101 @@
return Number.isNaN(date.getTime()) ? null : date;
}
function parseDateTimeLocalValue(value) {
var raw = String(value || '').trim();
if (!raw) {
return null;
}
var date = new Date(raw);
return Number.isNaN(date.getTime()) ? null : date;
}
function clearFieldValidity(input) {
if (!input) {
return;
}
input.setCustomValidity('');
input.classList.remove('is-invalid');
if (input.removeAttribute) {
input.removeAttribute('aria-invalid');
}
}
function setFieldValidity(input, message) {
if (!input) {
return;
}
input.setCustomValidity(message);
input.classList.add('is-invalid');
if (input.setAttribute) {
input.setAttribute('aria-invalid', 'true');
}
}
function validateEntryRow(row) {
if (!row) {
return;
}
var startInput = row.querySelector('[name="entry_start_datetime[]"]');
var endInput = row.querySelector('[name="entry_end_datetime[]"]');
var startValue;
var endValue;
var startDate;
var endDate;
clearFieldValidity(endInput);
if (!startInput || !endInput) {
return;
}
startValue = String(startInput.value || '').trim();
endValue = String(endInput.value || '').trim();
if (!startValue || !endValue) {
return;
}
startDate = parseDateTimeLocalValue(startValue);
endDate = parseDateTimeLocalValue(endValue);
if (!startDate || !endDate) {
return;
}
if (endDate.getTime() < startDate.getTime() + 60000) {
setFieldValidity(endInput, 'End time must be at least 1 minute after the start time.');
}
}
function bindRowValidation(row) {
if (!row) {
return;
}
var startInput = row.querySelector('[name="entry_start_datetime[]"]');
var endInput = row.querySelector('[name="entry_end_datetime[]"]');
if (!startInput || !endInput) {
return;
}
function handleValidation() {
validateEntryRow(row);
}
startInput.addEventListener('input', handleValidation);
startInput.addEventListener('change', handleValidation);
endInput.addEventListener('input', handleValidation);
endInput.addEventListener('change', handleValidation);
validateEntryRow(row);
}
function toUtcDateTimeLocalValue(value) {
var localDate = new Date(String(value || '').trim());
return Number.isNaN(localDate.getTime()) ? '' : localDate.toISOString();
@@ -110,6 +205,7 @@
return;
}
bindRemove(row);
bindRowValidation(row);
body.appendChild(fragment);
markDirty();
}
@@ -117,6 +213,7 @@
body.querySelectorAll('[data-timetable-entry-row]').forEach(function (row) {
bindRemove(row);
syncRowValuesToLocal(row);
bindRowValidation(row);
});
form.addEventListener('formdata', function (event) {
@@ -59,6 +59,7 @@
</td>
<td>
<input type="datetime-local" name="entry_end_datetime[]" class="form-control" value="{{endValue}}" />
<div class="invalid-feedback">End time must be at least 1 minute after the start time.</div>
</td>
<td>
<button type="button" class="btn btn-outline-danger btn-sm" data-remove-timetable-entry>Remove</button>
@@ -86,6 +87,7 @@
</td>
<td>
<input type="datetime-local" name="entry_end_datetime[]" class="form-control" value="" />
<div class="invalid-feedback">End time must be at least 1 minute after the start time.</div>
</td>
<td>
<button type="button" class="btn btn-outline-danger btn-sm" data-remove-timetable-entry>Remove</button>