1(function ($) { 2 'use strict'; 3 4 /** 5 * Validate special takeaway pickup slots 6 * Ensures end_datetime is on the same day and after start_datetime 7 */ 8 function initSpecialSlotsValidation() { 9 // Look for repeater rows containing both start_datetime and end_datetime 10 const validateDatetimeFields = function () { 11 $('.acf-repeater').each(function () { 12 const $repeater = $(this); 13 const $rows = $repeater.find('.acf-row:not(.acf-clone)'); 14 15 $rows.each(function () { 16 const $row = $(this); 17 const $startField = $row.find('[data-name="start_datetime"]'); 18 const $endField = $row.find('[data-name="end_datetime"]'); 19 20 if ($startField.length && $endField.length) { 21 // Find the actual input elements 22 const $startInput = $startField.find('input[type="datetime-local"], input[type="text"]').first(); 23 const $endInput = $endField.find('input[type="datetime-local"], input[type="text"]').first(); 24 25 if ($startInput.length && $endInput.length) { 26 // Add validation on blur 27 $startInput.off('blur').on('blur', function () { 28 validateEndDateTime($row, $startInput, $endInput, $endField); 29 }); 30 31 $endInput.off('blur').on('blur', function () { 32 validateEndDateTime($row, $startInput, $endInput, $endField); 33 }); 34 35 // Add change event for real-time validation 36 $startInput.off('change').on('change', function () { 37 validateEndDateTime($row, $startInput, $endInput, $endField); 38 }); 39 40 $endInput.off('change').on('change', function () { 41 validateEndDateTime($row, $startInput, $endInput, $endField); 42 }); 43 } 44 } 45 }); 46 }); 47 }; 48 49 const validateEndDateTime = function ($row, $startInput, $endInput, $endFieldWrapper) { 50 const startValue = $startInput.val(); 51 const endValue = $endInput.val(); 52 53 // Skip if either field is empty 54 if (!startValue || !endValue) { 55 clearValidationError($row, $endFieldWrapper); 56 return; 57 } 58 59 try { 60 // Parse datetime values 61 // ACF date_time_picker can return various formats (datetime-local or formatted string) 62 const startDate = parseDateTime(startValue); 63 const endDate = parseDateTime(endValue); 64 65 if (!startDate || !endDate) { 66 clearValidationError($row, $endFieldWrapper); 67 return; 68 } 69 70 // Check if dates are on the same day 71 const startDay = startDate.toDateString(); 72 const endDay = endDate.toDateString(); 73 74 if (startDay !== endDay) { 75 showValidationError( 76 $row, 77 $endFieldWrapper, 78 ajiAcfSpecialSlotsText?.same_day_error || 79 'La date de fin doit être le même jour que la date de début' 80 ); 81 return; 82 } 83 84 // Check if end time is after start time 85 if (endDate <= startDate) { 86 showValidationError( 87 $row, 88 $endFieldWrapper, 89 ajiAcfSpecialSlotsText?.end_after_start_error || 90 'L\'heure de fin doit être après l\'heure de début' 91 ); 92 return; 93 } 94 95 // All validations passed 96 clearValidationError($row, $endFieldWrapper); 97 } catch (error) { 98 console.warn('Error validating special slots datetime:', error); 99 clearValidationError($row, $endFieldWrapper); 100 } 101 }; 102 103 const parseDateTime = function (value) { 104 if (!value) return null; 105 106 // Try parsing as ISO datetime format (datetime-local input type) 107 let date = new Date(value); 108 if (!isNaN(date.getTime())) { 109 return date; 110 } 111 112 // Try parsing common datetime formats 113 // Format: d/m/Y H:i (e.g., "15/11/2025 14:30") 114 const formats = [ 115 { regex: /^(\d{2})\/(\d{2})\/(\d{4})\s(\d{2}):(\d{2})$/, groups: { day: 1, month: 2, year: 3, hour: 4, min: 5 } }, 116 // Format: Y-m-d H:i (e.g., "2025-11-15 14:30") 117 { regex: /^(\d{4})-(\d{2})-(\d{2})\s(\d{2}):(\d{2})$/, groups: { year: 1, month: 2, day: 3, hour: 4, min: 5 } } 118 ]; 119 120 for (let format of formats) { 121 const match = value.match(format.regex); 122 if (match) { 123 const d = match[format.groups.day]; 124 const m = match[format.groups.month]; 125 const y = match[format.groups.year]; 126 const h = match[format.groups.hour]; 127 const min = match[format.groups.min]; 128 129 date = new Date(parseInt(y), parseInt(m) - 1, parseInt(d), parseInt(h), parseInt(min)); 130 if (!isNaN(date.getTime())) { 131 return date; 132 } 133 } 134 } 135 136 return null; 137 }; 138 139 const showValidationError = function ($row, $endFieldWrapper, message) { 140 // Remove existing error if any 141 $endFieldWrapper.find('.aji-slot-validation-error').remove(); 142 143 // Add error message inside end_datetime field 144 const $error = $(`<div class="aji-slot-validation-error" style="color: #e74c3c; font-size: 12px; margin-top: 5px; font-weight: bold; display: flex; align-items: center; gap: 6px;"> 145 ${message} 146 </div>`); 147 148 $endFieldWrapper.append($error); 149 $row.addClass('aji-slot-has-error'); 150 }; 151 152 const clearValidationError = function ($row, $endFieldWrapper) { 153 $endFieldWrapper.find('.aji-slot-validation-error').remove(); 154 $row.removeClass('aji-slot-has-error'); 155 }; 156 157 // Initial validation on page load 158 validateDatetimeFields(); 159 160 // Re-validate when new rows are added to repeater 161 $(document).on('acf/insert_row', function () { 162 setTimeout(validateDatetimeFields, 100); 163 }); 164 165 // Also watch for changes via ACF field update 166 if (window.acf) { 167 acf.addAction('render_field', function () { 168 validateDatetimeFields(); 169 }); 170 } 171 } 172 173 // Initialize when document is ready 174 $(document).ready(function () { 175 initSpecialSlotsValidation(); 176 }); 177 178 // Also initialize after ACF loads (for AJAX contexts) 179 if (window.acf) { 180 acf.addAction('ready', function () { 181 initSpecialSlotsValidation(); 182 }); 183 } 184 185})(jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.