addons-cm/website_sale_aplicoop/static/src/js/website_sale.js

2001 lines
85 KiB
JavaScript

/*
* 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 =
'<div class="alert alert-danger">' + "Error: Order ID not found</div>";
}
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 +
'<button type="button" class="btn-close" data-bs-dismiss="alert"></button>';
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 =
'<div class="modal-dialog modal-dialog-centered">' +
'<div class="modal-content">' +
'<div class="modal-header">' +
'<h5 class="modal-title">' +
(labels.confirmation || "Confirmation") +
"</h5>" +
'<button type="button" class="btn-close" id="modal-close-x"></button>' +
"</div>" +
'<div class="modal-body">' +
"<p>" +
message.replace(/\n/g, "<br>") +
"</p>" +
"</div>" +
'<div class="modal-footer">' +
'<button type="button" class="btn btn-secondary" id="modal-cancel">' +
(labels.cancel || "Cancel") +
"</button>" +
'<button type="button" class="btn btn-primary" id="modal-confirm">' +
(labels.confirm || "Confirm") +
"</button>" +
"</div>" +
"</div>" +
"</div>";
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 =
'<i class="fa fa-check"></i> <strong>' + productName + "</strong> " + 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 <br> tags for HTML display
htmlOutput =
msg.replace(/\n/g, "<br>") +
"<br><br>" +
deliveryNotice.replace(/\n/g, "<br>");
console.log("[UPDATE_LABELS] Final HTML with notice:", htmlOutput);
} else {
htmlOutput = msg.replace(/\n/g, "<br>");
}
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 = '<i class="fa fa-spinner fa-spin me-2"></i>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 =
'<p class="text-muted" style="font-size: 1.1rem;">' +
(labels.empty_cart || "This order's cart is empty.") +
"</p>";
return;
}
var html = '<div class="list-group">';
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 +=
'<div class="list-group-item" style="font-size: 1.05rem;">' +
'<div class="mb-2">' +
'<h6 class="mb-0" style="font-size: 1.1rem;">' +
self.escapeHtml(item.name) +
"</h6>" +
"</div>" +
'<div class="d-flex justify-content-between align-items-center">' +
'<small class="text-muted" style="font-size: 1rem;">' +
parseFloat(item.qty).toFixed(1) +
" x €" +
item.price.toFixed(2) +
"</small>" +
'<div class="d-flex align-items-center gap-2">' +
'<strong style="font-size: 1.1rem;">€' +
subtotal.toFixed(2) +
"</strong>" +
'<button class="btn btn-sm btn-danger remove-from-cart" ' +
'data-product-id="' +
productId +
'" aria-label="Remove ' +
self.escapeHtml(item.name) +
' from cart" data-bs-toggle="tooltip" title="' +
removeItemLabel +
'">' +
'<i class="fa fa-trash" aria-hidden="true"></i>' +
"</button>" +
"</div>" +
"</div>" +
"</div>";
});
html += "</div>";
html +=
'<div class="cart-total mt-3 pt-3" style="font-size: 1.2rem; font-weight: bold;">' +
"Total: €" +
total.toFixed(2) +
"</div>";
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 = '<i class="fa fa-spinner fa-spin"></i>';
// 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 = '<i class="fa fa-check"></i>';
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);
})();