From 016ce9397993ce9841dd109c2fd3b27e6dfa6518 Mon Sep 17 00:00:00 2001 From: Kenearos Date: Tue, 12 May 2026 18:27:06 +0200 Subject: [PATCH] feat: add date-stepper buttons (Feature C) clamped to selected month --- app.js | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- index.html | 6 +++- styles.css | 26 ++++++++++++++++ 3 files changed, 120 insertions(+), 3 deletions(-) diff --git a/app.js b/app.js index 01d6aa7..0caab40 100644 --- a/app.js +++ b/app.js @@ -43,8 +43,13 @@ class DienstplanApp { // Duty management document.getElementById('add-duty-btn').addEventListener('click', () => this.addDuty()); document.getElementById('employee-select-duty').addEventListener('change', () => this.loadDutiesForSelectedEmployee()); - document.getElementById('month-select').addEventListener('change', () => this.loadDutiesForSelectedEmployee()); - document.getElementById('year-select').addEventListener('change', () => this.loadDutiesForSelectedEmployee()); + + // Date stepper buttons (Feature C) + document.getElementById('duty-date-prev').addEventListener('click', () => this.stepDutyDate(-1)); + document.getElementById('duty-date-next').addEventListener('click', () => this.stepDutyDate(+1)); + document.getElementById('duty-date').addEventListener('change', () => this.updateDateStepperState()); + document.getElementById('month-select').addEventListener('change', () => this.onDutyMonthChange()); + document.getElementById('year-select').addEventListener('change', () => this.onDutyMonthChange()); // Calculation document.getElementById('calculate-btn').addEventListener('click', () => this.calculateBonuses()); @@ -104,6 +109,8 @@ class DienstplanApp { // Set date input to today const today = new Date().toISOString().split('T')[0]; document.getElementById('duty-date').value = today; + + this.updateDateStepperState(); } /** @@ -265,6 +272,86 @@ class DienstplanApp { this.loadDutiesForSelectedEmployee(); } + /** + * Step the duty-date input by +/-1 day, clamped to the currently selected month. + */ + stepDutyDate(delta) { + const dateInput = document.getElementById('duty-date'); + const monthSelect = document.getElementById('month-select'); + const yearSelect = document.getElementById('year-select'); + const month = parseInt(monthSelect.value); + const year = parseInt(yearSelect.value); + const lastDay = new Date(year, month, 0).getDate(); + + if (!dateInput.value) { + // Initialize to 1st of the selected month + dateInput.value = `${year}-${String(month).padStart(2, '0')}-01`; + this.updateDateStepperState(); + return; + } + const cur = new Date(dateInput.value + 'T12:00:00'); + // If outside selected month, snap to 1st + const inMonth = (cur.getFullYear() === year) && ((cur.getMonth() + 1) === month); + if (!inMonth) { + dateInput.value = `${year}-${String(month).padStart(2, '0')}-01`; + this.updateDateStepperState(); + return; + } + const curDay = cur.getDate(); + const newDay = curDay + delta; + if (newDay < 1 || newDay > lastDay) return; // clamp + const newDate = new Date(year, month - 1, newDay, 12, 0, 0); + const yyyy = newDate.getFullYear(); + const mm = String(newDate.getMonth() + 1).padStart(2, '0'); + const dd = String(newDate.getDate()).padStart(2, '0'); + dateInput.value = `${yyyy}-${mm}-${dd}`; + this.updateDateStepperState(); + } + + /** + * Update the disabled state of the stepper buttons based on current date / month. + */ + updateDateStepperState() { + const dateInput = document.getElementById('duty-date'); + const monthSelect = document.getElementById('month-select'); + const yearSelect = document.getElementById('year-select'); + const prevBtn = document.getElementById('duty-date-prev'); + const nextBtn = document.getElementById('duty-date-next'); + if (!dateInput || !prevBtn || !nextBtn) return; + + const month = parseInt(monthSelect.value); + const year = parseInt(yearSelect.value); + const lastDay = new Date(year, month, 0).getDate(); + + if (!dateInput.value) { + prevBtn.disabled = false; + nextBtn.disabled = false; + return; + } + const cur = new Date(dateInput.value + 'T12:00:00'); + const inSelectedMonth = (cur.getFullYear() === year) && ((cur.getMonth() + 1) === month); + if (!inSelectedMonth) { + prevBtn.disabled = false; + nextBtn.disabled = false; + return; + } + prevBtn.disabled = cur.getDate() <= 1; + nextBtn.disabled = cur.getDate() >= lastDay; + } + + /** + * Handle month/year change in the duty tab: set date to 1st of new month, refresh list, refresh stepper. + */ + onDutyMonthChange() { + const monthSelect = document.getElementById('month-select'); + const yearSelect = document.getElementById('year-select'); + const month = parseInt(monthSelect.value); + const year = parseInt(yearSelect.value); + document.getElementById('duty-date').value = `${year}-${String(month).padStart(2, '0')}-01`; + this.updateDateStepperState(); + this.loadDutiesForSelectedEmployee(); + } + /** * Load duties for the selected employee and month */ diff --git a/index.html b/index.html index 2b43ebe..92dbdf2 100644 --- a/index.html +++ b/index.html @@ -69,7 +69,11 @@
- +
+ + + +
diff --git a/styles.css b/styles.css index 706eda2..055b387 100644 --- a/styles.css +++ b/styles.css @@ -658,3 +658,29 @@ header h1 { color: #dc3545; font-weight: 600; } + +/* === Date Stepper === */ +.date-stepper { + display: grid; + grid-template-columns: auto 1fr auto; + gap: 6px; + align-items: stretch; +} + +.date-stepper input[type="date"] { + /* override the .form-group width */ + width: 100%; +} + +.date-stepper button { + padding: 0 14px; + margin: 0; + font-size: 1.2rem; + line-height: 1; + min-width: 44px; +} + +.date-stepper button:disabled { + opacity: 0.4; + cursor: not-allowed; +}