addons-cm/website_sale_aplicoop/static/src/js/website_sale.js
2026-02-11 15:32:11 +01:00

1788 lines
82 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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);
// Wait for i18nManager to load translations from server
i18nManager.init().then(function() {
console.log('[groupOrderShop] Translations loaded from server');
self.labels = i18nManager.getAll();
// Initialize event listeners and state after translations are ready
self._attachEventListeners();
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));
},
_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': 'confirm_order',
'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;
// Adjust quantity step based on UoM category
// Categories without decimals (per unit): "Unit", "Units", etc.
// Categories with decimals: "Weight", "Volume", "Length", etc.
var unitInputs = document.querySelectorAll('.product-qty');
console.log('=== ADJUSTING QUANTITY STEPS ===');
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 uomCategory = form.getAttribute('data-uom-category') || 'Unit';
// If category is "Unit" (English) or "Units" (plural), use step=1 (no decimals)
// If other category (Weight, Volume, etc.), use step=0.1 (with decimals)
var isUnitCategory = /^unit/i.test(uomCategory) || /^unidad/i.test(uomCategory);
if (isUnitCategory) {
unitInputs[j].step = '1';
unitInputs[j].min = '1';
unitInputs[j].value = '1';
unitInputs[j].dataset.isUnit = 'true';
console.log('Input #' + j + ': UoM="' + uomCategory + '" → step=1 (integer)');
} else {
// Para peso, volumen, etc.
unitInputs[j].step = '0.1';
unitInputs[j].min = '0.1';
unitInputs[j].value = '1';
unitInputs[j].dataset.isUnit = 'false';
console.log('Input #' + j + ': UoM="' + uomCategory + '" → step=0.1 (decimal)');
}
}
console.log('=== END ADJUSTING QUANTITY STEPS ===');
// Botones + y - para aumentar/disminuir cantidad
// Helper function to round decimals correctly
function roundDecimal(value, decimals) {
var factor = Math.pow(10, decimals);
return Math.round(value * factor) / factor;
}
// Remove old listeners by cloning elements (to avoid duplication)
var decreaseButtons = document.querySelectorAll('.qty-decrease');
for (var k = 0; k < decreaseButtons.length; k++) {
var newBtn = decreaseButtons[k].cloneNode(true);
decreaseButtons[k].parentNode.replaceChild(newBtn, decreaseButtons[k]);
}
var increaseButtons = document.querySelectorAll('.qty-increase');
for (var k = 0; k < increaseButtons.length; k++) {
var newBtn = increaseButtons[k].cloneNode(true);
increaseButtons[k].parentNode.replaceChild(newBtn, increaseButtons[k]);
}
// Ahora asignar nuevos listeners
decreaseButtons = document.querySelectorAll('.qty-decrease');
for (var k = 0; k < decreaseButtons.length; k++) {
decreaseButtons[k].addEventListener('click', function(e) {
e.preventDefault();
var productId = this.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;
}
});
}
increaseButtons = document.querySelectorAll('.qty-increase');
for (var k = 0; k < increaseButtons.length; k++) {
increaseButtons[k].addEventListener('click', function(e) {
e.preventDefault();
var productId = this.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;
}
});
}
// Botones de agregar al carrito
var buttons = document.querySelectorAll('.add-to-cart-btn');
for (var i = 0; i < buttons.length; i++) {
buttons[i].addEventListener('click', function(e) {
e.preventDefault();
var form = this.closest('.add-to-cart-form');
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;
console.log('Adding:', {
productId: productId,
productName: productName,
productPrice: productPrice,
quantity: quantity
});
if (quantity > 0) {
self._addToCart(productId, productName,
productPrice, quantity);
} else {
var labels = self._getLabels();
self._showNotification(labels.invalid_quantity || 'Please enter a valid quantity', 'warning');
}
});
}
// Button to save cart as draft (in My Cart header)
var savCartBtn = document.getElementById('save-cart-btn');
if (savCartBtn) {
// Remove old listeners by cloning
var savCartBtnNew = savCartBtn.cloneNode(true);
savCartBtn.parentNode.replaceChild(savCartBtnNew, savCartBtn);
savCartBtnNew.addEventListener('click', function(e) {
e.preventDefault();
self._saveCartAsDraft();
});
}
// Button to reload from draft (in My Cart header)
var reloadCartBtn = document.getElementById('reload-cart-btn');
if (reloadCartBtn) {
// Remove old listeners by cloning
var reloadCartBtnNew = reloadCartBtn.cloneNode(true);
reloadCartBtn.parentNode.replaceChild(reloadCartBtnNew, reloadCartBtn);
reloadCartBtnNew.addEventListener('click', function(e) {
e.preventDefault();
self._loadDraftCart();
});
}
// Button to save as draft
var saveBtn = document.getElementById('save-order-btn');
if (saveBtn) {
saveBtn.addEventListener('click', function(e) {
e.preventDefault();
self._saveOrderDraft();
});
}
// Confirm order button
var confirmBtn = document.getElementById('confirm-order-btn');
if (confirmBtn) {
confirmBtn.addEventListener('click', function(e) {
e.preventDefault();
self._confirmOrder();
});
}
},
_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 this week.',
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');
}
},
_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
};
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 if (data.existing_draft) {
// A draft already exists - show modal with merge/replace options
self._showDraftConflictModal(data);
} 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 {
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 {
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('Saving order as draft:', this.orderId);
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
});
});
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) {
self._showNotification('✓ Order saved as draft successfully', 'success', 5000);
} else if (data.existing_draft) {
// A draft already exists - show modal with merge/replace options
self._showDraftConflictModal(data);
} 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 {
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));
},
_showDraftConflictModal: function(data) {
/**
* Show modal with merge/replace options for existing draft.
* Uses labels from window.groupOrderShop.labels or falls back to defaults.
*/
var self = this;
// Get labels - they should already be loaded by page init
var labels = (window.groupOrderShop && window.groupOrderShop.labels) ? window.groupOrderShop.labels : self._getDefaultLabels();
console.log('[_showDraftConflictModal] Using labels:', Object.keys(labels).length, 'keys available');
var existing_items = data.existing_items || [];
var current_items = data.current_items || [];
var existing_draft_id = data.existing_draft_id;
// Create modal HTML with inline styles (no Bootstrap needed)
var modalHTML = `
<div id="draftConflictModal" style="
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 9999;
">
<div style="
background: white;
border-radius: 8px;
max-width: 500px;
width: 90%;
max-height: 90vh;
overflow-y: auto;
box-shadow: 0 4px 20px rgba(0,0,0,0.3);
">
<!-- Header -->
<div style="
padding: 20px;
border-bottom: 1px solid #ddd;
display: flex;
justify-content: space-between;
align-items: center;
">
<h5 style="margin: 0; font-size: 1.25rem;">${labels.draft_already_exists || 'Draft Already Exists'}</h5>
<button class="draft-modal-close" style="
background: none;
border: none;
font-size: 24px;
cursor: pointer;
color: #666;
">×</button>
</div>
<!-- Body -->
<div style="padding: 20px;">
<p><strong>${labels.draft_exists_message || 'A draft already exists'}</strong></p>
<p style="margin-top: 15px;">${labels.draft_two_options || 'You have two options:'}</p>
<!-- Option 1 -->
<div style="
border: 1px solid #ddd;
border-radius: 4px;
margin-bottom: 15px;
overflow: hidden;
">
<div style="
background: #17a2b8;
color: white;
padding: 10px 15px;
font-weight: bold;
">
<i class="fa fa-code-fork"></i> ${labels.draft_option1_title || 'Option 1'}
</div>
<div style="padding: 15px;">
<p>${labels.draft_option1_desc || 'Merge with existing'}</p>
<ul style="margin: 10px 0; padding-left: 20px; font-size: 0.9rem;">
<li>${existing_items.length} ${labels.draft_items_count || 'items'} - ${labels.draft_existing_items || 'Existing'}</li>
<li>${current_items.length} ${labels.draft_items_count || 'items'} - ${labels.draft_current_items || 'Current'}</li>
</ul>
<p style="color: #666; font-size: 0.85rem; margin: 10px 0 0 0;">
${labels.draft_merge_note || 'Products will be merged'}
</p>
</div>
</div>
<!-- Option 2 -->
<div style="
border: 1px solid #ddd;
border-radius: 4px;
overflow: hidden;
">
<div style="
background: #ffc107;
color: black;
padding: 10px 15px;
font-weight: bold;
">
<i class="fa fa-refresh"></i> ${labels.draft_option2_title || 'Option 2'}
</div>
<div style="padding: 15px;">
<p>${labels.draft_option2_desc || 'Replace with current'}</p>
<p style="color: #666; font-size: 0.85rem; margin: 0;">
<i class="fa fa-exclamation-triangle"></i> ${labels.draft_replace_warning || 'Old draft will be deleted'}
</p>
</div>
</div>
</div>
<!-- Footer -->
<div style="
padding: 15px 20px;
border-top: 1px solid #ddd;
display: flex;
gap: 10px;
justify-content: flex-end;
">
<button class="draft-modal-close" style="
padding: 8px 16px;
background: #6c757d;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 0.9rem;
">${labels.cancel || 'Cancel'}</button>
<button id="mergeBtn" data-existing-id="${existing_draft_id}" style="
padding: 8px 16px;
background: #17a2b8;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 0.9rem;
">
<i class="fa fa-code-fork"></i> ${labels.draft_merge_btn || 'Merge'}
</button>
<button id="replaceBtn" data-existing-id="${existing_draft_id}" style="
padding: 8px 16px;
background: #ffc107;
color: black;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 0.9rem;
">
<i class="fa fa-refresh"></i> ${labels.draft_replace_btn || 'Replace'}
</button>
</div>
</div>
</div>
`;
// Remove existing modal if any
var existingModal = document.getElementById('draftConflictModal');
if (existingModal) {
existingModal.remove();
}
// Add modal to body
document.body.insertAdjacentHTML('beforeend', modalHTML);
var modalElement = document.getElementById('draftConflictModal');
// Handle close buttons
document.querySelectorAll('.draft-modal-close').forEach(function(btn) {
btn.addEventListener('click', function() {
modalElement.remove();
});
});
// Handle merge button
document.getElementById('mergeBtn').addEventListener('click', function() {
var existingId = this.getAttribute('data-existing-id');
modalElement.remove();
self._executeSaveDraftWithAction('merge', existingId);
});
// Handle replace button
document.getElementById('replaceBtn').addEventListener('click', function() {
var existingId = this.getAttribute('data-existing-id');
modalElement.remove();
self._executeSaveDraftWithAction('replace', existingId);
});
// Close modal when clicking outside
modalElement.addEventListener('click', function(e) {
if (e.target === modalElement) {
modalElement.remove();
}
});
},
_executeSaveDraftWithAction: function(action, existingDraftId) {
/**
* Execute save draft with merge or replace action.
*/
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
});
});
var orderData = {
order_id: this.orderId,
items: items,
merge_action: action,
existing_draft_id: existingDraftId
};
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) {
// Use server-provided labels instead of hardcoding
var labels = self._getLabels();
// Use the translated messages from server
var msg = data.merged
? ('✓ ' + (labels.draft_merged_success || 'Draft merged successfully'))
: ('✓ ' + (labels.draft_replaced_success || 'Draft replaced successfully'));
self._showNotification(msg, 'success', 5000);
} else {
var labels = self._getLabels();
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 {
try {
var errorData = JSON.parse(xhr.responseText);
self._showNotification('Error ' + xhr.status + ': ' + (errorData.error || 'Request error'), 'danger');
} catch (e) {
self._showNotification('Error saving order (HTTP ' + xhr.status + ')', 'danger');
}
}
};
xhr.onerror = function() {
self._showNotification('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 {
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);
if (cartContainer || confirmBtn) {
console.log('Calling init()');
var result = window.groupOrderShop.init();
console.log('init() result:', result);
} else {
console.warn(
'No elements found to initialize cart'
);
}
});
// 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);
})();