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;
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>
</head>
<body>
@@ -355,11 +384,11 @@
</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>
<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-col">
@@ -367,8 +396,15 @@
<input type="date" id="event-start-date" autocomplete="off">
</div>
<div class="options-col">
<label for="event-start-time">Beginn Uhrzeit (24h):</label>
<input type="time" id="event-start-time" step="60" autocomplete="off">
<div class="time-24h-block">
<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 class="options-row">
@@ -377,8 +413,15 @@
<input type="date" id="event-end-date" autocomplete="off">
</div>
<div class="options-col">
<label for="event-end-time">Ende (optional) Uhrzeit (24h):</label>
<input type="time" id="event-end-time" step="60" autocomplete="off">
<div class="time-24h-block">
<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>
<p class="event-berlin-preview" id="event-berlin-preview" aria-live="polite"></p>
+108 -35
View File
@@ -10,9 +10,11 @@ document.addEventListener('DOMContentLoaded', function() {
const wifiPasswordInput = document.getElementById('wifi-password');
const eventTitleInput = document.getElementById('event-title');
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 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 eventDescriptionInput = document.getElementById('event-description');
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 hasEvent = eventTitleInput.value.trim().length > 0 ||
eventStartDateInput.value.length > 0 ||
eventStartTimeInput.value.length > 0 ||
eventStartHourInput.value !== '' ||
eventStartMinuteInput.value !== '' ||
eventEndDateInput.value.length > 0 ||
eventEndTimeInput.value.length > 0 ||
eventEndHourInput.value !== '' ||
eventEndMinuteInput.value !== '' ||
eventLocationInput.value.trim().length > 0 ||
eventDescriptionInput.value.trim().length > 0;
if (hasText || hasWifi || hasEvent) {
@@ -130,9 +134,11 @@ document.addEventListener('DOMContentLoaded', function() {
wifiPasswordInput.value = '';
eventTitleInput.value = '';
eventStartDateInput.value = '';
eventStartTimeInput.value = '';
eventStartHourInput.value = '';
eventStartMinuteInput.value = '';
eventEndDateInput.value = '';
eventEndTimeInput.value = '';
eventEndHourInput.value = '';
eventEndMinuteInput.value = '';
eventLocationInput.value = '';
eventDescriptionInput.value = '';
contentModeSelect.value = 'text';
@@ -187,12 +193,35 @@ document.addEventListener('DOMContentLoaded', function() {
}
eventStartDateInput.addEventListener('input', onEventDateTimeInput);
eventStartDateInput.addEventListener('change', onEventDateTimeInput);
eventStartTimeInput.addEventListener('input', onEventDateTimeInput);
eventStartTimeInput.addEventListener('change', onEventDateTimeInput);
eventStartHourInput.addEventListener('input', onEventDateTimeInput);
eventStartHourInput.addEventListener('change', onEventDateTimeInput);
eventStartMinuteInput.addEventListener('input', onEventDateTimeInput);
eventStartMinuteInput.addEventListener('change', onEventDateTimeInput);
eventEndDateInput.addEventListener('input', onEventDateTimeInput);
eventEndDateInput.addEventListener('change', onEventDateTimeInput);
eventEndTimeInput.addEventListener('input', onEventDateTimeInput);
eventEndTimeInput.addEventListener('change', onEventDateTimeInput);
eventEndHourInput.addEventListener('input', 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() {
generateQRCode();
toggleClearButton();
@@ -216,26 +245,39 @@ document.addEventListener('DOMContentLoaded', function() {
return /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/.test(s);
}
function combineDateTime(dateStr, timeStr) {
if (!dateStr || !timeStr) {
function combineDateTimeFromParts(dateStr, hourStr, minuteStr) {
if (!dateStr || hourStr === '' || minuteStr === '') {
return '';
}
if (!/^\d{4}-\d{2}-\d{2}$/.test(dateStr)) {
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 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)) {
return;
}
const parts = combined.split('T');
dateInput.value = parts[0];
timeInput.value = parts[1];
var splitT = combined.split('T');
var d = splitT[0];
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
@@ -279,13 +321,13 @@ document.addEventListener('DOMContentLoaded', function() {
if (urlParams.has('eventStart')) {
const es = urlParams.get('eventStart');
if (isValidDatetimeLocal(es)) {
applyCombinedToDateTimeInputs(es, eventStartDateInput, eventStartTimeInput);
applyCombinedToDateTimeInputs(es, eventStartDateInput, eventStartHourInput, eventStartMinuteInput);
}
}
if (urlParams.has('eventEnd')) {
const ee = urlParams.get('eventEnd');
if (isValidDatetimeLocal(ee)) {
applyCombinedToDateTimeInputs(ee, eventEndDateInput, eventEndTimeInput);
applyCombinedToDateTimeInputs(ee, eventEndDateInput, eventEndHourInput, eventEndMinuteInput);
}
}
if (urlParams.has('eventLocation')) {
@@ -387,15 +429,26 @@ document.addEventListener('DOMContentLoaded', function() {
eventBerlinPreview.textContent = '';
return;
}
const startRaw = combineDateTime(eventStartDateInput.value, eventStartTimeInput.value);
const startRaw = combineDateTimeFromParts(
eventStartDateInput.value,
eventStartHourInput.value,
eventStartMinuteInput.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 = '';
if (endD && endT) {
endRaw = combineDateTime(endD, endT);
} else if (endD || endT) {
if (endD && endTimeComplete) {
endRaw = combineDateTimeFromParts(endD, endH, endM);
} else if (endD && !endTimeComplete) {
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;
}
const sp = parseDatetimeLocalValue(startRaw);
@@ -526,9 +579,16 @@ document.addEventListener('DOMContentLoaded', function() {
if (mode === 'event') {
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 endT = eventEndTimeInput.value;
const endH = eventEndHourInput.value;
const endM = eventEndMinuteInput.value;
const endTimeComplete = endH !== '' && endM !== '';
const endTimeAny = endH !== '' || endM !== '';
const loc = eventLocationInput.value;
const desc = eventDescriptionInput.value;
@@ -539,20 +599,25 @@ document.addEventListener('DOMContentLoaded', function() {
return;
}
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';
qrcodeImg.style.display = 'none';
return;
}
if (endD || endT) {
if (!endD || !endT) {
if (endD && !endTimeComplete) {
errorMessage.textContent =
'Bitte geben Sie für das Ende sowohl Datum (TT/MM/JJJJ) als auch Uhrzeit (24h) an, oder lassen Sie beides leer';
'Für das Ende: bitte Stunde und Minute ergänzen (023 / 059) oder Ende leer lassen';
qrcodeCanvas.style.display = 'none';
qrcodeImg.style.display = 'none';
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);
@@ -566,7 +631,7 @@ document.addEventListener('DOMContentLoaded', function() {
const dtStart = wallTimeToICal(startParts);
let endParts;
const endRaw = endD && endT ? combineDateTime(endD, endT) : '';
const endRaw = endD && endTimeComplete ? combineDateTimeFromParts(endD, endH, endM) : '';
if (endRaw) {
endParts = parseDatetimeLocalValue(endRaw);
if (!endParts) {
@@ -740,8 +805,16 @@ document.addEventListener('DOMContentLoaded', function() {
if (eventTitleInput.value.trim()) {
params.set('eventTitle', eventTitleInput.value.trim());
}
const shareStart = combineDateTime(eventStartDateInput.value, eventStartTimeInput.value);
const shareEnd = combineDateTime(eventEndDateInput.value, eventEndTimeInput.value);
const shareStart = combineDateTimeFromParts(
eventStartDateInput.value,
eventStartHourInput.value,
eventStartMinuteInput.value
);
const shareEnd = combineDateTimeFromParts(
eventEndDateInput.value,
eventEndHourInput.value,
eventEndMinuteInput.value
);
if (shareStart) {
params.set('eventStart', shareStart);
}