PageSourceSearch

http://www.ville-grasse.fr/wp-content/plugins/creasit-gf-add-on-in…/assets/src/js/inventory-slots.js?ver=1.0

js ville-grasse.fr collected 2026-10-02 05:57:16 UTC 13,949 bytes, 345 lines download raw bytes

1jQuery(function($) {
2    $.datepicker.regional['fr'] = {
3        closeText:        'Fermer',
4        prevText:         '<Préc',
5        nextText:         'Suiv>',
6        currentText:      'Aujourd\'hui',
7        monthNames:       ['Janvier','Février','Mars','Avril','Mai','Juin',
8            'Juillet','Août','Septembre','Octobre','Novembre','Décembre'],
9        monthNamesShort:  ['Janvier','Février','Mars','Avril','Mai','Juin',
10            'Juillet','Août','Septembre','Octobre','Novembre','Décembre'],
11        dayNames:         ['Dimanche','Lundi','Mardi','Mercredi','Jeudi','Vendredi','Samedi'],
12        dayNamesShort:    ['Dim.','Lun.','Mar.','Mer.','Jeu.','Ven.','Sam.'],
13        dayNamesMin:      ['dim','lun','mar','mer','jeu','ven','sam'],
14        weekHeader:       'Sem.',
15        dateFormat:       'dd/mm/yy',
16        firstDay:         1,
17        isRTL:            false,
18        showMonthAfterYear: false,
19        yearSuffix:       ''
20    };
21    $.datepicker.setDefaults( $.datepicker.regional['fr'] );
22});
23
24jQuery(document).ready(function ($) {
25
26    // ─────────────────────────────────────────────
27    // Initialisation de chaque champ inventory_slots
28    // ─────────────────────────────────────────────
29
30    // ─── Premier chargement ───
31    $('.gf-inventory-date').each(function () {
32        let $input   = $(this);
33        let $wrapper = $input.closest('.ginput_container');
34        initDatePicker( $input );
35        restoreState( $wrapper );
36    });
37
38    // ─── Après chaque re-render GF (pagination, erreur…) ───
39    $(document).on('gform_post_render', function () {
40
41        $('.gf-inventory-date').each(function () {
42            let $input   = $(this);
43            let $wrapper = $input.closest('.ginput_container');
44            initDatePicker( $input );
45            restoreState( $wrapper );
46        });
47    });
48
49    // ─────────────────────────────────────────────
50    // Initialisation du datepicker
51    // ─────────────────────────────────────────────
52
53    function initDatePicker( $input ) {
54
55        if ( $input.hasClass('hasDatepicker') ) return;
56
57        let fieldId        = $input.data('field-id');
58        let formId         = $input.data('form-id');
59        let slotsData      = [];
60        let availableDates = [];
61
62        try {
63            slotsData      = JSON.parse( $input.attr('data-slots') || '[]' );
64            availableDates = slotsData.map(d => d.date);
65        } catch (e) {
66            console.warn('gf-inventory : impossible de parser data-slots', e);
67        }
68
69        let $wrapper = $input.closest('.ginput_container');
70
71        let slots = [];
72        try {
73            slots = JSON.parse( $input.data('slots') || '[]' );
74        } catch(e) {
75            slots = [];
76        }
77
78        let defaultDate = null;
79        if ( slotsData.length ) {
80            let today = new Date();
81            today.setHours(0,0,0,0);
82            let futureDates = slotsData
83                .map(d => new Date(d.date + 'T00:00:00'))
84                .filter(d => d >= today)
85                .sort((a, b) => a - b);
86            if ( futureDates.length ) {
87                let diffMs   = futureDates[0].getTime() - today.getTime();
88                let diffDays = Math.round( diffMs / (1000 * 60 * 60 * 24) );
89                defaultDate  = diffDays;
90            }
91        }
92
93        $input.datepicker({
94            dateFormat: 'dd/mm/yy',
95            defaultDate: defaultDate,
96            minDate: 0,
97            changeMonth: true,
98            changeYear: true,
99            yearRange: 'c:c+20',
100
101            beforeShow: function (input, inst) {
102                $(inst.dpDiv).addClass('gf-inventory-datepicker gform-theme-datepicker gravity-theme');
103                // console.log($.datepicker.regional)
104                if ( $.datepicker.regional && $.datepicker.regional['fr'] ) {
105                    // console.log($.datepicker.regional)
106                    $.datepicker.setDefaults( $.datepicker.regional['fr'] );
107                }
108            },
109
110            onClose: function (dateText, inst) {
111                $(inst.dpDiv).removeClass('gf-inventory-datepicker');
112            },
113
114            beforeShowDay: function (date) {
115                let d = $.datepicker.formatDate('yy-mm-dd', date);
116                if ( availableDates.includes(d) ) {
117                    return [true, 'available-date', 'Disponible'];
118                }
119                return [false, '', 'Non disponible'];
120            },
121
122            onSelect: function (dateText) {
123                let dateObj = $(this).datepicker('getDate');
124                let isoDate = $.datepicker.formatDate('yy-mm-dd', dateObj);
125
126                $wrapper.find('.gf-date-value').val(isoDate);
127                $wrapper.find('.gf-main-value').val(isoDate);
128                $wrapper.find('.gf-slot-value').val('');
129                resetQty( $wrapper );
130                loadSlots( $input, formId, fieldId, isoDate );
131
132                $wrapper.find('.gf-qty').trigger('change');
133            }
134        });
135
136        $input.on('keydown', function (e) {
137            if ( e.key === 'Backspace' || e.key === 'Delete' ) {
138                $input.val('');
139                $wrapper.find('.gf-date-value').val('');
140                $wrapper.find('.gf-slot-value').val('');
141                $wrapper.find('.gf-main-value').val('');
142                $wrapper.find('.gf-inventory-slots').empty();
143                resetQty( $wrapper );
144                $input.datepicker('setDate', null);
145            }
146        });
147    }
148
149    // ─────────────────────────────────────────────
150    // Gestion de la quantité
151    // ─────────────────────────────────────────────
152
153    function resetQty( $wrapper ) {
154        $wrapper.find('.gf-qty-wrapper')
155            .hide()
156            .find('input')
157            .prop('disabled', true)
158            .val('');
159    }
160
161    function enableQty( $wrapper, max, min, stockTitle ) {
162        min = min || 1;
163        let label = stockTitle && stockTitle.trim() !== '' ? stockTitle : 'Quantité';
164        $wrapper.find('.gf-qty-wrapper').find('.gfield_label').text( label );
165        $wrapper.find('.gf-qty')
166            .attr('max', max)
167            .attr('min', min)
168            .val('')
169            .prop('disabled', false);
170        $wrapper.find('.gf-qty-wrapper').show();
171    }
172
173    // ─────────────────────────────────────────────
174    // Restauration de l'état post-erreur / pagination
175    // ─────────────────────────────────────────────
176
177    function restoreState( $wrapper ) {
178        let $input = $wrapper.find('.gf-inventory-date');
179        let date   = $input.data('restore-date') || $wrapper.find('.gf-date-value').val();
180        let slot   = $wrapper.find('.gf-slot-value').val();
181        let qty    = parseInt( $wrapper.find('.gf-qty').val() ) || 1;
182
183        if ( ! date ) return;
184
185        loadSlots(
186            $input,
187            $input.data('form-id'),
188            $input.data('field-id'),
189            date,
190            function () {
191                let formatted = $.datepicker.formatDate('dd/mm/yy', new Date(date));
192                $input.val(formatted);
193
194                if ( slot ) {
195                    let $radio = $wrapper.find(`input[type="radio"][value="${slot}"]`);
196                    if ( $radio.length ) {
197                        $radio.prop('checked', true).trigger('change');
198                    }
199                }
200
201                if ( qty > 0 ) {
202                    $wrapper.find('.gf-qty').val(qty);
203                    $wrapper.find('.gf-qty').trigger('gf-qty-restore');
204                }
205            }
206        );
207    }
208
209    // ─────────────────────────────────────────────
210    // Chargement AJAX des créneaux
211    // ─────────────────────────────────────────────
212
213    function loadSlots( $input, formId, fieldId, date, callback ) {
214        let $wrapper   = $input.closest('.ginput_container');
215        let $container = $wrapper.find('.gf-inventory-slots');
216
217        $container.html('<p>Chargement…</p>');
218
219        $.post(
220            gfInventory.ajax_url,
221            {
222                action:   'get_slots',
223                nonce:    gfInventory.nonce,
224                form_id:  formId,
225                field_id: fieldId,
226                date:     date,
227            },
228            function (response) {
229                renderSlots( $container, response, formId, fieldId );
230                if ( typeof callback === 'function' ) callback();
231            }
232        ).fail(function () {
233            $container.html('<p>Erreur lors du chargement des créneaux.</p>');
234        });
235    }
236
237    // ─────────────────────────────────────────────
238    // Rendu des créneaux
239    // ─────────────────────────────────────────────
240
241    function renderSlots( $container, slots, formId, fieldId ) {
242
243        if ( ! slots || ! slots.length ) {
244            $container.html('<p style="color:#c92c2c; font-style:italic;">Aucun créneau disponible</p>');
245            return;
246        }
247
248        $container.empty();
249
250        let radioName = `gf_slot_${formId}_${fieldId}`;
251
252        slots.forEach(function (slot) {
253
254            let stock     = parseInt(slot.stock || 0);
255            let isSoldOut = stock <= 0;
256            let title     = slot.stockTitle ? ' ' + slot.stockTitle : '';
257            let label     = slot.stockLabel ? ' ' + slot.stockLabel : '';
258            let remaining = stock;
259            let total     = parseInt(slot.stockTotal || stock);
260
261            let $slot = $(`
262                <div class="gf-slot ${isSoldOut ? 'soldout' : ''}" data-max="${remaining}" data-min="${slot.min || 1}" data-stock-title="${slot.stockTitle ?? ''}">
263                    <label>
264                        <input
265                            type="radio"
266                            name="${radioName}"
267                            value="${slot.label}"
268                            ${isSoldOut ? 'disabled' : ''}
269                        >
270                        ${slot.label}
271                        ${isSoldOut
272                ? `<span class="gf-stock-notice" style="color:#c92c2c; font-style:italic; font-weight:400;">
273                           ${slot.noticeSoldOut || 'Complet'}
274                       </span>`
275                : slot.displayStock
276                    ? `<span class="gf-stock-label" style="color:#c92c2c; font-style:italic; font-weight:400;">
277                           ${remaining}/${total}${label}
278                       </span>`
279                    : ''
280            }
281                    </label>
282                </div>
283            `);
284
285            $container.append($slot);
286        });
287
288        // ── Sélection d'un créneau ──
289        $container.on('change', 'input[type="radio"]', function () {
290            let $wrapper  = $container.closest('.ginput_container');
291            let $slotDiv  = $(this).closest('.gf-slot');
292            let max       = parseInt( $slotDiv.data('max') || 9999 );
293            let min       = parseInt( $slotDiv.data('min') || 1 );
294            let stockTitle = $slotDiv.data('stock-title') || '';
295
296            $container.find('.gf-slot').removeClass('selected');
297            $slotDiv.addClass('selected');
298
299            $wrapper.find('.gf-slot-value').val( $(this).val() );
300            $wrapper.find('.gf-main-value').val(
301                $wrapper.find('.gf-date-value').val() + '|' + $(this).val()
302            );
303
304            enableQty( $wrapper, max, min, stockTitle );
305
306            $wrapper.find('.gf-qty').val('').trigger('change');
307        });
308
309        // ── Validation de la quantité ──
310        $container.on('input', '.gf-qty', function () {
311            let max = parseInt( $(this).attr('max') || 9999 );
312            let val = parseInt( $(this).val() || 1 );
313
314            if ( val > max ) {
315                $(this).val(max);
316                showQtyError( $(this), 'Veuillez saisir un nombre ≤ ' + max );
317            } else if ( val < 1 ) {
318                $(this).val(1);
319                clearQtyError( $(this) );
320            } else {
321                clearQtyError( $(this) );
322            }
323        });
324    }
325
326    // ─────────────────────────────────────────────
327    // Messages d'erreur inline sur la quantité
328    // ─────────────────────────────────────────────
329
330    function showQtyError( $input, message ) {
331        let $wrapper = $input.closest('.gf-qty-wrapper');
332        let $error   = $wrapper.find('.gf-qty-error');
333
334        if ( ! $error.length ) {
335            $error = $('<span class="gf-qty-error" style="color:red; font-size:.875em;"></span>');
336            $wrapper.append($error);
337        }
338        $error.text(message);
339    }
340
341    function clearQtyError( $input ) {
342        $input.closest('.gf-qty-wrapper').find('.gf-qty-error').remove();
343    }
344
345});

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.