/* * Copyright 2025 Criptomart * License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) */ (function () { "use strict"; // Objeto global para gestión de carrito window.groupOrderShop = { orderId: null, cart: {}, labels: {}, // Will be loaded from HTML data attributes init: function () { console.log("[groupOrderShop] Initializing..."); var self = this; // Get order ID first (needed by i18nManager and other functions) var confirmBtn = document.getElementById("confirm-order-btn"); var cartContainer = document.getElementById("cart-items-container"); var orderIdElement = confirmBtn || cartContainer; if (!orderIdElement) { console.log("No elements found to get order ID"); return false; } // Get the order ID from the data attribute or from the URL this.orderId = orderIdElement.getAttribute("data-order-id"); if (!this.orderId) { var urlMatch = window.location.pathname.match(/\/eskaera\/(\d+)/); this.orderId = urlMatch ? urlMatch[1] : null; } if (!this.orderId) { console.error("Order ID not found"); if (cartContainer) { cartContainer.innerHTML = '
' + "Error: Order ID not found
"; } return false; } console.log("Initializing cart for order:", this.orderId); // Attach event listeners FIRST (doesn't depend on translations) this._attachEventListeners(); console.log("[groupOrderShop] Event listeners attached"); // Wait for i18nManager to load translations from server i18nManager .init() .then(function () { console.log("[groupOrderShop] Translations loaded from server"); self.labels = i18nManager.getAll(); self._checkGroupOrderStatus(function () { self._loadCart(); self._checkConfirmationMessage(); self._initializeTooltips(); // Update display if there is cart-items-container if (cartContainer) { self._updateCartDisplay(); } // Check if we're loading from history var storageKey = "load_from_history_" + self.orderId; if (sessionStorage.getItem(storageKey)) { // Load items from historical order self._loadFromHistory(); } else { // Auto-load draft order on page load (silent mode) self._autoLoadDraftOnInit(); } // Emit event when fully initialized document.dispatchEvent( new CustomEvent("groupOrderShopReady", { detail: { labels: self.labels }, }) ); console.log("[groupOrderShop] ✓ Initialization complete"); }); }) .catch(function (error) { console.error("[groupOrderShop] Failed to initialize translations:", error); // Fallback: use empty labels so app doesn't crash self.labels = {}; }); // Dispatch event to notify that cart is ready (preliminary, will be fired again when translations loaded) var event = new CustomEvent("groupOrderCartReady", { detail: { orderId: this.orderId }, }); document.dispatchEvent(event); console.log("Cart ready event dispatched for order:", this.orderId); return true; }, _checkConfirmationMessage: function () { // Removed: this was showing confirmation message again in eskaera page // which confused the user. The message is shown only in the checkout page // before redirecting. var confirmationMessage = sessionStorage.getItem("confirmation_message"); if (confirmationMessage) { // Just remove it, don't show it again sessionStorage.removeItem("confirmation_message"); } }, _autoLoadDraftOnInit: function () { console.log("Attempting to auto-load draft order..."); var self = this; // Only auto-load if cart is empty var cartItemsCount = Object.keys(this.cart).length; if (cartItemsCount > 0) { console.log("Cart already has items (" + cartItemsCount + "), skipping auto-load"); return; } var orderData = { order_id: this.orderId, }; var xhr = new XMLHttpRequest(); xhr.open("POST", "/eskaera/load-draft", true); xhr.setRequestHeader("Content-Type", "application/json"); xhr.onload = function () { if (xhr.status === 200) { try { var data = JSON.parse(xhr.responseText); if (data.success) { // Clear current cart self.cart = {}; // Load items from draft order var items = data.items || []; items.forEach(function (item) { var productId = item.product_id; self.cart[productId] = { name: item.product_name, price: item.product_price, qty: item.quantity, }; }); // Save to localStorage self._saveCart(); // Restore pickup fields if available if (data.pickup_day) { var dayElement = document.getElementById("pickup-day"); if (dayElement) { dayElement.value = data.pickup_day; console.log("Auto-loaded pickup_day:", data.pickup_day); } } if (data.pickup_date) { var dateElement = document.getElementById("pickup-date"); if (dateElement) { dateElement.value = data.pickup_date; console.log("Auto-loaded pickup_date:", data.pickup_date); } } if (data.home_delivery !== undefined) { var homeDeliveryElement = document.getElementById("home-delivery-checkbox"); if (homeDeliveryElement) { homeDeliveryElement.checked = data.home_delivery; console.log("Auto-loaded home_delivery:", data.home_delivery); } } // Update display self._updateCartDisplay(); console.log("Auto-loaded " + items.length + " items from draft"); // Show a subtle notification var labels = self._getLabels(); var cartRestoredMsg = (labels.cart_restored || "Your cart has been restored") + " (" + items.length + " items)"; self._showNotification("✓ " + cartRestoredMsg, "info", 3000); } else { // Silently ignore - no draft found (normal case) console.log("No draft found to auto-load (normal)"); } } catch (e) { console.error("Error parsing auto-load response:", e); } } else if (xhr.status === 404) { // No draft found - this is normal, not an error console.log("No draft order found (404 - normal)"); } else { console.log("Auto-load failed with status:", xhr.status); } }; xhr.onerror = function () { console.log("Auto-load connection error (non-critical)"); }; xhr.send(JSON.stringify(orderData)); }, _loadFromHistory: function () { // Load items from historical order (called from load_from_history page) var self = this; var storageKey = "load_from_history_" + this.orderId; var orderNameKey = "load_from_history_order_name_" + this.orderId; var pickupDayKey = "load_from_history_pickup_day_" + this.orderId; var pickupDateKey = "load_from_history_pickup_date_" + this.orderId; var homeDeliveryKey = "load_from_history_home_delivery_" + this.orderId; var itemsJson = sessionStorage.getItem(storageKey); var orderName = sessionStorage.getItem(orderNameKey); var pickupDay = sessionStorage.getItem(pickupDayKey); var pickupDate = sessionStorage.getItem(pickupDateKey); var homeDelivery = sessionStorage.getItem(homeDeliveryKey) === "true"; console.log("DEBUG: _loadFromHistory called for orderId:", this.orderId); console.log("DEBUG: sessionStorageKey:", storageKey); console.log("DEBUG: sessionStorage value type:", typeof itemsJson, "value:", itemsJson); console.log("DEBUG: orderName:", orderName); console.log("DEBUG: pickupDay:", pickupDay, "(empty means different group order)"); console.log("DEBUG: pickupDate:", pickupDate, "(empty means different group order)"); console.log( "DEBUG: homeDelivery:", homeDelivery, "(empty means different group order)" ); if (!itemsJson || itemsJson === "[object Object]") { console.log("No valid items from history found in sessionStorage"); sessionStorage.removeItem(storageKey); sessionStorage.removeItem(orderNameKey); sessionStorage.removeItem(pickupDayKey); sessionStorage.removeItem(pickupDateKey); sessionStorage.removeItem(homeDeliveryKey); return; } try { // Ensure itemsJson is a string before parsing if (typeof itemsJson !== "string") { console.log("itemsJson is not a string, converting..."); itemsJson = JSON.stringify(itemsJson); } // Parse items var items = JSON.parse(itemsJson); // Verify we got an array if (!Array.isArray(items)) { console.error("Parsed items is not an array:", items); sessionStorage.removeItem(storageKey); sessionStorage.removeItem(orderNameKey); sessionStorage.removeItem(pickupDayKey); sessionStorage.removeItem(pickupDateKey); sessionStorage.removeItem(homeDeliveryKey); return; } console.log("Loaded " + items.length + " items from history:", items); // Clear current cart this.cart = {}; console.log("Cart cleared, now empty"); // Add each item to cart items.forEach(function (item) { self.cart[item.product_id] = { name: item.product_name || "Product " + item.product_id, price: item.price || 0, qty: item.quantity || 1, }; console.log( "Added to cart: product_id=" + item.product_id + ", qty=" + item.quantity ); }); console.log("Cart after adding all items:", self.cart); // Save to localStorage this._saveCart(); console.log( "After _saveCart(), localStorage contains:", localStorage.getItem("eskaera_" + this.orderId + "_cart") ); // Restore pickup fields if available (non-empty) // Empty values mean the order was from a different group order, // so we use the current group order's pickup fields instead if (pickupDay && pickupDay.trim() !== "") { var dayElement = document.getElementById("pickup-day"); if (dayElement) { dayElement.value = pickupDay; console.log("Restored pickup_day from old order:", pickupDay); } } else { console.log( "Did NOT restore pickup_day (empty = different group order, using current group order days)" ); } if (pickupDate && pickupDate.trim() !== "") { var dateElement = document.getElementById("pickup-date"); if (dateElement) { dateElement.value = pickupDate; console.log("Restored pickup_date from old order:", pickupDate); } } else { console.log("Did NOT restore pickup_date (empty = different group order)"); } if (homeDelivery !== null && homeDelivery !== false) { var homeDeliveryElement = document.getElementById("home-delivery-checkbox"); if (homeDeliveryElement) { homeDeliveryElement.checked = homeDelivery; console.log("Restored home_delivery from old order:", homeDelivery); } } else { console.log( "Did NOT restore home_delivery (empty/false = different group order or not set)" ); } // Update display this._updateCartDisplay(); // Show notification with order reference if available var labels = this._getLabels(); var itemsLabel = labels.items || "items"; var orderLoadedMsg = labels.order_loaded || "Order loaded"; var message = "✓ " + orderLoadedMsg + " (" + items.length + " " + itemsLabel + ")"; if (orderName) { message += " - " + orderName; } this._showNotification(message, "success", 3000); // Clear sessionStorage sessionStorage.removeItem(storageKey); sessionStorage.removeItem(orderNameKey); sessionStorage.removeItem(pickupDayKey); sessionStorage.removeItem(pickupDateKey); sessionStorage.removeItem(homeDeliveryKey); } catch (e) { console.error("Error loading from history:", e); console.error("itemsJson was:", itemsJson); // Don't show error to user if sessionStorage had invalid data - just continue sessionStorage.removeItem(storageKey); sessionStorage.removeItem(orderNameKey); sessionStorage.removeItem(pickupDayKey); sessionStorage.removeItem(pickupDateKey); sessionStorage.removeItem(homeDeliveryKey); // Only show notification if we actually tried to load something if (itemsJson && itemsJson !== "[object Object]") { this._showNotification("Error loading order: " + e.message, "danger"); } } }, _loadCart: function () { var cartKey = "eskaera_" + this.orderId + "_cart"; var savedCart = localStorage.getItem(cartKey); this.cart = savedCart ? JSON.parse(savedCart) : {}; console.log("Cart loaded from localStorage[" + cartKey + "]:", this.cart); console.log("Raw localStorage value:", savedCart); }, _saveCart: function () { var cartKey = "eskaera_" + this.orderId + "_cart"; var cartJson = JSON.stringify(this.cart); localStorage.setItem(cartKey, cartJson); console.log("Cart saved to localStorage[" + cartKey + "]:", this.cart); console.log("Verification - immediately read back:", localStorage.getItem(cartKey)); }, _clearCurrentOrderCartSilently: function () { var cartKey = "eskaera_" + this.orderId + "_cart"; localStorage.removeItem(cartKey); this.cart = {}; console.log("[groupOrderShop] Cart cleared silently for order:", this.orderId); }, _isClosedOrderResponse: function (xhr) { if (!xhr) { return false; } if (xhr.status !== 400 && xhr.status !== 403) { return false; } try { var errorData = JSON.parse(xhr.responseText || "{}"); return ( errorData.code === "group_order_not_open" || errorData.action === "clear_cart" || errorData.order_state === "closed" || errorData.order_state === "cancelled" ); } catch (e) { return false; } }, _checkGroupOrderStatus: function (callback) { var self = this; var done = function () { if (typeof callback === "function") { callback(); } }; var xhr = new XMLHttpRequest(); xhr.open("POST", "/eskaera/check-status", true); xhr.setRequestHeader("Content-Type", "application/json"); xhr.onload = function () { if (xhr.status !== 200) { done(); return; } try { var data = JSON.parse(xhr.responseText || "{}"); if (data && data.is_open === false) { self._clearCurrentOrderCartSilently(); } } catch (e) { console.warn("[groupOrderShop] check-status parse error", e); } done(); }; xhr.onerror = function () { done(); }; xhr.send( JSON.stringify({ order_id: this.orderId, }) ); }, _showNotification: function (message, type, duration) { // type: 'success', 'error', 'warning', 'info' // duration: milliseconds (default 8000 = 8 seconds) type = type || "info"; duration = duration || 8000; var notification = document.createElement("div"); notification.className = "alert alert-" + type + " alert-dismissible fade show position-fixed"; notification.style.cssText = "top: 80px; right: 20px; z-index: 9999; min-width: 300px; max-width: 500px; font-size: 18px; padding: 1.5rem; transition: opacity 0.3s ease-out;"; notification.innerHTML = message + ''; document.body.appendChild(notification); setTimeout(function () { notification.classList.remove("show"); setTimeout(function () { if (notification.parentNode) { notification.parentNode.removeChild(notification); } }, 300); }, duration); }, _getLabels: function () { // Get current labels from window.groupOrderShop which is updated by checkout_labels.js console.log("[_getLabels] Starting label resolution..."); console.log("[_getLabels] window.groupOrderShop exists:", !!window.groupOrderShop); if (window.groupOrderShop && window.groupOrderShop.labels) { console.log("[_getLabels] window.groupOrderShop.labels exists"); var keysCount = Object.keys(window.groupOrderShop.labels).length; console.log("[_getLabels] Keys count:", keysCount); if (keysCount > 0) { console.log( "[_getLabels] ✅ USING window.groupOrderShop.labels (from endpoint):", window.groupOrderShop.labels ); return window.groupOrderShop.labels; } } console.log("[_getLabels] window.groupOrderShop check failed, trying this.labels"); if (this.labels && Object.keys(this.labels).length > 0) { console.log("[_getLabels] ⚠️ USING this.labels (fallback):", this.labels); return this.labels; } console.log("[_getLabels] ❌ USING default labels"); return this._getDefaultLabels(); }, _initializeTooltips: function () { console.log("[_initializeTooltips] Initializing tooltips with translated labels..."); var self = this; var labels = this._getLabels(); // Map of button IDs/classes to label keys var tooltipMap = { "save-cart-btn": "save_cart", "reload-cart-btn": "reload_cart", "confirm-order-btn": "save_cart", "remove-from-cart": "remove_item", }; // Map of href patterns to label keys var hrefPatterns = [ { pattern: /\/checkout$/, labelKey: "proceed_to_checkout" }, { pattern: /\/eskaera\/\d+$/, labelKey: "back_to_cart" }, ]; // Find all elements with data-bs-toggle="tooltip" var tooltipElements = document.querySelectorAll('[data-bs-toggle="tooltip"]'); console.log("[_initializeTooltips] Found", tooltipElements.length, "tooltip elements"); // Also update category select placeholder text var categorySelect = document.getElementById("realtime-category-select"); if (categorySelect && categorySelect.options[0]) { var allCategoriesLabel = labels["browse_categories"] || labels["all_categories"] || "Browse Product Categories"; categorySelect.options[0].text = allCategoriesLabel; console.log( "[_initializeTooltips] Updated category select option to:", allCategoriesLabel ); } tooltipElements.forEach(function (element) { var tooltipText = null; var labelKey = null; // Check ID-based mapping if (element.id && tooltipMap[element.id]) { labelKey = tooltipMap[element.id]; tooltipText = labels[labelKey]; } // Check class-based mapping if (!tooltipText) { for (var key in tooltipMap) { if (element.classList.contains(key)) { labelKey = tooltipMap[key]; tooltipText = labels[labelKey]; break; } } } // Check href-based mapping if (!tooltipText && element.href) { for (var i = 0; i < hrefPatterns.length; i++) { if (hrefPatterns[i].pattern.test(element.href)) { labelKey = hrefPatterns[i].labelKey; tooltipText = labels[labelKey]; break; } } } // Fallback: use data-bs-title if no label found if (!tooltipText) { tooltipText = element.getAttribute("data-bs-title"); } if (tooltipText) { element.setAttribute("title", tooltipText); console.log( "[_initializeTooltips] Set title on", element.id || element.className, "→", tooltipText, "(label key:", labelKey, ")" ); } else { console.warn( "[_initializeTooltips] No tooltip text found for", element.id || element.className ); } }); }, _showConfirmation: function (message, onConfirm, onCancel) { var self = this; // Get current labels - may be updated by checkout_labels.js endpoint var labels = this._getLabels(); console.log("[_showConfirmation] Using labels:", labels); // Create modal backdrop var backdrop = document.createElement("div"); backdrop.className = "modal-backdrop fade show"; backdrop.style.zIndex = "9998"; // Create modal var modal = document.createElement("div"); modal.className = "modal fade show d-block"; modal.style.zIndex = "9999"; modal.innerHTML = '"; document.body.appendChild(backdrop); document.body.appendChild(modal); var closeModal = function () { modal.classList.remove("show"); backdrop.classList.remove("show"); setTimeout(function () { if (modal.parentNode) modal.parentNode.removeChild(modal); if (backdrop.parentNode) backdrop.parentNode.removeChild(backdrop); }, 150); }; document.getElementById("modal-confirm").addEventListener("click", function () { closeModal(); if (onConfirm) onConfirm(); }); document.getElementById("modal-cancel").addEventListener("click", function () { closeModal(); if (onCancel) onCancel(); }); document.getElementById("modal-close-x").addEventListener("click", function () { closeModal(); if (onCancel) onCancel(); }); }, _attachEventListeners: function () { var self = this; // Helper function to round decimals correctly function roundDecimal(value, decimals) { var factor = Math.pow(10, decimals); return Math.round(value * factor) / factor; } // Safety net: ensure add-to-cart buttons stay enabled in case a stale // cached template left the "disabled" attribute or a legacy class. // Stock validation happens server-side on confirm; here we keep the UI usable. var forceEnableCartButtons = function () { var buttons = document.querySelectorAll(".add-to-cart-btn"); buttons.forEach(function (btn) { if (btn.hasAttribute("disabled")) { btn.removeAttribute("disabled"); } btn.classList.remove("btn-disabled"); }); }; forceEnableCartButtons(); // ============ ATTACH CHECKOUT BUTTONS (ALWAYS, on any page) ============ // These buttons exist on checkout page, not on cart pages if (!this._cartCheckoutListenersAttached) { console.log("[_attachEventListeners] Attaching checkout button listeners..."); // Button to save as draft (in checkout page) var saveBtn = document.getElementById("save-order-btn"); console.log("[_attachEventListeners] save-order-btn found:", !!saveBtn); if (saveBtn) { saveBtn.addEventListener("click", function (e) { console.log("[CLICK] save-order-btn clicked"); e.preventDefault(); self._saveOrderDraft(); }); } // Confirm order button (in checkout page) var confirmBtn = document.getElementById("confirm-order-btn"); console.log("[_attachEventListeners] confirm-order-btn found:", !!confirmBtn); if (confirmBtn) { confirmBtn.addEventListener("click", function (e) { console.log("[CLICK] confirm-order-btn clicked (save draft)"); e.preventDefault(); self._saveOrderDraft(); }); } // Button to reload from draft (in My Cart header - cart pages) var reloadCartBtn = document.getElementById("reload-cart-btn"); console.log("[_attachEventListeners] reload-cart-btn found:", !!reloadCartBtn); if (reloadCartBtn) { reloadCartBtn.addEventListener("click", function (e) { console.log("[CLICK] reload-cart-btn clicked"); e.preventDefault(); self._loadDraftCart(); }); } // Button to save cart as draft (in My Cart header - shop pages) var saveCartBtn = document.getElementById("save-cart-btn"); console.log("[_attachEventListeners] save-cart-btn found:", !!saveCartBtn); if (saveCartBtn) { saveCartBtn.addEventListener("click", function (e) { console.log("[CLICK] save-cart-btn clicked"); e.preventDefault(); self._saveCartAsDraft(); }); } this._cartCheckoutListenersAttached = true; console.log("[_attachEventListeners] Checkout listeners attached (one-time)"); } // ============ LAZY LOADING: Load More Button ============ this._attachLoadMoreListener(); // ============ USE EVENT DELEGATION FOR QUANTITY & CART BUTTONS ============ // This way, new products loaded via AJAX will automatically have listeners var productsGrid = document.getElementById("products-grid"); if (!productsGrid) { console.log("[_attachEventListeners] No products-grid found (checkout page?)"); return; } // Shared handler for add-to-cart to reuse across grid/document listeners var handleAddToCart = function (e) { if (e._groupOrderAddToCartHandled) { return; } var cartBtn = e.target.closest(".add-to-cart-btn"); if (!cartBtn) return; e._groupOrderAddToCartHandled = true; e.preventDefault(); var form = cartBtn.closest(".add-to-cart-form"); if (!form) return; var productId = form.getAttribute("data-product-id"); var productName = form.getAttribute("data-product-name") || "Product"; var productPrice = parseFloat(form.getAttribute("data-product-price")) || 0; var quantityInput = form.querySelector(".product-qty"); var quantity = quantityInput ? parseFloat(quantityInput.value) : 1; // Block add-to-cart if product is flagged out of stock (from template) var isOutOfStock = (form.getAttribute("data-out-of-stock") || "false") === "true" || (cartBtn.getAttribute("data-out-of-stock") || "false") === "true"; // Fallback guards in case cached markup drops the data attribute if (!isOutOfStock) { var btnTitle = (cartBtn.getAttribute("title") || "").toLowerCase(); var btnAria = (cartBtn.getAttribute("aria-label") || "").toLowerCase(); var iconEl = cartBtn.querySelector("i"); var hasBanIcon = iconEl && iconEl.classList.contains("fa-ban"); if ( hasBanIcon || btnTitle.includes("out of stock") || btnTitle.includes("sin stock") || btnAria.includes("out of stock") || btnAria.includes("sin stock") ) { isOutOfStock = true; } } if (isOutOfStock) { var labels = self._getLabels(); self._showNotification( labels.out_of_stock || "Product is out of stock", "warning" ); return; } console.log("Adding:", { productId: productId, productName: productName, productPrice: productPrice, quantity: quantity, }); if (quantity > 0) { self._addToCart(productId, productName, productPrice, quantity); } else { var labels2 = self._getLabels(); self._showNotification( labels2.invalid_quantity || "Please enter a valid quantity", "warning" ); } }; // First, adjust quantity steps for all existing inputs var unitInputs = document.querySelectorAll(".product-qty"); console.log("=== ADJUSTING QUANTITY STEPS (from data-quantity-step) ==="); console.log("Found " + unitInputs.length + " quantity inputs"); for (var j = 0; j < unitInputs.length; j++) { var form = unitInputs[j].closest(".add-to-cart-form"); var step = form ? form.getAttribute("data-quantity-step") : null; if (!step) step = "1"; unitInputs[j].step = step; unitInputs[j].min = step; unitInputs[j].value = "1"; // Para debug, también marcamos si es unidad o no var uomCategory = form ? form.getAttribute("data-uom-category") : ""; var isUnitCategory = /^unit/i.test(uomCategory) || /^unidad/i.test(uomCategory); unitInputs[j].dataset.isUnit = isUnitCategory ? "true" : "false"; console.log( "Input #" + j + ": step=" + step + ', UoM="' + uomCategory + '", isUnit=' + unitInputs[j].dataset.isUnit ); } console.log("=== END ADJUSTING QUANTITY STEPS ==="); // IMPORTANT: Do NOT clone the grid node - this destroys all products! // Instead, use a flag to prevent adding duplicate event listeners if (productsGrid._delegationListenersAttached) { console.log( "[_attachEventListeners] Grid delegation listeners already attached, skipping" ); return; } productsGrid._delegationListenersAttached = true; console.log("[_attachEventListeners] Attaching grid delegation listeners (one-time)"); // Quantity decrease button (via event delegation) productsGrid.addEventListener("click", function (e) { var decreaseBtn = e.target.closest(".qty-decrease"); if (!decreaseBtn) return; e.preventDefault(); var productId = decreaseBtn.getAttribute("data-product-id"); var input = document.getElementById("qty_" + productId); if (!input) return; var step = parseFloat(input.step) || 1; var currentValue = parseFloat(input.value) || 0; var min = parseFloat(input.min) || 0; var newValue = Math.max(min, roundDecimal(currentValue - step, 1)); // Si es unidad, mostrar como entero if (input.dataset.isUnit === "true") { input.value = Math.floor(newValue); } else { input.value = newValue; } }); // Quantity increase button (via event delegation) productsGrid.addEventListener("click", function (e) { var increaseBtn = e.target.closest(".qty-increase"); if (!increaseBtn) return; e.preventDefault(); var productId = increaseBtn.getAttribute("data-product-id"); var input = document.getElementById("qty_" + productId); if (!input) return; var step = parseFloat(input.step) || 1; var currentValue = parseFloat(input.value) || 0; var newValue = roundDecimal(currentValue + step, 1); // Si es unidad, mostrar como entero if (input.dataset.isUnit === "true") { input.value = Math.floor(newValue); } else { input.value = newValue; } }); // Add to cart button (via event delegation on grid) productsGrid.addEventListener("click", handleAddToCart); // Also attach a document-level delegation as fallback for dynamically // inserted products (infinite scroll) in case the grid listener is lost // after DOM replacement. if (!this._docCartListenerAttached) { document.addEventListener("click", handleAddToCart, true); this._docCartListenerAttached = true; } }, _addToCart: function (productId, productName, productPrice, quantity) { console.log("_addToCart called with:", { productId: productId, productName: productName, productPrice: productPrice, quantity: quantity, }); if (!this.cart[productId]) { this.cart[productId] = { name: productName, price: productPrice, qty: 0, }; } this.cart[productId].qty += quantity; console.log("Product added:", productId, this.cart[productId]); this._saveCart(); this._updateCartDisplay(); // Show animation instead of alert this._showAddedAnimation(productName); // Use aria-live to announce to screen readers var liveRegion = document.getElementById("cart-items-container"); if (liveRegion) { liveRegion.setAttribute("aria-live", "assertive"); setTimeout(function () { liveRegion.setAttribute("aria-live", "polite"); }, 1000); } }, _showAddedAnimation: function (productName) { var self = this; // Create toast element var toast = document.createElement("div"); toast.className = "toast-notification"; // Get translated "added to cart" label from server var labels = this._getLabels(); var addedMsg = labels.added_to_cart || "added to cart"; toast.innerHTML = ' ' + productName + " " + addedMsg; document.body.appendChild(toast); // Force reflow to activate animation toast.offsetHeight; toast.classList.add("show"); // Remove after 3 seconds setTimeout(function () { toast.classList.remove("show"); setTimeout(function () { document.body.removeChild(toast); }, 300); }, 3000); }, /** * Get default translated labels (fallback if JSON fails to parse) */ _getDefaultLabels: function () { return { confirmation: "Confirmation", cancel: "Cancel", confirm: "Confirm", empty_cart: "Your cart is empty", save_draft_confirm: "Are you sure you want to save this cart as draft? Items to save: ", save_draft_reload: "You will be able to reload this cart later.", error_save_draft: "Error saving cart", save_cart: "Save Cart", reload_cart: "Reload Cart", proceed_to_checkout: "Proceed to Checkout", remove_item: "Remove Item", confirm_order: "Confirm Order", back_to_cart: "Back to Cart", all_categories: "All categories", // Draft modal labels draft_already_exists: "Draft Already Exists", draft_exists_message: "A saved draft already exists for the current order period.", draft_two_options: "You have two options:", draft_option1_title: "Option 1: Merge with Existing Draft", draft_option1_desc: "Combine your current cart with the existing draft.", draft_existing_items: "Existing draft has", draft_current_items: "Current cart has", draft_items_count: "item(s)", draft_merge_note: "Products will be merged by adding quantities. If a product exists in both, quantities will be combined.", draft_option2_title: "Option 2: Replace with Current Cart", draft_option2_desc: "Delete the old draft and save only the current cart items.", draft_replace_warning: "The existing draft will be permanently deleted.", draft_merge_btn: "Merge", draft_replace_btn: "Replace", }; }, /** * Update DOM elements with translated labels */ _updateDOMLabels: function (labels) { console.log("[UPDATE_LABELS] Starting DOM update with labels:", labels); // Map of element ID to label key var elementLabelMap = { "label-home-delivery": "home_delivery", "label-delivery-information": "delivery_information", "label-important": "important", "label-confirm-warning": "confirm_order_warning", }; // Update each element for (var elementId in elementLabelMap) { var element = document.getElementById(elementId); var labelKey = elementLabelMap[elementId]; var translatedText = labels[labelKey]; console.log( "[UPDATE_LABELS] Element:", elementId, "| Exists:", !!element, "| Label Key:", labelKey, "| Translated:", translatedText ); if (element && translatedText) { var oldText = element.textContent; element.textContent = translatedText; console.log( "[UPDATE_LABELS] ✅ Updated #" + elementId + ': "' + oldText + '" → "' + translatedText + '"' ); } else if (!element) { console.log("[UPDATE_LABELS] ❌ Element not found: #" + elementId); } else if (!translatedText) { console.log( "[UPDATE_LABELS] ❌ Label not found: " + labelKey + " (available keys: " + Object.keys(labels).join(", ") + ")" ); } } // Update delivery day text if available if ( window.groupOrderShop && window.groupOrderShop.labels && window.groupOrderShop.labels.delivery_info_template ) { var deliveryDayText = document.getElementById("delivery-day-text"); console.log("[UPDATE_LABELS] Delivery day text element exists:", !!deliveryDayText); if (deliveryDayText) { // Get delivery data from window.deliveryData first, then fallback to attributes var pickupDayIndex = ""; var pickupDate = ""; var deliveryNotice = ""; if (window.deliveryData) { console.log( "[UPDATE_LABELS] Using window.deliveryData:", window.deliveryData ); pickupDayIndex = window.deliveryData.pickupDay || ""; pickupDate = window.deliveryData.pickupDate || ""; deliveryNotice = window.deliveryData.deliveryNotice || ""; } else { console.log( "[UPDATE_LABELS] window.deliveryData not found, using data attributes" ); var wrap = document.getElementById("wrap"); pickupDayIndex = wrap ? wrap.getAttribute("data-pickup-day") : ""; pickupDate = wrap ? wrap.getAttribute("data-pickup-date") : ""; deliveryNotice = wrap ? wrap.getAttribute("data-delivery-notice") : ""; } // Normalize: convert "undefined" strings and null to empty for processing if (pickupDayIndex === "undefined" || pickupDayIndex === null) pickupDayIndex = ""; if (pickupDate === "undefined" || pickupDate === null) pickupDate = ""; if (deliveryNotice === "undefined" || deliveryNotice === null) deliveryNotice = ""; console.log("[UPDATE_LABELS] Delivery data (final):", { pickupDayIndex: pickupDayIndex, pickupDate: pickupDate, deliveryNotice: deliveryNotice, }); // Day names mapping var dayNames = { 0: "Monday", 1: "Tuesday", 2: "Wednesday", 3: "Thursday", 4: "Friday", 5: "Saturday", 6: "Sunday", }; // Get translated day names if available if (window.groupOrderShop && window.groupOrderShop.day_names) { dayNames = window.groupOrderShop.day_names; } // Get the day name from index var pickupDayName = pickupDayIndex && dayNames[pickupDayIndex] ? dayNames[pickupDayIndex] : pickupDayIndex; // Build message from template var msg = window.groupOrderShop.labels.delivery_info_template; msg = msg.replace("{pickup_day}", pickupDayName); msg = msg.replace("{pickup_date}", pickupDate); console.log("[UPDATE_LABELS] Built delivery message:", msg); // Build final HTML output var htmlOutput = msg; if (deliveryNotice) { // Replace newlines with
tags for HTML display htmlOutput = msg.replace(/\n/g, "
") + "

" + deliveryNotice.replace(/\n/g, "
"); console.log("[UPDATE_LABELS] Final HTML with notice:", htmlOutput); } else { htmlOutput = msg.replace(/\n/g, "
"); } deliveryDayText.innerHTML = htmlOutput; console.log( "[UPDATE_LABELS] ✅ Updated delivery day text with translated template" ); } } else { console.log("[UPDATE_LABELS] ❌ delivery_info_template label not found"); } }, _attachLoadMoreListener: function () { var self = this; var btn = document.getElementById("load-more-btn"); if (!btn) { console.log("[LAZY_LOADING] No load-more-btn found - lazy loading disabled"); return; } console.log("[LAZY_LOADING] Attaching load-more-btn listener"); btn.addEventListener("click", function (e) { e.preventDefault(); var orderId = btn.getAttribute("data-order-id"); var nextPage = btn.getAttribute("data-page"); var perPage = btn.getAttribute("data-per-page"); if (!orderId || !nextPage || !perPage) { console.error("[LAZY_LOADING] Missing data attributes", { orderId: orderId, nextPage: nextPage, perPage: perPage, }); return; } console.log("[LAZY_LOADING] Loading page", nextPage, "for order", orderId); // Show spinner: disable button and change text var originalText = btn.innerHTML; btn.disabled = true; btn.innerHTML = 'Loading...'; // AJAX GET request var xhr = new XMLHttpRequest(); var url = "/eskaera/" + orderId + "/load-page?page=" + nextPage; xhr.open("GET", url, true); xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); xhr.onload = function () { if (xhr.status === 200) { console.log("[LAZY_LOADING] Page loaded successfully"); // Parse response HTML var html = xhr.responseText; var grid = document.getElementById("products-grid"); if (!grid) { console.error("[LAZY_LOADING] products-grid not found"); btn.disabled = false; btn.innerHTML = originalText; return; } // Insert new products at the end of grid grid.insertAdjacentHTML("beforeend", html); console.log("[LAZY_LOADING] Products inserted into grid"); // Re-attach event listeners for new products self._attachEventListeners(); console.log("[LAZY_LOADING] Event listeners re-attached"); // Update button state var nextPageNum = parseInt(nextPage) + 1; // Check if there are more products by looking at the response // If response contains data-next="false" or similar, hide button // For now, we'll always increment and let backend handle has_next btn.setAttribute("data-page", nextPageNum); // Hide button if no more pages (check if response indicates no more) // Simple approach: if response is very short, assume no more products if (html.trim().length < 100) { console.log("[LAZY_LOADING] No more products, hiding load-more button"); btn.style.display = "none"; } else { // Re-enable button for next page btn.disabled = false; btn.innerHTML = originalText; } } else { console.error("[LAZY_LOADING] Error loading page:", xhr.status); btn.disabled = false; btn.innerHTML = originalText; var errorMsg = "Error loading more products (status: " + xhr.status + ")"; if (self._showNotification) { self._showNotification(errorMsg, "danger"); } else { alert(errorMsg); } } }; xhr.onerror = function () { console.error("[LAZY_LOADING] Network error"); btn.disabled = false; btn.innerHTML = originalText; var errorMsg = "Error: Network request failed"; if (self._showNotification) { self._showNotification(errorMsg, "danger"); } else { alert(errorMsg); } }; xhr.send(); }); }, _updateCartDisplay: function () { var cartContainer = document.getElementById("cart-items-container"); if (!cartContainer) return; // Get labels first before using them var labels = this._getLabels(); if (Object.keys(this.cart).length === 0) { cartContainer.innerHTML = '

' + (labels.empty_cart || "This order's cart is empty.") + "

"; return; } var html = '
'; var total = 0; var self = this; var removeItemLabel = labels.remove_item || "Remove Item"; Object.keys(this.cart).forEach(function (productId) { var item = self.cart[productId]; var subtotal = item.qty * item.price; total += subtotal; html += '
' + '
' + '
' + self.escapeHtml(item.name) + "
" + "
" + '
' + '' + parseFloat(item.qty).toFixed(1) + " x €" + item.price.toFixed(2) + "" + '
' + '€' + subtotal.toFixed(2) + "" + '" + "
" + "
" + "
"; }); html += "
"; html += '
' + "Total: €" + total.toFixed(2) + "
"; cartContainer.innerHTML = html; // Reassign event listeners to remove buttons var removeButtons = cartContainer.querySelectorAll(".remove-from-cart"); for (var i = 0; i < removeButtons.length; i++) { removeButtons[i].addEventListener("click", function (e) { e.preventDefault(); var productId = this.getAttribute("data-product-id"); self._removeFromCart(productId); }); } // Reinitialize tooltips for newly added remove buttons console.log("[_updateCartDisplay] Reinitializing tooltips for remove buttons"); var tooltipsToInit = cartContainer.querySelectorAll('[data-bs-toggle="tooltip"]'); tooltipsToInit.forEach(function (elem) { var bsTooltip = window.bootstrap?.Tooltip; if (bsTooltip) { // Destroy existing tooltip if any var existingTooltip = bsTooltip.getInstance(elem); if (existingTooltip) { existingTooltip.dispose(); } // Create new tooltip with updated title new bsTooltip(elem); console.log( "[_updateCartDisplay] Tooltip reinitialized for element:", elem.getAttribute("title") ); } }); }, _removeFromCart: function (productId) { console.log("Removing from cart:", productId); var removedItem = this.cart[productId]; var itemName = removedItem ? removedItem.name : "Product"; delete this.cart[productId]; this._saveCart(); // Use aria-live to announce to screen readers var liveRegion = document.getElementById("cart-items-container"); if (liveRegion) { liveRegion.setAttribute("aria-live", "assertive"); setTimeout(function () { liveRegion.setAttribute("aria-live", "polite"); }, 1000); } this._updateCartDisplay(); }, _saveCartAsDraft: function () { console.log("Saving cart as draft"); var self = this; var items = []; Object.keys(this.cart).forEach(function (productId) { var item = self.cart[productId]; items.push({ product_id: productId, product_name: item.name, quantity: item.qty, product_price: item.price, }); }); if (items.length === 0) { var labels = self._getLabels(); self._showNotification(labels.empty_cart || "Your cart is empty", "warning"); return; } // Call save-order endpoint which handles draft conflicts self._executeSaveCartAsDraft(items); }, _executeSaveCartAsDraft: function (items) { var self = this; var orderData = { order_id: this.orderId, items: items, merge_action: "replace", }; var xhr = new XMLHttpRequest(); xhr.open("POST", "/eskaera/save-order", true); xhr.setRequestHeader("Content-Type", "application/json"); xhr.onload = function () { if (xhr.status === 200) { try { var data = JSON.parse(xhr.responseText); var labels = self._getLabels(); if (data.success) { var successMsg = (labels.draft_saved_success || "Cart saved as draft successfully") + " (Order ID: " + data.sale_order_id + ")"; self._showNotification("✓ " + successMsg, "success", 5000); } else { self._showNotification( "Error: " + (data.error || labels.error_unknown || "Unknown error"), "danger" ); } } catch (e) { console.error("Error parsing response:", e); var labels = self._getLabels(); self._showNotification( labels.error_processing_response || "Error processing response", "danger" ); } } else { if (self._isClosedOrderResponse(xhr)) { self._clearCurrentOrderCartSilently(); self._updateCartDisplay(); return; } try { var errorData = JSON.parse(xhr.responseText); self._showNotification( "Error " + xhr.status + ": " + (errorData.error || "Request error"), "danger" ); } catch (e) { var labels = self._getLabels(); self._showNotification( labels.error_saving_draft || "Error saving cart (HTTP " + xhr.status + ")", "danger" ); } } }; xhr.onerror = function () { console.error("Error:", xhr); var labels = self._getLabels(); self._showNotification(labels.connection_error || "Connection error", "danger"); }; xhr.send(JSON.stringify(orderData)); }, _loadDraftCart: function () { console.log("Loading draft cart (manual)"); var self = this; // Check if cart has items var hasItems = Object.keys(this.cart).length > 0; if (hasItems) { // Ask for confirmation if cart has items var labels = this._getLabels(); var confirmMessage = (labels.reload_draft_confirm || "Are you sure you want to load your last saved draft?") + "\n\n" + (labels.reload_draft_replace || "This will replace your current cart items") + " (" + Object.keys(this.cart).length + " " + (labels.items_placeholder || "items") + ") " + (labels.reload_draft_with || "with the saved draft."); self._showConfirmation(confirmMessage, function () { // User confirmed - continue with load self._executeLoadDraftCart(); }); } else { // Cart is empty, load directly without confirmation self._executeLoadDraftCart(); } }, _executeLoadDraftCart: function () { var self = this; var orderData = { order_id: this.orderId, }; var xhr = new XMLHttpRequest(); xhr.open("POST", "/eskaera/load-draft", true); xhr.setRequestHeader("Content-Type", "application/json"); xhr.onload = function () { if (xhr.status === 200) { try { var data = JSON.parse(xhr.responseText); if (data.success) { // Clear current cart self.cart = {}; // Load items from draft order var items = data.items || []; items.forEach(function (item) { var productId = item.product_id; self.cart[productId] = { name: item.product_name, price: item.product_price, qty: item.quantity, }; }); // Save to localStorage self._saveCart(); // Update display self._updateCartDisplay(); var labels = self._getLabels(); var loadMsg = (labels.draft_loaded_success || "Loaded") + " " + items.length + " items from draft order (ID: " + data.sale_order_id + ")"; self._showNotification("✓ " + loadMsg, "success", 5000); } else { self._showNotification( "Error: " + (data.error || labels.error_unknown || "Unknown error"), "danger" ); } } catch (e) { console.error("Error parsing response:", e); var labels = self._getLabels(); self._showNotification( labels.error_processing_response || "Error processing response", "danger" ); } } else { if (self._isClosedOrderResponse(xhr)) { self._clearCurrentOrderCartSilently(); self._updateCartDisplay(); return; } try { var errorData = JSON.parse(xhr.responseText); self._showNotification( "Error " + xhr.status + ": " + (errorData.error || "Request error"), "danger" ); } catch (e) { var labels = self._getLabels(); self._showNotification( labels.error_loading_draft || "Error loading draft (HTTP " + xhr.status + ")", "danger" ); } } }; xhr.onerror = function () { console.error("Error:", xhr); var labels = self._getLabels(); self._showNotification(labels.connection_error || "Connection error", "danger"); }; xhr.send(JSON.stringify(orderData)); }, _saveOrderDraft: function () { console.log("[_saveOrderDraft] Starting - this.orderId:", this.orderId); var self = this; var labels = this._getLabels(); var cartKey = "eskaera_" + this.orderId + "_cart"; var storedCart = localStorage.getItem(cartKey); var cart = storedCart ? JSON.parse(storedCart) : this.cart; var items = []; Object.keys(cart).forEach(function (productId) { var item = cart[productId]; items.push({ product_id: productId, product_name: item.name, quantity: item.qty, product_price: item.price, }); }); var orderData = { order_id: this.orderId, items: items, }; var xhr = new XMLHttpRequest(); xhr.open("POST", "/eskaera/save-order", true); xhr.setRequestHeader("Content-Type", "application/json"); xhr.onload = function () { if (xhr.status === 200) { try { var data = JSON.parse(xhr.responseText); console.log("Response:", data); if (data.success) { var successMsg = labels.draft_saved_success || labels.draft_saved || "Order saved as draft successfully"; self._showNotification("\u2713 " + successMsg, "success", 5000); } else { self._showNotification( "Error: " + (data.error || labels.error_unknown || "Unknown error"), "danger" ); } } catch (e) { console.error("Error parsing response:", e); self._showNotification("Error processing response", "danger"); } } else { if (self._isClosedOrderResponse(xhr)) { self._clearCurrentOrderCartSilently(); self._updateCartDisplay(); return; } try { var errorData = JSON.parse(xhr.responseText); console.error("HTTP error:", xhr.status, errorData); self._showNotification( "Error " + xhr.status + ": " + (errorData.error || "Request error"), "danger" ); } catch (e) { console.error("HTTP error:", xhr.status, xhr.responseText); self._showNotification( "Error saving order (HTTP " + xhr.status + ")", "danger" ); } } }; xhr.onerror = function () { console.error("Error:", xhr); var labels = self._getLabels(); self._showNotification(labels.connection_error || "Connection error", "danger"); }; xhr.send(JSON.stringify(orderData)); }, _confirmOrder: function () { console.log("=== _confirmOrder started ==="); console.log("orderId:", this.orderId); // IMPORTANT: Read cart directly from localStorage, not from this.cart // because home_delivery.js updates localStorage directly var cartKey = "eskaera_" + this.orderId + "_cart"; var storedCart = localStorage.getItem(cartKey); console.log("localStorage[" + cartKey + "]:", storedCart); // Parse cart from localStorage (more reliable than this.cart) var cart = storedCart ? JSON.parse(storedCart) : this.cart; console.log("Parsed cart from localStorage:", cart); var self = this; var items = []; Object.keys(cart).forEach(function (productId) { var item = cart[productId]; console.log("Processing cart item: productId=" + productId + ", item=", item); items.push({ product_id: productId, product_name: item.name, quantity: item.qty, unit_price: item.price, }); }); console.log("Items to send to server:", items); console.log("Total items count:", items.length); // Check if home delivery is enabled var deliveryCheckbox = document.getElementById("home-delivery-checkbox"); var isDelivery = deliveryCheckbox ? deliveryCheckbox.checked : false; var orderData = { order_id: this.orderId, items: items, is_delivery: isDelivery, }; var xhr = new XMLHttpRequest(); xhr.open("POST", "/eskaera/confirm", true); xhr.setRequestHeader("Content-Type", "application/json"); console.log("Sending request to /eskaera/confirm with data:", orderData); xhr.onload = function () { if (xhr.status === 200) { try { var data = JSON.parse(xhr.responseText); console.log("Response:", data); var labels = self._getLabels(); if (data.success) { var message = data.message || labels.order_confirmed || "Order confirmed"; self._showNotification("✓ " + message, "success", 4000); // Clear cart from localStorage localStorage.removeItem("eskaera_" + self.orderId + "_cart"); console.log("Cart cleared from localStorage after confirmation"); // Save confirmation message in sessionStorage to display on eskaera page sessionStorage.setItem("confirmation_message", message); // Wait a moment before redirecting to let user see the message setTimeout(function () { window.location.href = data.redirect_url || "/eskaera"; }, 4000); } else { var unknownErrorMsg = labels.error_unknown || labels.error_unknown || "Unknown error"; self._showNotification( "Error: " + (data.error || unknownErrorMsg), "danger" ); } } catch (e) { console.error("Error parsing response:", e); var labels = self._getLabels(); self._showNotification( labels.error_processing_response || "Error processing response", "danger" ); } } else { if (self._isClosedOrderResponse(xhr)) { self._clearCurrentOrderCartSilently(); self._updateCartDisplay(); return; } try { var errorData = JSON.parse(xhr.responseText); console.error("HTTP error:", xhr.status, errorData); self._showNotification( "Error " + xhr.status + ": " + (errorData.error || "Request error"), "danger" ); } catch (e) { console.error("HTTP error:", xhr.status, xhr.responseText); self._showNotification( "Error confirming order (HTTP " + xhr.status + ")", "danger" ); } } }; xhr.onerror = function () { console.error("Error:", xhr); var labels = self._getLabels(); self._showNotification(labels.connection_error || "Connection error", "danger"); }; xhr.send(JSON.stringify(orderData)); }, escapeHtml: function (text) { var div = document.createElement("div"); div.textContent = text; return div.innerHTML; }, }; // Initialize when DOM is ready document.addEventListener("DOMContentLoaded", function () { console.log("DOM loaded"); console.log("Looking for #cart-items-container..."); console.log("Looking for #confirm-order-btn..."); var cartContainer = document.getElementById("cart-items-container"); var confirmBtn = document.getElementById("confirm-order-btn"); console.log("cart-items-container found:", !!cartContainer); console.log("confirm-order-btn found:", !!confirmBtn); // Always initialize - it handles both cart pages and checkout pages console.log("Calling init()"); var result = window.groupOrderShop.init(); console.log("init() result:", result); }); // Handle confirm order buttons in portal (My Orders page) document.addEventListener("click", function (e) { if (e.target.closest(".confirm-order-btn")) { e.preventDefault(); var btn = e.target.closest(".confirm-order-btn"); var groupOrderId = btn.getAttribute("data-group-order-id"); var orderId = btn.getAttribute("data-order-id"); console.log("Confirm order clicked: order=" + orderId + ", group=" + groupOrderId); if (!groupOrderId || !orderId) { if (window.groupOrderShop && window.groupOrderShop._showNotification) { window.groupOrderShop._showNotification( "Error: Missing order or group information", "danger" ); } else { alert("Error: Missing order or group information"); } return; } // Show loading state var originalText = btn.innerHTML; btn.disabled = true; btn.innerHTML = ''; // Send AJAX request to confirm the order var xhr = new XMLHttpRequest(); xhr.open("POST", "/eskaera/" + groupOrderId + "/confirm/" + orderId, true); xhr.setRequestHeader("Content-Type", "application/json"); xhr.onload = function () { btn.disabled = false; if (xhr.status === 200) { try { var response; if ( typeof xhr.responseText === "string" && xhr.responseText.trim() !== "" ) { response = JSON.parse(xhr.responseText); } else { response = {}; } console.log("Response from confirm endpoint:", response); // Odoo JSON-RPC wraps the result in response.result var result = response.result || response; if (result && result.success) { console.log( "Order confirmed successfully: order_id=" + result.order_id ); btn.innerHTML = ''; btn.classList.remove("btn-success"); btn.classList.add("btn-secondary"); btn.disabled = true; btn.title = "Order confirmed"; // Show success notification with order_id var message = "Order #" + result.order_id + " confirmed successfully"; if (result.message) { message = result.message + " (Order #" + result.order_id + ")"; } if (window.groupOrderShop && window.groupOrderShop._showNotification) { window.groupOrderShop._showNotification(message, "success", 4000); } else { alert(message); } } else { console.error("Error confirming order:", result); btn.innerHTML = originalText; var errorMsg = "Error: " + (result && result.error ? result.error : labels.error_unknown || "Unknown error"); if (window.groupOrderShop && window.groupOrderShop._showNotification) { window.groupOrderShop._showNotification(errorMsg, "danger"); } else { alert(errorMsg); } } } catch (e) { console.error("Error parsing response:", e); console.error("Response text was:", xhr.responseText); btn.innerHTML = originalText; var errorMsg = "Error processing response: " + e.message; if (window.groupOrderShop && window.groupOrderShop._showNotification) { window.groupOrderShop._showNotification(errorMsg, "danger"); } else { alert(errorMsg); } } } else { console.error("HTTP error:", xhr.status, xhr.responseText); btn.innerHTML = originalText; var errorMsg = "Error " + xhr.status + ": Failed to confirm order"; if (window.groupOrderShop && window.groupOrderShop._showNotification) { window.groupOrderShop._showNotification(errorMsg, "danger"); } else { alert(errorMsg); } } }; xhr.onerror = function () { btn.disabled = false; btn.innerHTML = originalText; var errorMsg = "Error: Network request failed"; if (window.groupOrderShop && window.groupOrderShop._showNotification) { window.groupOrderShop._showNotification(errorMsg, "danger"); } else { alert(errorMsg); } }; xhr.send(JSON.stringify({})); } }); // Also try to initialize after a delay in case DOM // takes longer to load setTimeout(function () { if (!window.groupOrderShop.orderId) { console.log("Reintentando init() después de delay..."); var cartContainer = document.getElementById("cart-items-container"); var confirmBtn = document.getElementById("confirm-order-btn"); if (cartContainer || confirmBtn) { console.log("Llamando a init() en delay"); var result = window.groupOrderShop.init(); console.log("init() en delay resultado:", result); } } }, 1000); })();