fix: Termin-Uhrzeit ohne AM/PM (Stunde/Minute als Zahleneingabe)

Ersetzt type=time durch number 0–23 / 0–59, damit kein 12h-Picker mehr erscheint.
Validierung, URL-Parameter und Vorschau angepasst; lang de-DE im Termin-Block.

Made-with: Cursor
This commit is contained in:
2026-04-16 08:17:49 +02:00
parent d5cd4427d8
commit ca3ed44ff3
2 changed files with 162 additions and 46 deletions
+49 -6
View File
@@ -318,6 +318,35 @@
margin: -4px 0 12px 0; margin: -4px 0 12px 0;
line-height: 1.4; line-height: 1.4;
} }
.time-24h-block {
margin-bottom: 15px;
}
.time-24h-block .time-24h-row {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.time-24h-block input[type="number"] {
width: 4.25rem;
margin-bottom: 0;
text-align: center;
}
.time-24h-sep {
font-weight: 600;
color: #374151;
user-select: none;
}
.time-24h-hint {
font-size: 12px;
color: #6b7280;
margin-top: 4px;
}
</style> </style>
</head> </head>
<body> <body>
@@ -355,11 +384,11 @@
</div> </div>
</div> </div>
<div id="section-event" class="content-section" style="display: none;" lang="en-GB"> <div id="section-event" class="content-section" style="display: none;" lang="de-DE">
<label for="event-title">Titel:</label> <label for="event-title">Titel:</label>
<input type="text" id="event-title" placeholder="" autocomplete="off"> <input type="text" id="event-title" placeholder="" autocomplete="off">
<p class="event-datetime-hint">Datum im Format <strong>TT/MM/JJJJ</strong> (Kalenderfeld), Uhrzeit im <strong>24-Stunden-Format</strong> (keine AM/PM). Alle Zeiten: Ortszeit <strong>Europe/Berlin</strong>.</p> <p class="event-datetime-hint">Datum über das Kalenderfeld; Uhrzeit als <strong>Stunden 023</strong> und <strong>Minuten 059</strong> (reines 24h-Format, kein AM/PM). Ortszeit <strong>Europe/Berlin</strong>.</p>
<div class="options-row"> <div class="options-row">
<div class="options-col"> <div class="options-col">
@@ -367,8 +396,15 @@
<input type="date" id="event-start-date" autocomplete="off"> <input type="date" id="event-start-date" autocomplete="off">
</div> </div>
<div class="options-col"> <div class="options-col">
<label for="event-start-time">Beginn Uhrzeit (24h):</label> <div class="time-24h-block">
<input type="time" id="event-start-time" step="60" autocomplete="off"> <span class="label" style="display:block; margin-bottom:5px; font-weight:500; color:#374151;">Beginn Uhrzeit (24h):</span>
<div class="time-24h-row" role="group" aria-label="Beginn Uhrzeit 24 Stunden">
<input type="number" id="event-start-hour" min="0" max="23" step="1" placeholder="HH" inputmode="numeric" autocomplete="off" aria-label="Stunde Beginn, 0 bis 23">
<span class="time-24h-sep">:</span>
<input type="number" id="event-start-minute" min="0" max="59" step="1" placeholder="MM" inputmode="numeric" autocomplete="off" aria-label="Minute Beginn, 0 bis 59">
</div>
<p class="time-24h-hint">Stunde 023, Minute 059</p>
</div>
</div> </div>
</div> </div>
<div class="options-row"> <div class="options-row">
@@ -377,8 +413,15 @@
<input type="date" id="event-end-date" autocomplete="off"> <input type="date" id="event-end-date" autocomplete="off">
</div> </div>
<div class="options-col"> <div class="options-col">
<label for="event-end-time">Ende (optional) Uhrzeit (24h):</label> <div class="time-24h-block">
<input type="time" id="event-end-time" step="60" autocomplete="off"> <span class="label" style="display:block; margin-bottom:5px; font-weight:500; color:#374151;">Ende (optional) Uhrzeit (24h):</span>
<div class="time-24h-row" role="group" aria-label="Ende Uhrzeit 24 Stunden">
<input type="number" id="event-end-hour" min="0" max="23" step="1" placeholder="HH" inputmode="numeric" autocomplete="off" aria-label="Stunde Ende, 0 bis 23">
<span class="time-24h-sep">:</span>
<input type="number" id="event-end-minute" min="0" max="59" step="1" placeholder="MM" inputmode="numeric" autocomplete="off" aria-label="Minute Ende, 0 bis 59">
</div>
<p class="time-24h-hint">Stunde 023, Minute 059</p>
</div>
</div> </div>
</div> </div>
<p class="event-berlin-preview" id="event-berlin-preview" aria-live="polite"></p> <p class="event-berlin-preview" id="event-berlin-preview" aria-live="polite"></p>
+113 -40
View File
@@ -10,9 +10,11 @@ document.addEventListener('DOMContentLoaded', function() {
const wifiPasswordInput = document.getElementById('wifi-password'); const wifiPasswordInput = document.getElementById('wifi-password');
const eventTitleInput = document.getElementById('event-title'); const eventTitleInput = document.getElementById('event-title');
const eventStartDateInput = document.getElementById('event-start-date'); const eventStartDateInput = document.getElementById('event-start-date');
const eventStartTimeInput = document.getElementById('event-start-time'); const eventStartHourInput = document.getElementById('event-start-hour');
const eventStartMinuteInput = document.getElementById('event-start-minute');
const eventEndDateInput = document.getElementById('event-end-date'); const eventEndDateInput = document.getElementById('event-end-date');
const eventEndTimeInput = document.getElementById('event-end-time'); const eventEndHourInput = document.getElementById('event-end-hour');
const eventEndMinuteInput = document.getElementById('event-end-minute');
const eventLocationInput = document.getElementById('event-location'); const eventLocationInput = document.getElementById('event-location');
const eventDescriptionInput = document.getElementById('event-description'); const eventDescriptionInput = document.getElementById('event-description');
const sizeSelect = document.getElementById('size'); const sizeSelect = document.getElementById('size');
@@ -108,9 +110,11 @@ document.addEventListener('DOMContentLoaded', function() {
const hasWifi = wifiSsidInput.value.trim().length > 0 || wifiPasswordInput.value.trim().length > 0; const hasWifi = wifiSsidInput.value.trim().length > 0 || wifiPasswordInput.value.trim().length > 0;
const hasEvent = eventTitleInput.value.trim().length > 0 || const hasEvent = eventTitleInput.value.trim().length > 0 ||
eventStartDateInput.value.length > 0 || eventStartDateInput.value.length > 0 ||
eventStartTimeInput.value.length > 0 || eventStartHourInput.value !== '' ||
eventStartMinuteInput.value !== '' ||
eventEndDateInput.value.length > 0 || eventEndDateInput.value.length > 0 ||
eventEndTimeInput.value.length > 0 || eventEndHourInput.value !== '' ||
eventEndMinuteInput.value !== '' ||
eventLocationInput.value.trim().length > 0 || eventLocationInput.value.trim().length > 0 ||
eventDescriptionInput.value.trim().length > 0; eventDescriptionInput.value.trim().length > 0;
if (hasText || hasWifi || hasEvent) { if (hasText || hasWifi || hasEvent) {
@@ -130,9 +134,11 @@ document.addEventListener('DOMContentLoaded', function() {
wifiPasswordInput.value = ''; wifiPasswordInput.value = '';
eventTitleInput.value = ''; eventTitleInput.value = '';
eventStartDateInput.value = ''; eventStartDateInput.value = '';
eventStartTimeInput.value = ''; eventStartHourInput.value = '';
eventStartMinuteInput.value = '';
eventEndDateInput.value = ''; eventEndDateInput.value = '';
eventEndTimeInput.value = ''; eventEndHourInput.value = '';
eventEndMinuteInput.value = '';
eventLocationInput.value = ''; eventLocationInput.value = '';
eventDescriptionInput.value = ''; eventDescriptionInput.value = '';
contentModeSelect.value = 'text'; contentModeSelect.value = 'text';
@@ -187,12 +193,35 @@ document.addEventListener('DOMContentLoaded', function() {
} }
eventStartDateInput.addEventListener('input', onEventDateTimeInput); eventStartDateInput.addEventListener('input', onEventDateTimeInput);
eventStartDateInput.addEventListener('change', onEventDateTimeInput); eventStartDateInput.addEventListener('change', onEventDateTimeInput);
eventStartTimeInput.addEventListener('input', onEventDateTimeInput); eventStartHourInput.addEventListener('input', onEventDateTimeInput);
eventStartTimeInput.addEventListener('change', onEventDateTimeInput); eventStartHourInput.addEventListener('change', onEventDateTimeInput);
eventStartMinuteInput.addEventListener('input', onEventDateTimeInput);
eventStartMinuteInput.addEventListener('change', onEventDateTimeInput);
eventEndDateInput.addEventListener('input', onEventDateTimeInput); eventEndDateInput.addEventListener('input', onEventDateTimeInput);
eventEndDateInput.addEventListener('change', onEventDateTimeInput); eventEndDateInput.addEventListener('change', onEventDateTimeInput);
eventEndTimeInput.addEventListener('input', onEventDateTimeInput); eventEndHourInput.addEventListener('input', onEventDateTimeInput);
eventEndTimeInput.addEventListener('change', onEventDateTimeInput); eventEndHourInput.addEventListener('change', onEventDateTimeInput);
eventEndMinuteInput.addEventListener('input', onEventDateTimeInput);
eventEndMinuteInput.addEventListener('change', onEventDateTimeInput);
function clampTimeNumberInput(el, max) {
if (el.value === '') {
return;
}
var v = parseInt(el.value, 10);
if (isNaN(v)) {
return;
}
if (v < 0) {
el.value = '0';
} else if (v > max) {
el.value = String(max);
}
}
eventStartHourInput.addEventListener('blur', function() { clampTimeNumberInput(eventStartHourInput, 23); });
eventStartMinuteInput.addEventListener('blur', function() { clampTimeNumberInput(eventStartMinuteInput, 59); });
eventEndHourInput.addEventListener('blur', function() { clampTimeNumberInput(eventEndHourInput, 23); });
eventEndMinuteInput.addEventListener('blur', function() { clampTimeNumberInput(eventEndMinuteInput, 59); });
eventLocationInput.addEventListener('input', function() { eventLocationInput.addEventListener('input', function() {
generateQRCode(); generateQRCode();
toggleClearButton(); toggleClearButton();
@@ -216,26 +245,39 @@ document.addEventListener('DOMContentLoaded', function() {
return /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/.test(s); return /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/.test(s);
} }
function combineDateTime(dateStr, timeStr) { function combineDateTimeFromParts(dateStr, hourStr, minuteStr) {
if (!dateStr || !timeStr) { if (!dateStr || hourStr === '' || minuteStr === '') {
return ''; return '';
} }
if (!/^\d{4}-\d{2}-\d{2}$/.test(dateStr)) { if (!/^\d{4}-\d{2}-\d{2}$/.test(dateStr)) {
return ''; return '';
} }
if (!/^\d{2}:\d{2}$/.test(timeStr)) { var h = parseInt(String(hourStr).trim(), 10);
var mi = parseInt(String(minuteStr).trim(), 10);
if (isNaN(h) || isNaN(mi)) {
return ''; return '';
} }
return dateStr + 'T' + timeStr; if (h < 0 || h > 23 || mi < 0 || mi > 59) {
return '';
}
var pad = function(n) { return String(n).padStart(2, '0'); };
return dateStr + 'T' + pad(h) + ':' + pad(mi);
} }
function applyCombinedToDateTimeInputs(combined, dateInput, timeInput) { function applyCombinedToDateTimeInputs(combined, dateInput, hourInput, minuteInput) {
if (!isValidDatetimeLocal(combined)) { if (!isValidDatetimeLocal(combined)) {
return; return;
} }
const parts = combined.split('T'); var splitT = combined.split('T');
dateInput.value = parts[0]; var d = splitT[0];
timeInput.value = parts[1]; var t = splitT[1];
var tm = /^(\d{2}):(\d{2})$/.exec(t);
if (!tm) {
return;
}
dateInput.value = d;
hourInput.value = String(parseInt(tm[1], 10));
minuteInput.value = String(parseInt(tm[2], 10));
} }
// Or check for URL parameters if present // Or check for URL parameters if present
@@ -279,13 +321,13 @@ document.addEventListener('DOMContentLoaded', function() {
if (urlParams.has('eventStart')) { if (urlParams.has('eventStart')) {
const es = urlParams.get('eventStart'); const es = urlParams.get('eventStart');
if (isValidDatetimeLocal(es)) { if (isValidDatetimeLocal(es)) {
applyCombinedToDateTimeInputs(es, eventStartDateInput, eventStartTimeInput); applyCombinedToDateTimeInputs(es, eventStartDateInput, eventStartHourInput, eventStartMinuteInput);
} }
} }
if (urlParams.has('eventEnd')) { if (urlParams.has('eventEnd')) {
const ee = urlParams.get('eventEnd'); const ee = urlParams.get('eventEnd');
if (isValidDatetimeLocal(ee)) { if (isValidDatetimeLocal(ee)) {
applyCombinedToDateTimeInputs(ee, eventEndDateInput, eventEndTimeInput); applyCombinedToDateTimeInputs(ee, eventEndDateInput, eventEndHourInput, eventEndMinuteInput);
} }
} }
if (urlParams.has('eventLocation')) { if (urlParams.has('eventLocation')) {
@@ -387,15 +429,26 @@ document.addEventListener('DOMContentLoaded', function() {
eventBerlinPreview.textContent = ''; eventBerlinPreview.textContent = '';
return; return;
} }
const startRaw = combineDateTime(eventStartDateInput.value, eventStartTimeInput.value); const startRaw = combineDateTimeFromParts(
eventStartDateInput.value,
eventStartHourInput.value,
eventStartMinuteInput.value
);
const endD = eventEndDateInput.value; const endD = eventEndDateInput.value;
const endT = eventEndTimeInput.value; const endH = eventEndHourInput.value;
const endM = eventEndMinuteInput.value;
const endTimeComplete = endH !== '' && endM !== '';
const endTimeAny = endH !== '' || endM !== '';
let endRaw = ''; let endRaw = '';
if (endD && endT) { if (endD && endTimeComplete) {
endRaw = combineDateTime(endD, endT); endRaw = combineDateTimeFromParts(endD, endH, endM);
} else if (endD || endT) { } else if (endD && !endTimeComplete) {
eventBerlinPreview.textContent = eventBerlinPreview.textContent =
'Bitte geben Sie für das Ende entweder beides (Datum und Uhrzeit, 24h) ein oder lassen Sie beides leer.'; 'Bitte geben Sie für das Ende Datum sowie Stunde (023) und Minute (059) an, oder lassen Sie das Ende vollständig leer.';
return;
} else if (!endD && endTimeAny) {
eventBerlinPreview.textContent =
'Bitte geben Sie für das Ende ein Datum ein, oder entfernen Sie Stunde/Minute.';
return; return;
} }
const sp = parseDatetimeLocalValue(startRaw); const sp = parseDatetimeLocalValue(startRaw);
@@ -526,9 +579,16 @@ document.addEventListener('DOMContentLoaded', function() {
if (mode === 'event') { if (mode === 'event') {
const title = eventTitleInput.value.trim(); const title = eventTitleInput.value.trim();
const startRaw = combineDateTime(eventStartDateInput.value, eventStartTimeInput.value); const startRaw = combineDateTimeFromParts(
eventStartDateInput.value,
eventStartHourInput.value,
eventStartMinuteInput.value
);
const endD = eventEndDateInput.value; const endD = eventEndDateInput.value;
const endT = eventEndTimeInput.value; const endH = eventEndHourInput.value;
const endM = eventEndMinuteInput.value;
const endTimeComplete = endH !== '' && endM !== '';
const endTimeAny = endH !== '' || endM !== '';
const loc = eventLocationInput.value; const loc = eventLocationInput.value;
const desc = eventDescriptionInput.value; const desc = eventDescriptionInput.value;
@@ -539,20 +599,25 @@ document.addEventListener('DOMContentLoaded', function() {
return; return;
} }
if (!startRaw) { if (!startRaw) {
errorMessage.textContent = 'Bitte geben Sie Datum und Uhrzeit (24h) für den Beginn ein (TT/MM/JJJJ)'; errorMessage.textContent =
'Bitte geben Sie Datum sowie Stunde (023) und Minute (059) für den Beginn ein';
qrcodeCanvas.style.display = 'none'; qrcodeCanvas.style.display = 'none';
qrcodeImg.style.display = 'none'; qrcodeImg.style.display = 'none';
return; return;
} }
if (endD || endT) { if (endD && !endTimeComplete) {
if (!endD || !endT) { errorMessage.textContent =
errorMessage.textContent = 'Für das Ende: bitte Stunde und Minute ergänzen (023 / 059) oder Ende leer lassen';
'Bitte geben Sie für das Ende sowohl Datum (TT/MM/JJJJ) als auch Uhrzeit (24h) an, oder lassen Sie beides leer'; qrcodeCanvas.style.display = 'none';
qrcodeCanvas.style.display = 'none'; qrcodeImg.style.display = 'none';
qrcodeImg.style.display = 'none'; return;
return; }
} if (!endD && endTimeAny) {
errorMessage.textContent = 'Für das Ende bitte zuerst ein Datum wählen oder Zeitfelder leeren';
qrcodeCanvas.style.display = 'none';
qrcodeImg.style.display = 'none';
return;
} }
const startParts = parseDatetimeLocalValue(startRaw); const startParts = parseDatetimeLocalValue(startRaw);
@@ -566,7 +631,7 @@ document.addEventListener('DOMContentLoaded', function() {
const dtStart = wallTimeToICal(startParts); const dtStart = wallTimeToICal(startParts);
let endParts; let endParts;
const endRaw = endD && endT ? combineDateTime(endD, endT) : ''; const endRaw = endD && endTimeComplete ? combineDateTimeFromParts(endD, endH, endM) : '';
if (endRaw) { if (endRaw) {
endParts = parseDatetimeLocalValue(endRaw); endParts = parseDatetimeLocalValue(endRaw);
if (!endParts) { if (!endParts) {
@@ -740,8 +805,16 @@ document.addEventListener('DOMContentLoaded', function() {
if (eventTitleInput.value.trim()) { if (eventTitleInput.value.trim()) {
params.set('eventTitle', eventTitleInput.value.trim()); params.set('eventTitle', eventTitleInput.value.trim());
} }
const shareStart = combineDateTime(eventStartDateInput.value, eventStartTimeInput.value); const shareStart = combineDateTimeFromParts(
const shareEnd = combineDateTime(eventEndDateInput.value, eventEndTimeInput.value); eventStartDateInput.value,
eventStartHourInput.value,
eventStartMinuteInput.value
);
const shareEnd = combineDateTimeFromParts(
eventEndDateInput.value,
eventEndHourInput.value,
eventEndMinuteInput.value
);
if (shareStart) { if (shareStart) {
params.set('eventStart', shareStart); params.set('eventStart', shareStart);
} }