diff --git a/app/static/css/app.css b/app/static/css/app.css index ff68946..db2139d 100644 --- a/app/static/css/app.css +++ b/app/static/css/app.css @@ -1004,3 +1004,15 @@ body { .hidden { display: none !important; } + +/* Time text inputs (replacing type=time to avoid AM/PM) */ +.time-input { + font-variant-numeric: tabular-nums; + letter-spacing: 0.5px; + text-align: center; +} + +.time-input.input-error { + border-color: var(--danger) !important; + background: #fff5f5; +} diff --git a/app/static/index.html b/app/static/index.html index c9ee739..8e633f6 100644 --- a/app/static/index.html +++ b/app/static/index.html @@ -191,11 +191,11 @@
- +
- +
diff --git a/app/static/js/app.js b/app/static/js/app.js index 1a7624e..e72c0ea 100644 --- a/app/static/js/app.js +++ b/app/static/js/app.js @@ -293,8 +293,10 @@ const App = { const responsible = document.getElementById('modalBlockResponsible').value.trim() || null; const notes = document.getElementById('modalBlockNotes').value.trim() || null; + const timeRe = /^\d{2}:\d{2}$/; if (!title) { this.toast('Zadejte název bloku', 'error'); return; } if (!start || !end) { this.toast('Zadejte čas začátku a konce', 'error'); return; } + if (!timeRe.test(start) || !timeRe.test(end)) { this.toast('Neplatný formát času (HH:MM)', 'error'); return; } if (blockId) { // Edit existing block (no series expansion on edit — user edits only this one) @@ -370,7 +372,48 @@ const App = { // ─── Events ─────────────────────────────────────────────────────── + // ─── Time input helpers ─────────────────────────────────────────── + + _initTimeInput(el) { + // Auto-format: allow only digits + colon, insert ':' after 2 digits + el.addEventListener('input', (e) => { + let v = e.target.value.replace(/[^0-9:]/g, ''); + // Strip colons and rebuild + const digits = v.replace(/:/g, ''); + if (digits.length >= 3) { + v = digits.slice(0, 2) + ':' + digits.slice(2, 4); + } else { + v = digits; + } + e.target.value = v; + }); + el.addEventListener('blur', (e) => { + const v = e.target.value; + if (!v) return; + // Validate HH:MM format + if (!/^\d{2}:\d{2}$/.test(v)) { + e.target.classList.add('input-error'); + this.toast('Neplatný čas (formát HH:MM)', 'error'); + } else { + const [h, m] = v.split(':').map(Number); + if (h > 23 || m > 59) { + e.target.classList.add('input-error'); + this.toast('Neplatný čas (00:00–23:59)', 'error'); + } else { + e.target.classList.remove('input-error'); + } + } + }); + el.addEventListener('focus', (e) => { + e.target.classList.remove('input-error'); + }); + }, + bindEvents() { + // Init time inputs + this._initTimeInput(document.getElementById('modalBlockStart')); + this._initTimeInput(document.getElementById('modalBlockEnd')); + // Import JSON document.getElementById('importJsonInput').addEventListener('change', (e) => { if (e.target.files[0]) importJson(e.target.files[0]);