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]);