/** * Hours Copy Extension for form.min.js * Adds copy functionality to hours forms with modal interface * * Dependencies: form.min.js, modal.min.js, utility functions (targetCheck, getIcon) */ class HoursCopyExtension { constructor() { this.createModal(); this.setupModal(); this.currentSourceDay = null; this.days = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday']; this.init(); } handleChange(e) { if (window.targetCheck('.hours-copy-btn')) { e.preventDefault({bubbles: true}); } } init() { this.addCopyButtons(); this.bindEvents(); } createModal() { this.modalElement = document.querySelector('dialog#copy-hours'); } setupModal() { this.modal = new window.jvbModal( this.modalElement, { open: '.hours-copy-btn', openMessage: "Copy hours modal opened", closeMessage: "Copy hours modal closed", } ); this.modal.subscribe((event, data) => { switch(event) { case 'modal-open': let e = data.event; let modal = data.modal; const dayGroup = e.target.closest('.group:not([data-field="openingHours"])'); const dayName = this.getDayNameFromGroup(dayGroup); if (!dayName) return; this.currentSourceDay = dayName; // Get source day hours const sourceData = this.getSourceDayData(dayGroup); // Update source day info const sourceDayEl = modal.querySelector('.source-day'); const sourceHoursEl = modal.querySelector('.source-hours'); if (sourceDayEl) { sourceDayEl.textContent = window.uppercaseFirst ? window.uppercaseFirst(dayName) : dayName.charAt(0).toUpperCase() + dayName.slice(1); } if (sourceHoursEl) { if (sourceData.isOpen && sourceData.opens && sourceData.closes) { sourceHoursEl.textContent = `${sourceData.opens} - ${sourceData.closes}`; sourceHoursEl.className = 'source-hours'; } else { sourceHoursEl.textContent = 'Closed'; sourceHoursEl.className = 'source-hours closed'; } } this.modal.modal.querySelector(`label[for=copy-to-${dayName}]`).hidden = true; this.modal.modal.querySelectorAll(`label:not([for=copy-to-${dayName}])[hidden]`).hidden = false; break; case 'modal-close': this.applyCopyHours() this.currentSourceDay = null; break; } }); } addCopyButtons() { // Add copy buttons to each day group const dayGroups = document.querySelectorAll('[data-field="openingHours"] .group.field'); dayGroups.forEach(group => { if (!group.querySelector('.hours-copy-btn')) { const copyBtn = this.createCopyButton(); group.append(copyBtn); } }); } createCopyButton() { const button = window.getTemplate('blankButton'); button.type = 'button'; button.className = 'hours-copy-btn'; button.title = 'Copy this time to other days'; button.prepend(window.getIcon('copy')); button.setAttribute('data-action', 'copy-hours'); return button; } bindEvents() { this.changeHandler = this.handleChange.bind(this); this.modal.modal.addEventListener('change', this.changeHandler); } getDayNameFromGroup(group) { return group.dataset.field.replace('openingHours|',''); } getSourceDayData(dayGroup) { const openCheckbox = dayGroup.querySelector(`input[type="checkbox"][name="openingHours|${this.currentSourceDay}|isOpen"]`); const opensInput = dayGroup.querySelector(`input[name="openingHours|${this.currentSourceDay}|opens"]`); const closesInput = dayGroup.querySelector(`input[name="openingHours|${this.currentSourceDay}|closes"]`); return { isOpen: openCheckbox ? openCheckbox.checked : false, opens: opensInput ? opensInput.value : '', closes: closesInput ? closesInput.value : '' }; } applyCopyHours() { if (!this.currentSourceDay) return; const sourceGroup = document.querySelector(`.group[data-field="openingHours|${this.currentSourceDay}"]`); if (!sourceGroup) return; const sourceData = this.getSourceDayData(sourceGroup); // Get selected target days const selectedDays = Array.from( this.modal.modal.querySelectorAll('input:checked') ).map(checkbox => checkbox.value); if (selectedDays.length === 0) { alert('Please select at least one day to copy to.'); return; } // Apply hours to selected days selectedDays.forEach(day => { this.applyHoursToDay(day, sourceData); }); // Show success feedback this.showSuccessFeedback(selectedDays.length); } applyHoursToDay(dayName, sourceData) { const targetGroup = document.querySelector(`.group[data-field="openingHours|${dayName}`); if (!targetGroup) return; const openCheckbox = targetGroup.querySelector(`input[type="checkbox"][name="openingHours|${dayName}|isOpen"]`); const opensInput = targetGroup.querySelector(`input[name="openingHours|${dayName}|opens"]`); const closesInput = targetGroup.querySelector(`input[name="openingHours|${dayName}|closes"]`); if (!openCheckbox) return; // Set checkbox state openCheckbox.checked = sourceData.isOpen; openCheckbox.dispatchEvent(new Event('change', {bubbles: true})); // Set time values if open if (sourceData.isOpen && opensInput && closesInput) { opensInput.value = sourceData.opens; opensInput.dispatchEvent(new Event('change', {bubbles: true})); closesInput.value = sourceData.closes; closesInput.dispatchEvent(new Event('change', {bubbles: true})); } else if (opensInput && closesInput) { opensInput.value = ''; opensInput.dispatchEvent(new Event('change', {bubbles: true})); closesInput.value = ''; closesInput.dispatchEvent(new Event('change', {bubbles: true})); } } showSuccessFeedback(count) { // Create or update feedback element let feedback = document.getElementById('copy-success-feedback'); const message = count === 1 ? 'Hours copied to 1 day successfully!' : `Hours copied to ${count} days successfully!`; feedback.querySelector('span').textContent = message; // Show feedback feedback.classList.add('show'); // Hide after delay window.debouncer.schedule('copy-success', () => { feedback.classList.remove('show'); }, 3000); // Use accessibility announcements if available if (window.jvbA11y && window.jvbA11y.announce) { window.jvbA11y.announce(message); } } } document.addEventListener('DOMContentLoaded', () => { window.jvbCopy = new HoursCopyExtension(); });