From 66965ef448fdb218e815046e31b10b780bef68b3 Mon Sep 17 00:00:00 2001 From: Martin Renvoize Date: Sat, 3 Jan 2026 07:34:09 +0000 Subject: [PATCH] Bug 37707: (follow-up) Add textual feedback for booking lead/trail period conflicts This patch adds a feedback message container below the calendar that provides contextual information about lead/trail period requirements and conflicts improving accessibility. The feedback updates dynamically on hover, showing: - Info messages when hovering selectable dates - Error messages when hovering dates disabled due to conflicts - Specific explanations distinguishing between conflict types (e.g., new lead vs existing trail, new trail vs existing lead) This complements the visual color indicators by providing explicit textual explanations, improving accessibility and user understanding. Test plan: 1. Apply all patches and run: yarn build 2. Configure a bookable item with 2-day lead and 3-day trail 3. Create an existing booking for January 10-15 (protected: Jan 8-18) 4. Open the booking calendar and observe feedback area below calendar 5. Hover over Jan 12 (part of existing booking) - Verify message: "This date is part of an existing booking" 6. Hover over Jan 8 (existing booking's lead period) - Verify message explains it's part of the lead period 7. Try to select Jan 19-20 as booking dates (2-day lead = Jan 17-18) - Hover over Jan 19 when it appears disabled - Verify error message: "conflicts with existing booking's trail period" 8. Hover over Jan 21 (no conflicts) - Verify info message about lead/trail requirements 9. Verify all feedback messages are clear and helpful for librarians --- .../prog/js/modals/place_booking.js | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/koha-tmpl/intranet-tmpl/prog/js/modals/place_booking.js b/koha-tmpl/intranet-tmpl/prog/js/modals/place_booking.js index dc04dc17d49..35aae46c047 100644 --- a/koha-tmpl/intranet-tmpl/prog/js/modals/place_booking.js +++ b/koha-tmpl/intranet-tmpl/prog/js/modals/place_booking.js @@ -922,6 +922,18 @@ $("#placeBookingModal").on("show.bs.modal", function (e) { ); } + // Create feedback message container below the calendar + let feedbackDiv = periodPicker.calendarContainer.querySelector( + ".booking-conflict-feedback" + ); + if (!feedbackDiv) { + feedbackDiv = document.createElement("div"); + feedbackDiv.className = "booking-conflict-feedback"; + feedbackDiv.style.cssText = + "padding: 8px 12px; margin-top: 8px; border-radius: 4px; font-size: 13px; min-height: 20px; text-align: center;"; + periodPicker.calendarContainer.appendChild(feedbackDiv); + } + // Add hints for days before the start range and after the end range periodPicker.calendarContainer.addEventListener( "mouseover", @@ -1291,6 +1303,98 @@ $("#placeBookingModal").on("show.bs.modal", function (e) { true ); } + + // Update feedback message + const feedbackDiv = + periodPicker.calendarContainer.querySelector( + ".booking-conflict-feedback" + ); + if (feedbackDiv) { + let message = ""; + let messageType = "info"; // info, warning, error + + if (leadDisable || trailDisable) { + messageType = "error"; + if ( + leadDisable && + !periodPicker.selectedDates[0] + ) { + // Check what type of conflict + if ( + target.classList.contains( + "existingBookingTrail" + ) + ) { + message = `Cannot select: Lead period (${leadDays} days before start) conflicts with existing booking's processing time`; + } else { + message = `Cannot select: Lead period (${leadDays} days before start) conflicts with existing booking`; + } + } else if (trailDisable) { + // Check what type of conflict + if ( + target.classList.contains( + "existingBookingLead" + ) + ) { + message = `Cannot select: Processing period (${trailDays} days after return) conflicts with existing booking's preparation time`; + } else { + message = `Cannot select: Processing period (${trailDays} days after return) conflicts with existing booking`; + } + } + } else { + // Show helpful info about lead/trail periods + if (!periodPicker.selectedDates[0]) { + message = `Selecting start date. Lead period: ${leadDays} days for item preparation`; + messageType = "info"; + } else { + message = `Selecting end date. Processing period: ${trailDays} days after return`; + messageType = "info"; + } + + // Show if periods are visible + if ( + target.classList.contains( + "existingBookingLead" + ) + ) { + message += + " • Hovering existing booking preparation period"; + } else if ( + target.classList.contains( + "existingBookingTrail" + ) + ) { + message += + " • Hovering existing booking processing period"; + } + } + + feedbackDiv.textContent = message; + feedbackDiv.style.display = message + ? "block" + : "none"; + + // Style based on message type + if (messageType === "error") { + feedbackDiv.style.backgroundColor = + "#f8d7da"; + feedbackDiv.style.color = "#721c24"; + feedbackDiv.style.border = + "1px solid #f5c6cb"; + } else if (messageType === "warning") { + feedbackDiv.style.backgroundColor = + "#fff3cd"; + feedbackDiv.style.color = "#856404"; + feedbackDiv.style.border = + "1px solid #ffeeba"; + } else { + feedbackDiv.style.backgroundColor = + "#d1ecf1"; + feedbackDiv.style.color = "#0c5460"; + feedbackDiv.style.border = + "1px solid #bee5eb"; + } + } } } ); -- 2.52.0