PageSourceSearch

https://zoar.delivery/wp-content/themes/understrap-child-master/js/unified-button-manager.js?ver=1782224560

js zoar.delivery collected 2026-10-02 01:52:39 UTC 52,499 bytes, 1,177 lines download raw bytes

1/**
2 * Unified Button Manager pentru Fake Checkout
3 * Versiunea 2.1.0 - Production Ready (log cleaned)
4 * 
5 * Acest manager unificat este singurul sistem de toggle butoane:
6 * - fake-checkout-toggle.js (removed)
7 * - CheckoutButtonManager din form-checkout.js (removed in Task #011)
8 * - toggleCheckoutButtons din fake-checkout-handler.js (consolidated in Task #011)
9 * - Colaboreaza cu Horeka Core pentru validare suma minima
10 * 
11 * Caracteristici:
12 * - Un singur punct de control pentru toate butoanele
13 * - Fără race conditions sau conflicte
14 * - Debouncing pentru performanță
15 * - Minimal logging pentru producție
16 */
17
18(function($) {
19    'use strict';
20    
21    // ========================================
22    // CONFIG ȘI CONSTANTE
23    // ========================================
24    
25    // Logging pentru producție (dezactivat implicit)
26    const ENABLE_LOGGING = false;
27    const log = function(message, data) {
28        if (ENABLE_LOGGING) {
29            if (data) {
30                console.log(message, data);
31            } else {
32                console.log(message);
33            }
34        }
35    };
36    
37    const CONFIG = {
38        // Debounce delays pentru evitarea apelurilor multiple
39        DEBOUNCE_DELAY: 250,  // Delay pentru toggle buttons
40        INIT_DELAY: 500,      // Delay pentru inițializare
41        
42        // Selectori DOM
43        SELECTORS: {
44            normalButton: '#place_order',
45            fakeButton: '.fake-checkout-button, #place_order_fake',
46            paymentRadios: 'input[name="payment_method"]',
47            paymentDropdown: 'select[name="payment_method"]',
48            blueCards: '.blue-cards label',
49            body: 'body',
50            iframe: '#iframeCheckout'
51        },
52        
53        // Clase CSS pentru detectare
54        CLASSES: {
55            fakeCheckoutMode: 'fake-checkout-mode',
56            inIframe: 'in-iframe-checkout',
57            netopiaSelected: 'netopia-payment-selected'
58        },
59        
60        // Metode de plată
61        PAYMENT_METHODS: {
62            netopia: 'netopiapayments',
63            cod: 'cod',
64            cardOnDelivery: 'WC_Card_Livrare_Payment_Gateway'
65        }
66    };
67    
68    // ========================================
69    // MANAGER PRINCIPAL
70    // ========================================
71    window.UnifiedButtonManager = {
72        // State intern
73        state: {
74            isInitialized: false,
75            currentPaymentMethod: null,
76            isFakeCheckoutMode: false,
77            isNetopiaSelected: false,
78            lastToggleTime: 0,
79            toggleCount: 0,
80            // SOLUȚIE PAYMENT PERSISTENCE: Salvăm alegerea utilizatorului
81            userSelectedPaymentMethod: null, // Metoda aleasă explicit de utilizator
82            isRestoringPayment: false, // Flag pentru a evita salvarea în timpul restaurării
83            hasUserMadeSelection: false // Flag pentru a detecta dacă utilizatorul a făcut o selecție explicită
84        },
85        
86        /**
87         * Inițializează managerul
88         * Aceasta este singura metodă care trebuie apelată
89         */
90        init: function() {
91            const self = this;
92
93            // Auto-fix pointer-events:none on fake checkout button so clicks register.
94            document.addEventListener('click', function(e) {
95                var fakeBtn = document.querySelector('.fake-checkout-button');
96                if (!fakeBtn) {
97                    return;
98                }
99                var rect = fakeBtn.getBoundingClientRect();
100                var clickX = e.clientX, clickY = e.clientY;
101                if (clickX >= rect.left && clickX <= rect.right && clickY >= rect.top && clickY <= rect.bottom) {
102                    if (window.getComputedStyle(fakeBtn).pointerEvents === 'none') {
103                        fakeBtn.style.pointerEvents = 'auto';
104                        fakeBtn.style.opacity = '1';
105                        fakeBtn.style.cursor = 'pointer';
106                        fakeBtn.disabled = false;
107                        fakeBtn.classList.remove('button-disabled');
108                    }
109                }
110            }, true);
111
112            // Previne inițializarea dublă
113            if (this.state.isInitialized) {
114                return;
115            }
116
117            // SOLUȚIE CRITICĂ: Curăță localStorage la prima încărcare
118            // Doar dacă utilizatorul nu a făcut încă o selecție explicită în această sesiune
119            this.clearInitialPaymentState();
120            
121            // Detectează contextul inițial
122            this.detectContext();
123            
124            // Configurează event listeners
125            this.setupEventListeners();
126            
127            // Inițializare cu delay pentru a permite încărcarea completă a DOM
128            setTimeout(function() {
129                self.checkInitialState();
130                self.state.isInitialized = true;
131                log('[UnifiedManager] ✅ Manager inițializat cu succes');
132            }, CONFIG.INIT_DELAY);
133        },
134        
135        /**
136         * SOLUȚIE CRITICĂ: Curăță starea inițială de payment pentru prima vizitare
137         * Previne restaurarea automată din localStorage la prima încărcare a paginii
138         */
139        clearInitialPaymentState: function() {
140            // Verifică dacă avem un marker că utilizatorul a făcut o selecție în această sesiune
141            const sessionKey = 'horeka_user_made_payment_selection_' + Date.now().toString().slice(-8);
142            const hasSessionSelection = sessionStorage.getItem('horeka_user_made_selection');
143            
144            if (!hasSessionSelection) {
145                // Prima încărcare în această sesiune de browser - curăță localStorage
146                try {
147                    const oldValue = localStorage.getItem('horeka_user_payment_method');
148                    if (oldValue) {
149                        localStorage.removeItem('horeka_user_payment_method');
150                        log('[UnifiedManager] 🧹 Prima încărcare - șters localStorage payment method:', oldValue);
151                    }
152                } catch (e) {
153                    // Ignore localStorage errors
154                }
155                
156                // Marchează că am curățat starea inițială
157                try {
158                    sessionStorage.setItem('horeka_session_cleaned', 'true');
159                } catch (e) {
160                    // Ignore sessionStorage errors
161                }
162                
163                log('[UnifiedManager] 🆕 Prima încărcare detectată - utilizatorul va selecta fresh');
164            } else {
165                log('[UnifiedManager] 🔄 Sesiune continuă - păstrez preferințele utilizatorului');
166                this.state.hasUserMadeSelection = true;
167            }
168        },
169        
170        /**
171         * Detectează contextul (iframe, fake checkout mode, lite version)
172         */
173        detectContext: function() {
174            const $body = $(CONFIG.SELECTORS.body);
175            const $iframe = $(CONFIG.SELECTORS.iframe);
176            
177            // Verifică dacă suntem în iframe
178            const inIframe = (window !== window.top);
179            
180            // Verifică lite version din PHP config
181            const isLiteVersion = window.unifiedManagerConfig && window.unifiedManagerConfig.isLiteVersion;
182            
183            // Verifică dacă avem fake checkout mode (iframe SAU lite version)
184            this.state.isFakeCheckoutMode = 
185                $body.hasClass(CONFIG.CLASSES.fakeCheckoutMode) ||
186                $iframe.length > 0 ||
187                document.cookie.indexOf('is_iframe_context=1') > -1 ||
188                inIframe ||
189                isLiteVersion;
190            
191            log('[UnifiedManager] 🔍 Context detectat:', {
192                inIframe: inIframe,
193                isLiteVersion: isLiteVersion,
194                fakeCheckoutMode: this.state.isFakeCheckoutMode,
195                hasIframeElement: $iframe.length > 0,
196                hasCookie: document.cookie.indexOf('is_iframe_context=1') > -1
197            });
198            
199            return this.state.isFakeCheckoutMode;
200        },
201        
202        /**
203         * Verifică starea inițială și setează butoanele corect
204         */
205        checkInitialState: function() {
206            // Găsește metoda de plată selectată
207            const $selectedPayment = $(CONFIG.SELECTORS.paymentRadios + ':checked');
208            const $dropdown = $(CONFIG.SELECTORS.paymentDropdown);
209            
210            let currentMethod = null;
211            
212            if ($selectedPayment.length) {
213                currentMethod = $selectedPayment.val();
214            } else if ($dropdown.length) {
215                currentMethod = $dropdown.val();
216            }
217            
218            if (currentMethod) {
219                log('[UnifiedManager] 📋 Stare inițială - metodă plată:', currentMethod);
220                this.state.currentPaymentMethod = currentMethod;
221                this.updateButtons();
222            } else {
223                log('[UnifiedManager] ⚠️ Nicio metodă de plată selectată inițial');
224            }
225        },
226        
227        /**
228         * Configurează toți event listeners
229         * Un singur loc pentru toate evenimentele
230         */
231        setupEventListeners: function() {
232            const self = this;
233            
234            // IMPORTANT: Curățăm TOATE event listeners existente înainte de a adăuga noi
235            $(document).off('change', CONFIG.SELECTORS.paymentRadios);
236            $(document).off('change', CONFIG.SELECTORS.paymentDropdown);
237            $(document).off('click', CONFIG.SELECTORS.blueCards);
238            $(document.body).off('updated_checkout.unifiedManager');
239            $(document).off('payment_method_visibility_updated.unifiedManager');
240            
241            log('[UnifiedManager] 🧧 Event listeners curățate pentru a preveni duplicarea');
242            
243            // Debounce function pentru a preveni apelurile multiple
244            const debouncedUpdate = this.debounce(function() {
245                self.updateButtons();
246            }, CONFIG.DEBOUNCE_DELAY);
247            
248            // ========================================
249            // EVENIMENTE PENTRU SCHIMBAREA METODEI DE PLĂTĂ
250            // ========================================
251            
252            // 1. Radio buttons standard - cu namespace pentru identificare ușoară
253            $(document).on('change.unifiedManager', CONFIG.SELECTORS.paymentRadios, function() {
254                const method = $(this).val();
255                log('[UnifiedManager] 📻 Radio schimbat:', method);
256                self.state.currentPaymentMethod = method;
257                
258                // SOLUȚIE PAYMENT PERSISTENCE: Salvăm alegerea utilizatorului
259                // Salvăm DOAR dacă nu suntem în proces de restaurare și utilizatorul a făcut o selecție explicită
260                if (!self.state.isRestoringPayment) {
261                    // Marcăm că utilizatorul a făcut o selecție explicită (nu automată)
262                    self.state.hasUserMadeSelection = true;
263                    self.saveUserPaymentChoice(method);
264                    log('[UnifiedManager] 💾 Metodă salvată pentru restaurare (selecție utilizator):', method);
265                }
266                
267                debouncedUpdate();
268            });
269            
270            // 2. Dropdown select (dacă există) - cu namespace
271            $(document).on('change.unifiedManager', CONFIG.SELECTORS.paymentDropdown, function() {
272                const method = $(this).val();
273                log('[UnifiedManager] 📝 Dropdown schimbat:', method);
274                self.state.currentPaymentMethod = method;
275                
276                // SOLUȚIE PAYMENT PERSISTENCE: Salvăm și pentru dropdown
277                if (!self.state.isRestoringPayment) {
278                    self.state.hasUserMadeSelection = true;
279                    self.saveUserPaymentChoice(method);
280                    log('[UnifiedManager] 💾 Metodă salvată pentru restaurare (dropdown):', method);
281                }
282                
283                debouncedUpdate();
284            });
285            
286            // 3. Blue cards click (pentru design custom) - cu namespace
287            $(document).on('click.unifiedManager', CONFIG.SELECTORS.blueCards, function() {
288                const method = $(this).find('input').val();
289                log('[UnifiedManager] 💳 Card selectat:', method);
290                self.state.currentPaymentMethod = method;
291                
292                // SOLUȚIE PAYMENT PERSISTENCE: Salvăm și pentru blue cards
293                if (!self.state.isRestoringPayment) {
294                    self.state.hasUserMadeSelection = true;
295                    self.saveUserPaymentChoice(method);
296                    log('[UnifiedManager] 💾 Metodă salvată pentru restaurare (blue card):', method);
297                }
298                
299                // Delay mic pentru a permite radio să se selecteze
300                setTimeout(function() {
301                    debouncedUpdate();
302                }, 50);
303            });
304            
305            // ========================================
306            // EVENIMENTE WOOCOMMERCE
307            // ========================================
308            
309            // 4. După update checkout - cu namespace
310            $(document.body).on('updated_checkout.unifiedManager', function() {
311                log('[UnifiedManager] ♻️ Checkout actualizat');
312                self.detectContext(); // Re-verifică contextul
313                
314                // SOLUȚIE PAYMENT PERSISTENCE: Restaurăm metoda de plată salvată
315                // ÎMBUNĂTĂȚIRE TIMING: Restaurăm IMEDIAT pentru a preveni auto-selecția
316                setTimeout(function() {
317                    // PRIORITATE: Încercăm să restaurăm metoda salvată de utilizator PRIMUL
318                    self.restoreUserPaymentChoice();
319                    
320                    // După restaurare, verificăm starea inițială
321                    self.checkInitialState();
322                }, 10); // Redus de la 100ms la 10ms pentru timing mai rapid
323            });
324            
325            // 4b. ÎMBUNĂTĂȚIRE ANTI-RESET: Salvează metoda curentă ÎNAINTE de update_checkout
326            $(document.body).on('update_checkout.unifiedManager', function() {
327                log('[UnifiedManager] ⏰ Pre-update: Salvez metoda curentă...');
328                // Salvăm rapid metoda curent selectată înainte ca checkout-ul să se actualizeze
329                var currentMethod = $('input[name="payment_method"]:checked').val();
330                if (currentMethod) {
331                    self.state.userSelectedPaymentMethod = currentMethod;
332                    self.state.hasUserMadeSelection = true;
333                    log('[UnifiedManager] 💾 Pre-salvat pentru backup:', currentMethod);
334                }
335            });
336            
337            // 5. Custom event pentru update vizibilitate - cu namespace
338            $(document).on('payment_method_visibility_updated.unifiedManager', function() {
339                log('[UnifiedManager] 👁️ Vizibilitate actualizată');
340                debouncedUpdate();
341            });
342            
343            // ========================================
344            // EVENIMENTE PENTRU METODA DE LIVRARE
345            // ========================================
346            
347            // 6. Schimbarea metodei de livrare (radio buttons)
348            $(document).on('change.unifiedManager', 'input[name="delivery_method"]', function() {
349                const method = $(this).val();
350                log('[UnifiedManager] 🚚 Metodă livrare schimbată:', method);
351                
352                // Re-verifică suma minimă când se schimbă metoda de livrare
353                setTimeout(function() {
354                    self.updateButtons();
355                }, 100);
356            });
357            
358            // 7. Schimbarea metodei de livrare (dropdown)
359            $(document).on('change.unifiedManager', 'select[name="delivery_method"]', function() {
360                const method = $(this).val();
361                log('[UnifiedManager] 🚚 Metodă livrare schimbată (dropdown):', method);
362                
363                // Re-verifică suma minimă când se schimbă metoda de livrare
364                setTimeout(function() {
365                    self.updateButtons();
366                }, 100);
367            });
368            
369            // 8. Schimbarea adresei de livrare sau facturare
370            $(document).on('change.unifiedManager blur.unifiedManager', 
371                '#billing_address_1_field input, #shipping_address_1_field input, input[name="billing_address_1"], input[name="shipping_address_1"]', 
372                function() {
373                    const addressValue = $(this).val();
374                    log('[UnifiedManager] 🏠 Adresă schimbată:', addressValue);
375                    
376                    // Delay mai mare pentru a permite calculul taxelor de livrare
377                    setTimeout(function() {
378                        self.updateButtons();
379                    }, 300);
380                });
381            
382            // 9. Update când se schimbă totalul comenzii (pentru re-validare)
383            $(document.body).on('updated_cart_totals updated_checkout updated_shipping_method.unifiedManager', function() {
384                log('[UnifiedManager] 💰 Totaluri actualizate - re-verific suma minimă');
385                setTimeout(function() {
386                    self.updateButtons();
387                }, 200);
388            });
389            
390            // ========================================
391            // DEZACTIVARE SISTEME VECHI
392            // ========================================
393            
394            // Oprește alte managere să intervină
395            this.disableConflictingSystems();
396        },
397        
398        /**
399         * Ensures the fake checkout button exists in the DOM.
400         * Creates it dynamically next to #place_order if missing.
401         * Previously this logic lived in fake-checkout-handler.js toggleCheckoutButtons().
402         * Consolidated here in Task #011.
403         */
404        ensureFakeButtonExists: function() {
405            var $fakeButton = $(CONFIG.SELECTORS.fakeButton);
406            if ($fakeButton.length > 0) {
407                return $fakeButton;
408            }
409
410            var $normalButton = $(CONFIG.SELECTORS.normalButton);
411            if (!$normalButton.length) {
412                return $();
413            }
414
415            var buttonText = $normalButton.text() || 'Plaseaza comanda';
416            var fakeButtonHtml = '<button type="button" class="button alt fake-checkout-button" id="place_order_fake" style="display: none;" data-fake-checkout="true">' + buttonText + '</button>';
417            $normalButton.after(fakeButtonHtml);
418            $fakeButton = $('#place_order_fake');
419            log('[UnifiedManager] Fake button created dynamically');
420
421            return $fakeButton;
422        },
423
424        /**
425         * Funcția principală care actualizează butoanele
426         * Aceasta este singura funcție care manipulează DOM-ul
427         */
428        updateButtons: function() {
429            // Re-detectează contextul la fiecare update
430            this.detectContext();
431
432            const $normalButton = $(CONFIG.SELECTORS.normalButton);
433
434            // Ensure fake button exists before trying to use it
435            if (this.state.isFakeCheckoutMode &&
436                this.state.currentPaymentMethod === CONFIG.PAYMENT_METHODS.netopia) {
437                this.ensureFakeButtonExists();
438            }
439
440            const $fakeButton = $(CONFIG.SELECTORS.fakeButton);
441            
442            // Verifică dacă butoanele există
443            if (!$normalButton.length && !$fakeButton.length) {
444                return;
445            }
446            
447            // ========================================
448            // VERIFICARE SUMA MINIMĂ DE COMANDĂ
449            // ========================================
450            
451            // Verifică dacă suntem în modul redirect fake checkout SAU fake-checkout-mode (light app)
452            const urlParams = new URLSearchParams(window.location.search);
453            const isRedirectMode = urlParams.get('auto_submit') === '1' ||
454                                   urlParams.get('fake_checkout') === '1' ||
455                                   $('body').hasClass('fake-checkout-mode');
456
457            log('[UnifiedManager] updateButtons() - isRedirectMode:', isRedirectMode, 'fake-checkout-mode class:', $('body').hasClass('fake-checkout-mode'));
458            
459            // Verifică metoda de livrare ÎNAINTE de validarea sumei minime
460            const deliveryMethod = this.getDeliveryMethod();
461            
462            // Pentru ridicare personală, NU verifica suma minimă deloc
463            if (deliveryMethod === 'pickup') {
464                log('[UnifiedManager] 🏪 Ridicare personală - skip complet validare sumă minimă');
465            } else if (isRedirectMode) {
466                // În modul redirect pentru livrare acasă, NU verifica suma minimă
467                log('[UnifiedManager] 🎭 Redirect mode livrare acasă - skip validare sumă minimă');
468            } else {
469                // Doar pentru livrare acasă în mod normal, verifică suma minimă
470                log('[UnifiedManager] 🚚 Livrare acasă mod normal - verific suma minimă');
471                const validationPassed = this.checkMinimumOrderAmount();
472                
473                // Dacă validarea nu a trecut, butoanele rămân disable și ieșim
474                if (!validationPassed) {
475                    log('[UnifiedManager] ⚠️ Suma minimă nu este atinsă - butoane disabled');
476                    return;
477                }
478            }
479            
480            // VERIFICARE PRIORITARĂ: Dacă Horeka Core a disabled butoanele, respectă decizia
481            if (this.state.buttonsDisabledByHoreka) {
482                log('[UnifiedManager] ⏸️ Butoane disabled de Horeka Core - nu aplic toggle logic');
483                return; // NU schimba nimic, lasă Horeka Core să controleze
484            }
485            
486            // Determină dacă trebuie să arătăm butonul fake
487            const shouldShowFake = 
488                this.state.isFakeCheckoutMode && 
489                this.state.currentPaymentMethod === CONFIG.PAYMENT_METHODS.netopia &&
490                !this.hasAddressValidationErrors(); // ADĂUGAT: Verificare erori adresă
491            
492            // Logging doar dacă starea se schimbă
493            if (this.shouldLog()) {
494                log('[UnifiedManager] 🔄 Toggle butoane:', {
495                    metodaPlata: this.state.currentPaymentMethod,
496                    fakeMode: this.state.isFakeCheckoutMode,
497                    showFake: shouldShowFake,
498                    hasAddressErrors: this.hasAddressValidationErrors(),
499                    redirectMode: isRedirectMode
500                });
501            }
502            
503            // ========================================
504            // APLICARE STARE - SIMPLU ȘI CLAR
505            // ========================================
506            
507            if (shouldShowFake) {
508                // Arată butonul fake, ascunde pe cel normal - FORȚAT cu CSS inline
509                $normalButton
510                    .css({
511                        'display': 'none',
512                        'visibility': 'hidden',
513                        'position': 'absolute',
514                        'left': '-9999px'
515                    })
516                    .addClass('button-hidden')
517                    .removeClass('button-visible');
518                
519                $fakeButton
520                    .css({
521                        'display': 'inline-block',
522                        'visibility': 'visible',
523                        'position': 'relative',
524                        'left': 'auto',
525                        'pointer-events': 'auto',
526                        'opacity': '1',
527                        'cursor': 'pointer'
528                    })
529                    .prop('disabled', false)
530                    .addClass('button-visible')
531                    .removeClass('button-hidden button-disabled');
532
533                log('[UnifiedManager] updateButtons() - fake button ENABLED & SHOWN');
534                
535                // Adaugă clase pe body pentru CSS
536                $(CONFIG.SELECTORS.body).addClass(CONFIG.CLASSES.netopiaSelected);
537                
538                // DEZACTIVAT: Nu salva automat în localStorage - doar pentru redirect iframe specific
539                // localStorage.setItem('paymentMethod', CONFIG.PAYMENT_METHODS.netopia);
540                
541                log('[UnifiedManager] ✅ Buton FAKE activat pentru Netopia');
542            } else {
543                // Arată butonul normal, ascunde pe cel fake - FORȚAT cu CSS inline
544                $normalButton
545                    .css({
546                        'display': 'inline-block',
547                        'visibility': 'visible',
548                        'position': 'relative',
549                        'left': 'auto'
550                    })
551                    .addClass('button-visible')
552                    .removeClass('button-hidden');
553                
554                $fakeButton
555                    .css({
556                        'display': 'none',
557                        'visibility': 'hidden',
558                        'position': 'absolute',
559                        'left': '-9999px'
560                    })
561                    .addClass('button-hidden')
562                    .removeClass('button-visible');
563                
564                // Elimină clase de pe body
565                $(CONFIG.SELECTORS.body).removeClass(CONFIG.CLASSES.netopiaSelected);
566                
567                // Curăță localStorage
568                localStorage.removeItem('paymentMethod');
569                
570                if (this.shouldLog()) {
571                    log('[UnifiedManager] ✅ Buton NORMAL activat');
572                }
573            }
574            
575            // Update state pentru tracking
576            this.state.toggleCount++;
577        },
578        
579        /**
580         * Verifică suma minimă de comandă
581         * Colaborează REAL cu validarea din Horeka Core
582         */
583        checkMinimumOrderAmount: function() {
584            // 1. Verifică mai întâi tipul de livrare
585            const deliveryMethod = this.getDeliveryMethod();
586            
587            // Pentru ridicare personală (pickup), nu verifică suma minimă
588            if (deliveryMethod === 'pickup') {
589                log('[UnifiedManager] 🏪 Ridicare personală - nu se verifică suma minimă');
590                this.enableButtons();
591                return true;
592            }
593            
594            // 2. Pentru livrare acasă, verifică cookie-ul CanDeliver setat de Horeka Core
595            log('[UnifiedManager] 🚚 Livrare acasă - verific cookie CanDeliver');
596
597            var canDeliver = (this.getCookie('CanDeliver') || '').toLowerCase();
598
599            if (canDeliver === 'false') {
600                log('[UnifiedManager] 🔇 CanDeliver=False - butoane disabled (Horeka Core decision)');
601                this.disableButtons();
602                return false;
603            }
604
605            if (canDeliver === 'true' || canDeliver === '') {
606                log('[UnifiedManager] ✅ CanDeliver OK - butoane enabled');
607                this.enableButtons();
608                return true;
609            }
610
611            // Cookie absent (prima încărcare) — Horeka Core va seta cookie-ul la updated_checkout
612            log('[UnifiedManager] ⏳ Cookie CanDeliver absent - permit trecerea, aștept Horeka Core');
613            return true;
614        },
615        
616        /**
617         * Determină metoda de livrare selectată
618         */
619        getDeliveryMethod: function() {
620            // Caută radio buttons pentru metoda de livrare
621            const $deliveryRadio = $('input[name="delivery_method"]:checked');
622            if ($deliveryRadio.length) {
623                const method = $deliveryRadio.val();
624                
625                // Mapează valorile posibile la categorii
626                if (method === 'ridicare-personala' || method === 'servire-la-restaurant' || method === 'pickup') {
627                    return 'pickup';
628                } else if (method === 'livrare-la-domiciliu' || method === 'home_delivery') {
629                    return 'home_delivery';
630                }
631                return method; // Returnează valoarea exactă dacă nu se potrivește
632            }
633            
634            // Caută în dropdown
635            const $deliverySelect = $('select[name="delivery_method"]');
636            if ($deliverySelect.length) {
637                const method = $deliverySelect.val();
638                log('[UnifiedManager] 🔍 Metodă livrare detectată (select):', method);
639                
640                // Mapează valorile posibile
641                if (method === 'ridicare-personala' || method === 'servire-la-restaurant' || method === 'pickup') {
642                    return 'pickup';
643                } else if (method === 'livrare-la-domiciliu' || method === 'home_delivery') {
644                    return 'home_delivery';
645                }
646                return method;
647            }
648            
649            // Verifică în body class sau alte indicii
650            if ($(document.body).hasClass('pickup-delivery')) {
651                log('[UnifiedManager] 🔍 Metodă livrare detectată (body class): pickup');
652                return 'pickup';
653            }
654            
655            // Debug - arată toate input-urile de delivery pentru diagnosticare
656            const $allDeliveryInputs = $('input[name*="delivery"], select[name*="delivery"]');
657            if ($allDeliveryInputs.length) {
658                log('[UnifiedManager] 🔧 Debug - toate input-urile delivery găsite:');
659                $allDeliveryInputs.each(function() {
660                    log('  -', $(this).attr('name'), '=', $(this).val(), 'checked:', $(this).is(':checked'));
661                });
662            }
663            
664            // Default: presupune că e livrare acasă
665            log('[UnifiedManager] ⚠️ Nu s-a putut determina metoda de livrare - presupun livrare acasă');
666            return 'home_delivery';
667        },
668        
669        /**
670         * Verifică dacă există erori de validare adresă
671         * @returns {boolean} true dacă există erori de adresă
672         */
673        hasAddressValidationErrors: function() {
674            // Verifică mesajele de eroare WooCommerce
675            const $errors = $('.woocommerce-error, .woocommerce-message--error');
676            if ($errors.length > 0) {
677                const errorText = $errors.text().toLowerCase();
678                if (errorText.includes('nu putem livra') || 
679                    errorText.includes('cannot deliver') ||
680                    errorText.includes('select an address')) {
681                    log('[UnifiedManager] ❌ Eroare validare adresă detectată:', errorText);
682                    return true;
683                }
684            }
685            
686            // Verifică cookie-ul CanDeliver (poate fi 'false' cu F mic sau 'False' cu F mare)
687            const canDeliver = this.getCookie('CanDeliver');
688            if (canDeliver === 'false' || canDeliver === 'False') {
689                log('[UnifiedManager] ❌ Cookie CanDeliver =', canDeliver);
690                // Ascunde secțiunea place-order când adresa nu e validă
691                $('.form-row.place-order').hide();
692                return true;
693            } else if (canDeliver === 'true' || canDeliver === 'True' || canDeliver === '') {
694                // Afișează secțiunea place-order când adresa e validă sau neutră
695                $('.form-row.place-order').show();
696            }
697            
698            return false;
699        },
700        
701        /**
702         * Obține valoarea unui cookie
703         * @param {string} name Numele cookie-ului
704         * @returns {string} Valoarea cookie-ului sau string gol
705         */
706        getCookie: function(name) {
707            const nameEQ = name + "=";
708            // FIX: Split raw cookie string FIRST, then decode only the matched value.
709            // decodeURIComponent(document.cookie) breaks if ANY cookie has a raw '%'.
710            const cookies = document.cookie.split(';');
711            
712            for (let i = 0; i < cookies.length; i++) {
713                let c = cookies[i].trim();
714                if (c.indexOf(nameEQ) === 0) {
715                    const rawValue = c.substring(nameEQ.length);
716                    try {
717                        return decodeURIComponent(rawValue);
718                    } catch (e) {
719                        // Value contains malformed percent-encoding; return as-is
720                        return rawValue;
721                    }
722                }
723            }
724            return "";
725        },
726        
727        /**
728         * Validare fallback pentru suma minimă când Horeka Core nu e disponibil
729         */
730        validateMinimumOrderFallback: function() {
731            // Caută suma totală în DOM
732            const $totalElement = $('.order-total .woocommerce-Price-amount, .total .amount');
733            
734            if (!$totalElement.length) {
735                log('[UnifiedManager] ⚠️ Nu s-a găsit elementul cu suma totală');
736                return true; // Lasă să treacă dacă nu găsim suma
737            }
738            
739            // Extrage valoarea numerică din text
740            const totalText = $totalElement.text();
741            const totalValue = parseFloat(totalText.replace(/[^\d,.-]/g, '').replace(',', '.'));
742            
743            // Suma minimă default (poate fi configurată)
744            const minimumAmount = (window.unifiedManagerConfig && window.unifiedManagerConfig.minimumOrderAmount)
745                ? parseFloat(window.unifiedManagerConfig.minimumOrderAmount)
746                : 50;
747            
748            log('[UnifiedManager] 💰 Validare fallback:', {
749                totalText: totalText,
750                totalValue: totalValue,
751                minimumAmount: minimumAmount,
752                isValid: totalValue >= minimumAmount
753            });
754            
755            if (totalValue >= minimumAmount) {
756                return true;
757            } else {
758                log('[UnifiedManager] 🔇 Suma minimă nu e atinsă - disable silent fără popup');
759                return false;
760            }
761        },
762        
763        /**
764         * Afișează popup-ul pentru suma minimă (colaborează cu Horeka Core)
765         */
766        showMinimumOrderPopup: function() {
767            // Dacă Horeka Core are funcția pentru popup, o folosește
768            if (typeof window.showMinimumOrderPopup === 'function') {
769                log('[UnifiedManager] 📢 Afișez popup Horeka Core pentru suma minimă');
770                window.showMinimumOrderPopup();
771                return;
772            }
773            
774            // Fallback: popup simplu
775            const message = 'Suma minimă pentru comandă nu este atinsă. Te rugăm să adaugi mai multe produse.';
776            
777            // Verifică dacă avem sweet alert sau alt sistem
778            if (typeof Swal !== 'undefined') {
779                Swal.fire({
780                    icon: 'warning',
781                    title: 'Sumă minimă necesară',
782                    text: message,
783                    confirmButtonText: 'OK'
784                });
785            } else {
786                // Fallback la alert browser
787                alert(message);
788            }
789            
790            log('[UnifiedManager] 📢 Am afișat popup fallback pentru suma minimă');
791        },
792        
793        /**
794         * Dezactivează butoanele când suma minimă nu e atinsă
795         */
796        disableButtons: function() {
797            log('[UnifiedManager] 🚫 disableButtons() called');
798            const $normalButton = $(CONFIG.SELECTORS.normalButton);
799            const $fakeButton = $(CONFIG.SELECTORS.fakeButton);
800
801            // Disable și stilizează butoanele
802            $normalButton
803                .prop('disabled', true)
804                .css({
805                    'opacity': '0.5',
806                    'cursor': 'not-allowed',
807                    'pointer-events': 'none'
808                })
809                .addClass('button-disabled');
810            
811            $fakeButton
812                .prop('disabled', true)
813                .css({
814                    'opacity': '0.5',
815                    'cursor': 'not-allowed',
816                    'pointer-events': 'none'
817                })
818                .addClass('button-disabled');
819            
820            log('[UnifiedManager] 🚫 Butoane disabled pentru sumă minimă');
821        },
822        
823        /**
824         * Activează butoanele când suma minimă este atinsă
825         */
826        enableButtons: function() {
827            log('[UnifiedManager] ✅ enableButtons() called');
828            const $normalButton = $(CONFIG.SELECTORS.normalButton);
829            const $fakeButton = $(CONFIG.SELECTORS.fakeButton);
830            
831            // Enable și resetează stilurile
832            $normalButton
833                .prop('disabled', false)
834                .css({
835                    'opacity': '1',
836                    'cursor': 'pointer',
837                    'pointer-events': 'auto'
838                })
839                .removeClass('button-disabled');
840            
841            $fakeButton
842                .prop('disabled', false)
843                .css({
844                    'opacity': '1',
845                    'cursor': 'pointer',
846                    'pointer-events': 'auto'
847                })
848                .removeClass('button-disabled');
849            
850            log('[UnifiedManager] ✅ Butoane enabled - suma minimă atinsă');
851        },
852        
853        /**
854         * Configurează colaborarea perfectă cu Horeka Core
855         * Soluție hibridă: Horeka Core execută disable/enable, UnifiedManager sincronizează
856         */
857        disableConflictingSystems: function() {
858            const self = this;
859            
860            // SETEAZĂ FLAGS GLOBALE pentru autoritate partajată
861            window.UNIFIED_MANAGER_IS_ACTIVE = true;
862            window.BUTTON_MANAGER_ACTIVE = 'hybrid';
863            window.BUTTON_CONTROL_AUTHORITY = 'UnifiedManager+HorekaCore';
864            
865            log('[UnifiedManager] 🤝 Configurez colaborare hibridă cu Horeka Core');
866            
867            // STRATEGIA HIBRIDĂ: Permit Horeka Core să facă disable/enable REAL
868            // dar sincronizez UnifiedManager cu aceste schimbări
869            
870            // 1. NU bloca horekaButtonControlDisabled - permite Horeka Core să lucreze
871            // Acest flag rămâne false pentru a permite Horeka Core să execute logica
872            setTimeout(function() {
873                if (typeof window.horekaButtonControlDisabled !== 'undefined') {
874                    // FORȚEZ false pentru a permite Horeka Core să funcționeze
875                    window.horekaButtonControlDisabled = false;
876                    log('[UnifiedManager] 🔓 Permit Horeka Core să controleze butoanele (horekaButtonControlDisabled = false)');
877                }
878            }, 100);
879            
880            // 2. INTERCEPTARE INTELIGENTĂ: Horeka Core execută, UnifiedManager sincronizează
881            if (typeof window.disablePlaceOrderButton === 'function') {
882                const originalDisable = window.disablePlaceOrderButton;
883                window.disablePlaceOrderButton.unifiedManagerOriginal = originalDisable;
884                
885                window.disablePlaceOrderButton = function(reason) {
886                    log('[UnifiedManager] 📨 Horeka Core cere disable butoane:', reason);
887                    
888                    // 1. Execută ORIGINAL Horeka Core disable logic
889                    originalDisable.apply(this, arguments);
890                    
891                    // 2. Sincronizez UnifiedManager state
892                    if (self && self.state) {
893                        self.state.buttonsDisabledByHoreka = true;
894                        self.state.horekaDisableReason = reason;
895                    }
896                    
897                    // 3. Apply UnifiedManager styling pentru consistență
898                    setTimeout(function() {
899                        self.syncWithHorekaState('disabled', reason);
900                    }, 50);
901                };
902            }
903            
904            if (typeof window.enablePlaceOrderButton === 'function') {
905                const originalEnable = window.enablePlaceOrderButton;
906                window.enablePlaceOrderButton.unifiedManagerOriginal = originalEnable;
907                
908                window.enablePlaceOrderButton = function() {
909                    log('[UnifiedManager] 📨 Horeka Core cere enable butoane');
910                    
911                    // 1. Execută ORIGINAL Horeka Core enable logic
912                    originalEnable.apply(this, arguments);
913                    
914                    // 2. Sincronizez UnifiedManager state
915                    if (self && self.state) {
916                        self.state.buttonsDisabledByHoreka = false;
917                        delete self.state.horekaDisableReason;
918                    }
919                    
920                    // 3. Apply UnifiedManager logic pentru toggle buttons
921                    setTimeout(function() {
922                        self.syncWithHorekaState('enabled');
923                        // Re-apply button toggle logic dacă e necesar
924                        self.updateButtons();
925                    }, 50);
926                };
927            }
928            
929            // 3. Păstrează funcționalitatea checkPlaceOrderButtonState
930            if (typeof window.checkPlaceOrderButtonState === 'function') {
931                const originalCheck = window.checkPlaceOrderButtonState;
932                window.checkPlaceOrderButtonState.unifiedManagerOriginal = originalCheck;
933                
934                // NU suprascriu această funcție - o las să funcționeze normal
935                // Horeka Core o va apela și va apela disable/enable care sunt deja interceptate
936                log('[UnifiedManager] ✅ Păstrez checkPlaceOrderButtonState original - Horeka Core va apela disable/enable interceptate');
937            }
938            
939            // 4. CheckoutButtonManager was removed in Task #011 - no longer needs deactivation
940
941            // 5. Flags pentru scripturile vechi
942            window.FAKE_CHECKOUT_TOGGLE_DISABLED = true;
943            window.BUTTON_MANAGER_DISABLED = true;
944            
945            log('[UnifiedManager] ✅ Colaborare hibridă configurată: Horeka Core execută disable/enable, UnifiedManager sincronizează');
946        },
947        
948        /**
949         * Sincronizează UnifiedManager cu starea setată de Horeka Core
950         */
951        syncWithHorekaState: function(action, reason) {
952            log('[UnifiedManager] 🔄 Sincronizez cu starea Horeka Core:', action);
953            
954            const $normalButton = $(CONFIG.SELECTORS.normalButton);
955            const $fakeButton = $(CONFIG.SELECTORS.fakeButton);
956            
957            if (action === 'disabled') {
958                // Horeka Core a disabled butoanele - asigur consistența vizuală
959                
960                // Adaug clase UnifiedManager pentru tracking
961                $normalButton.addClass('unified-disabled-by-horeka');
962                $fakeButton.addClass('unified-disabled-by-horeka');
963                
964                // Asigur că toggle logic nu interferează când butoanele sunt disabled
965                log('[UnifiedManager] 🔒 Butoane disabled de Horeka Core - suspend toggle logic');
966                
967            } else if (action === 'enabled') {
968                // Horeka Core a enabled butoanele - pot continua cu toggle logic
969                
970                // Elimin clase de tracking
971                $normalButton.removeClass('unified-disabled-by-horeka');
972                $fakeButton.removeClass('unified-disabled-by-horeka');
973                
974                log('[UnifiedManager] 🔓 Butoane enabled de Horeka Core - reiau toggle logic');
975            }
976        },
977        
978        /**
979         * Handler pentru rezultatul validării din Horeka Core
980         * Această funcție este apelată automat când Horeka Core termină validarea
981         */
982        handleHorekaValidationResult: function(isValid) {
983            log('[UnifiedManager] 📨 Rezultat validare primit de la Horeka Core:', isValid);
984            
985            if (isValid) {
986                log('[UnifiedManager] ✅ Validare OK - activez butoanele');
987                this.enableButtons();
988            } else {
989                log('[UnifiedManager] 🔇 Validare failed - dezactivez butoanele silent (fără popup)');
990                this.disableButtons();
991            }
992        },
993        
994        /**
995         * SOLUȚIE PAYMENT PERSISTENCE: Salvează alegerea utilizatorului
996         * Funcție simplă care salvează în localStorage și în state
997         * @param {string} method - Metoda de plată selectată (ex: 'netopiapayments', 'cod')
998         */
999        saveUserPaymentChoice: function(method) {
1000            // Salvăm în state intern pentru acces rapid
1001            this.state.userSelectedPaymentMethod = method;
1002            
1003            // SOLUȚIE: Marchează că utilizatorul a făcut o selecție în această sesiune
1004            try {
1005                sessionStorage.setItem('horeka_user_made_selection', 'true');
1006            } catch (e) {
1007                // Ignore sessionStorage errors
1008            }
1009            
1010            // Salvăm și în localStorage pentru persistență între sesiuni
1011            // localStorage supraviețuiește și după refresh pagină
1012            try {
1013                localStorage.setItem('horeka_user_payment_method', method);
1014                log('[UnifiedManager] 💾 Salvat în localStorage (selecție utilizator):', method);
1015            } catch (e) {
1016                // Dacă localStorage nu e disponibil, folosim doar state
1017            }
1018        },
1019        
1020        /**
1021         * SOLUȚIE PAYMENT PERSISTENCE: Restaurează alegerea utilizatorului
1022         * Se apelează după fiecare update_checkout pentru a păstra selecția
1023         */
1024        restoreUserPaymentChoice: function() {
1025            log('[UnifiedManager] 🔄 Încerc restaurarea metodei de plată (timing optimizat)...');
1026            
1027            // VERIFICARE CRITICĂ: Restaurăm DOAR dacă utilizatorul a făcut o selecție anterioară
1028            if (!this.state.hasUserMadeSelection) {
1029                log('[UnifiedManager] ℹ️ Utilizatorul nu a făcut încă o selecție explicită - nu restaurez nimic');
1030                return;
1031            }
1032            
1033            // Prioritate 1: Încercăm din state (cel mai rapid)
1034            let savedMethod = this.state.userSelectedPaymentMethod;
1035            
1036            // Prioritate 2: Dacă nu e în state, încercăm din localStorage
1037            if (!savedMethod) {
1038                try {
1039                    savedMethod = localStorage.getItem('horeka_user_payment_method');
1040                    if (savedMethod) {
1041                        log('[UnifiedManager] 📂 Metodă găsită în localStorage:', savedMethod);
1042                        // Dacă găsim ceva în localStorage, înseamnă că user-ul a avut o selecție anterioară
1043                        this.state.hasUserMadeSelection = true;
1044                    }
1045                } catch (e) {
1046                    // localStorage indisponibil
1047                }
1048            }
1049            
1050            // Dacă nu avem nimic salvat, nu facem nimic
1051            if (!savedMethod) {
1052                log('[UnifiedManager] ℹ️ Nicio metodă salvată pe
1052ntru restaurare');
1053                return;
1054            }
1055            
1056            // Verificăm dacă metoda salvată mai există în DOM
1057            // (poate fi indisponibilă după update datorită logicii de business)
1058            const $savedMethodElement = $('input[name="payment_method"][value="' + savedMethod + '"]');
1059            
1060            if ($savedMethodElement.length === 0) {
1061                log('[UnifiedManager] ⚠️ Metoda salvată nu mai e disponibilă:', savedMethod);
1062                return;
1063            }
1064            
1065            // Setăm flag pentru a nu salva din nou în timpul restaurării
1066            this.state.isRestoringPayment = true;
1067            
1068            // Restaurăm selecția
1069            log('[UnifiedManager] ✅ Restaurez metoda de plată:', savedMethod);
1070            
1071            // 1. Setăm radio button-ul
1072            $savedMethodElement.prop('checked', true).trigger('change');
1073            
1074            // 2. Pentru blue cards, triggeruim și click pe label
1075            const $blueCard = $('.blue-cards input[value="' + savedMethod + '"]');
1076            if ($blueCard.length) {
1077                $blueCard.prop('checked', true);
1078                $blueCard.closest('label').addClass('active');
1079                log('[UnifiedManager] 💳 Blue card restaurat pentru:', savedMethod);
1080            }
1081            
1082            // 3. Pentru dropdown (dacă există)
1083            const $dropdown = $('select[name="payment_method"]');
1084            if ($dropdown.length) {
1085                $dropdown.val(savedMethod);
1086                log('[UnifiedManager] 📝 Dropdown restaurat pentru:', savedMethod);
1087            }
1088            
1089            // Actualizăm state-ul intern
1090            this.state.currentPaymentMethod = savedMethod;
1091            
1092            // Resetăm flag-ul după un delay mic
1093            const self = this;
1094            setTimeout(function() {
1095                self.state.isRestoringPayment = false;
1096                log('[UnifiedManager] ✅ Restaurare completă, gata pentru noi schimbări');
1097            }, 200);
1098        },
1099        
1100        /**
1101         * Helper pentru debounce - previne apelurile multiple rapide
1102         */
1103        debounce: function(func, delay) {
1104            let timeoutId;
1105            return function() {
1106                const context = this;
1107                const args = arguments;
1108                clearTimeout(timeoutId);
1109                timeoutId = setTimeout(function() {
1110                    func.apply(context, args);
1111                }, delay);
1112            };
1113        },
1114        
1115        /**
1116         * Helper pentru logging - reduce spam în console
1117         */
1118        shouldLog: function() {
1119            const now = Date.now();
1120            
1121            // Log primele 3 toggle-uri
1122            if (this.state.toggleCount < 3) {
1123                return true;
1124            }
1125            
1126            // După aceea, log doar la 2 secunde interval
1127            if (now - this.state.lastToggleTime > 2000) {
1128                this.state.lastToggleTime = now;
1129                return true;
1130            }
1131            
1132            return false;
1133        },
1134        
1135        /**
1136         * Funcții pentru debugging
1137         */
1138        debug: function() {
1139            
1140            
1141        }
1142    };
1143    
1144    // ========================================
1145    // INIȚIALIZARE AUTOMATĂ
1146    // ========================================
1147    
1148    // Așteaptă jQuery și DOM ready
1149    $(document).ready(function() {
1150        // Verifică dacă nu a fost dezactivat manual
1151        if (!window.UNIFIED_MANAGER_DISABLED) {
1152            // Delay mic pentru a permite altor scripturi să se încarce
1153            setTimeout(function() {
1154                window.UnifiedButtonManager.init();
1155            }, 100);
1156        }
1157        
1158        // SOLUȚIE PAYMENT PERSISTENCE: Cleanup după plasarea comenzii
1159        // Curățăm metoda salvată când suntem pe pagina de confirmare comandă
1160        if ($('body').hasClass('woocommerce-order-received')) {
1161            log('[UnifiedManager] 🧹 Comandă plasată - curăț metoda de plată salvată');
1162            try {
1163                localStorage.removeItem('horeka_user_payment_method');
1164            } catch (e) {
1165                // Ignorăm erorile de localStorage
1166            }
1167        }
1168    });
1169    
1170    // Expune funcția de debug global
1171    window.debugUnifiedManager = function() {
1172        window.UnifiedButtonManager.debug();
1173    };
1174    
1175    // Script încărcat - folosește debugUnifiedManager() pentru debug
1176    
1177})(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.