PageSourceSearch

https://pizzeriaarena.ro/wp-content/themes/understrap-child-master/js/unified-button-manager.js?ver=1788941930

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