1788 lines
82 KiB
JavaScript
1788 lines
82 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);
|
||
|
||
// 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);
|
||
|
||
})();
|
||
|