import { sendMessage as serviceSendMessage } from './service/chat.js';
import { getProducts, sendOrder } from './service/product.js';
import { login } from './service/auth.js'
import { createGlobalLoader, showGlobalLoader, hideGlobalLoader } from './utils/loader.js';
import { updateProgress, claimReward } from './utils/progressBar.js';
import { showError } from './utils/error.js';
import { addHistoryRow, setupShoppingCart } from './utils/shoppingCart.js';
import { hideGUI, showGUI } from './utils/gui.js';
// --- Variables de Usuario ---
let userId = -1;
let userName = "Cliente";
let userTable = null;
let userToken = null;
// --- Datos de Productos y Carrito ---
let products = [];
let cart = [];
let itsEmpty = true;
// --- Historial de Chat ---
let chatHistory = [
{ role: "system", content: "¡Hola! Soy tu asistente en Biergarten Klein. ¿Te gustaría una recomendación de nuestras cervezas artesanales?" }
];
// --- Elementos del DOM: Productos y Carrito ---
const productListElement = document.getElementById("productList");
const cartItemsElement = document.getElementById("cartItems");
const cartTotalElement = document.getElementById("cartTotal");
const emptyCartTextElement = document.getElementById("emptyCartText");
const checkoutButton = document.getElementById("checkoutButton");
const originalCheckoutButtonText = checkoutButton ? checkoutButton.textContent : "Finalizar Pedido";
const cartCountElement = document.getElementById("cartCount");
// --- Elementos del DOM: Chat ---
const chatMessagesElement = document.getElementById("chatMessages");
const chatInputElement = document.getElementById("chatInput");
const chatForm = document.getElementById("chatForm");
const aiLoadingIndicator = document.getElementById("aiLoadingIndicator");
const chatSuggestionsElement = document.getElementById("chatSuggestions");
//#region --- Inicialización y Configuracion ---
async function initializeApp() {
updateCartDisplay();
initializeChat();
setupBasicListeners();
showGlobalLoader("Cargando productos...");
setupShoppingCart(userId, userToken,userName);
await renderProducts();
showGUI();
hideGlobalLoader();
const chatSuggestions = Array.from(chatSuggestionsElement.children);
chatSuggestions.forEach(suggestion => {
suggestion.addEventListener("click", () => {
sendSuggestion(suggestion.querySelector(".chat-suggestion").textContent);
});
});
}
function initializeLoginModal() {
const sessionModal = document.getElementById('sessionModal');
const loginForm = document.getElementById('loginForm');
sessionModal.classList.remove('hidden');
loginForm.addEventListener('submit', async (event) => {
event.preventDefault();
event.stopPropagation();
const fd = new FormData(loginForm);
const email = fd.get('email').trim();
const pin = fd.get('pin').trim();
userTable = Number(fd.get('table').trim());
if (!email || !pin || !userTable) {
showError("Por favor, completa todos los campos.");
return;
}
try{
const {data} = await login(email, pin)
userToken = data.token;
userName = data.name;
userId = data.id;
updateProgress(data.reward_progress || 0);
if (!userToken || data.id === undefined) {
showError("Error al iniciar sesión.");
return;
}
sessionModal.classList.add('hidden');
initializeApp();
}catch (error) {
console.error(error)
}
})
}
function initializeChat() {
if (!chatForm) return;
chatForm.addEventListener("submit", (event) => {
event.preventDefault();
if (chatInputElement.value.trim() === "") return;
sendMessageToAI();
chatInputElement.addEventListener("input", () => {
if (chatInputElement.value.trim() === "") {
chatSuggestionsElement.classList.remove("hidden");
} else {
chatSuggestionsElement.classList.add("hidden");
}
});
});
}
function setupBasicListeners() {
if (!checkoutButton) return;
checkoutButton.addEventListener("click", processOrder);
initializeRewards();
}
//#endregion
//#region ===== Utilidad =====
function formatPrice(price) {
return price.toLocaleString("es-CL", { style: "currency", currency: "CLP" });
}
//#endregion
//#region ===== Productos =====
async function renderProducts() {
if (!productListElement) return;
const template = document.getElementById("product-card-template");
if (!template) return;
productListElement.innerHTML = "";
console.log("Cargando productos...");
products = await getProducts(userToken);
products.forEach(product => {
const clone = template.content.cloneNode(true);
clone.querySelector(".product-type").textContent = product.type || "Sin categoría";
clone.querySelector(".product-name").textContent = product.name;
clone.querySelector(".product-description").textContent = product.description;
clone.querySelector(".product-price").textContent = formatPrice(product.price);
clone.querySelector(".product-image").style.backgroundImage = `url('${product.image}')`;
const addBtn = clone.querySelector(".add-to-cart-btn");
addBtn.dataset.productId = product.id; // el listener usa esta info
productListElement.appendChild(clone);
});
document.querySelectorAll('.add-to-cart-btn').forEach(button => {
button.addEventListener('click', (event) => {
const productId = parseInt(event.target.dataset.productId);
addToCart(productId, event.target);
});
});
}
//#endregion
//#region ===== Carrito =====
async function addToCart (productId, buttonElement = null) {
const product = products.find(p => p.id === productId);
if (!product) return;
const cartItem = cart.find(item => item.id === productId);
if (cartItem) {
cartItem.quantity++;
} else {
cart.push({ ...product, quantity: 1 });
}
if (buttonElement) {
const originalHTML = buttonElement.innerHTML;
buttonElement.textContent = "✔ Agregado!";
buttonElement.disabled = true;
setTimeout(() => {
buttonElement.innerHTML = originalHTML;
buttonElement.disabled = false;
}, 300);
}
updateCartDisplay();
// Dentro de addToCart (después de updateCartDisplay())
if (typeof showToast === "function") showToast(`${product.name} agregado al carrito`);
};
async function removeFromCart (productId, removeAll = false) {
const itemIndex = cart.findIndex(item => item.id === productId);
if (itemIndex > -1) {
if (removeAll || cart[itemIndex].quantity === 1) {
cart.splice(itemIndex, 1);
} else {
cart[itemIndex].quantity--;
}
}
updateCartDisplay();
};
function calculateTotal() {
if (!cartTotalElement) return;
const total = cart.reduce((sum, item) => sum + item.price * item.quantity, 0);
cartTotalElement.textContent = formatPrice(total);
}
function updateCartDisplay() {
if (!cartItemsElement || !emptyCartTextElement || !checkoutButton || !cartCountElement) return;
cartItemsElement.innerHTML = "";
cartCountElement.textContent = cart.reduce((sum, item) => sum + item.quantity, 0);
if (cart.length === 0) {
cartCountElement.classList.add("hidden");
emptyCartTextElement.classList.remove("hidden");
checkoutButton.disabled = true;
itsEmpty = true;
} else {
cartCountElement.classList.remove("hidden");
if (cartCountElement && itsEmpty) {
itsEmpty = false;
cartCountElement.animate([
{ transform: 'scale(0)' },
{ transform: 'scale(1)' }
], {
duration: 300,
iterations: 1,
easing: 'ease-in-out'
})
} else {
cartCountElement.animate([
{ transform: 'scale(1) rotate(0deg)' },
{ transform: 'scale(1.2) rotate(180deg)' },
{ transform: 'scale(1) rotate(360deg)' }
], {
duration: 300,
iterations: 1,
easing: 'ease-in-out'
})
}
emptyCartTextElement.classList.add("hidden");
checkoutButton.disabled = false;
cart.forEach(item => {
const cartItemHTML = `
${item.name} (x${item.quantity})
${formatPrice(item.price * item.quantity)}
`;
cartItemsElement.innerHTML += cartItemHTML;
const plusButton = cartItemsElement.querySelectorAll(".plus-button");
const minusButton = cartItemsElement.querySelectorAll(".minus-button");
const removeButton = cartItemsElement.querySelectorAll(".remove-button");
plusButton.forEach((btn, index) => {
btn.addEventListener("click", () => {
addToCart(item.id);
btn.classList.add("animate-pulse");
setTimeout(() => btn.classList.remove("animate-pulse"), 300);
});
});
minusButton.forEach((btn, index) => {
btn.addEventListener("click", () => {
removeFromCart(item.id);
btn.classList.add("animate-pulse");
setTimeout(() => btn.classList.remove("animate-pulse"), 300);
});
});
removeButton.forEach((btn, index) => {
btn.addEventListener("click", () => {
removeFromCart(item.id, true);
btn.classList.add("animate-pulse");
setTimeout(() => btn.classList.remove("animate-pulse"), 300);
});
});
});
cart
}
calculateTotal();
}
//#endregion
//#region ===== Pedidos =====
async function processOrder() {
if (cart.length === 0) return;
showGlobalLoader();
if (checkoutButton) {
checkoutButton.disabled = true;
checkoutButton.textContent = "Procesando...";
}
try {
const orderData = {
customerId: userId,
table: userTable,
items: cart.map(item => ({ id: item.id, price: item.price, quantity: item.quantity})),
totalAmount: cart.reduce((sum, item) => sum + item.price * item.quantity, 0),
orderDate: new Date().toLocaleString('sv-SE').replace(' ', 'T')
};
const data = await sendOrder(orderData,userToken);
if (data && data.new_progress) {
updateProgress(data.new_progress);
}
alert("Pedido enviado con éxito.");
cart.forEach(item => {
addHistoryRow({
productName: item.name,
quantity: item.quantity,
price: item.price,
});
});
cart = []
updateCartDisplay();
} catch (error) {
console.error("Error al procesar la orden:", error);
alert(`Hubo un problema: ${error.message || "Por favor, inténtalo de nuevo."}`);
} finally {
hideGlobalLoader();
checkoutButton.disabled = cart.length === 0;
checkoutButton.textContent = originalCheckoutButtonText
}
}
//#endregion
//#region ===== Chat =====
function displayChatMessage(sender, message) {
if (!chatMessagesElement) return;
const bubbleClass = sender === "user" ? "chat-bubble-user" : "chat-bubble-ai";
const messageDiv = document.createElement("div");
messageDiv.classList.add("chat-bubble", bubbleClass);
messageDiv.innerHTML = sender === "ai" && window.marked ? marked.parse(message) : message;
chatMessagesElement.appendChild(messageDiv);
chatMessagesElement.scrollTop = chatMessagesElement.scrollHeight;
}
async function sendSuggestion(suggestion) {
if (!chatInputElement || !aiLoadingIndicator) return;
chatInputElement.value = suggestion;
chatInputElement.focus();
}
async function sendMessageToAI() {
if (!chatInputElement || !aiLoadingIndicator) return;
const userInput = chatInputElement.value.trim();
if (!userInput) return;
displayChatMessage("user", userInput);
chatSuggestionsElement.classList.add("hidden");
chatInputElement.value = '';
aiLoadingIndicator.classList.remove("hidden");
try {
const response = await serviceSendMessage(userInput, chatHistory, userName, userToken);
if (!response) {
displayChatMessage("ai", "Hubo un problema al conectar con el Chef IA.");
} else if (response === "not_init") {
if (await initializeService()) {
const response = await serviceSendMessage(userInput, chatHistory, userName, userToken);
if (response) {
chatHistory = response.messageList;
displayChatMessage("ai", response.assistantResponse);
} else {
displayChatMessage("ai", "Hubo un problema al enviar el mensaje.");
}
} else {
displayChatMessage("ai", "Fallo la reconexión. Por favor, refresca la página.");
}
} else if (response.assistantResponse) {
chatHistory = response.messageList;
displayChatMessage("ai", response.assistantResponse);
}
} catch (error) {
console.error("Error enviando mensaje a IA:", error);
displayChatMessage("ai", `Error: ${error.message || "No se pudo conectar con el Chef IA."}`);
} finally {
aiLoadingIndicator.classList.add("hidden");
if (chatInputElement) chatInputElement.focus();
}
}
//#endregion
//#region ===== Rewards =====
// Referencias a elementos del DOM
const rewardBtn = document.getElementById('rewardBtn');
const rewardModal = document.getElementById('rewardModal');
const closeRewardModal = document.getElementById('closeRewardModal');
const closeSuccessRewardModalButton = document.getElementById('closeSuccessRewardModal');
const cancelRewardBtn = document.getElementById('cancelRewardBtn');
const acceptTermsCheckbox = document.getElementById('acceptTermsCheckbox');
const claimRewardBtn = document.getElementById('claimRewardBtn');
function initializeRewards() {
// Abrir modal cuando se hace clic en el botón de recompensa
closeSuccessRewardModalButton.addEventListener("click", closeSuccessRewardModal);
rewardBtn.addEventListener('click', function() {
if (!rewardBtn.disabled) {
rewardModal.classList.remove('hidden');
document.body.style.overflow = 'hidden'; // Evitar scroll del fondo
}
});
// Cerrar modal - botón X
closeRewardModal.addEventListener('click', function() {
closeModal();
});
// Cerrar modal - botón Cancelar
cancelRewardBtn.addEventListener('click', function() {
closeModal();
});
// Cerrar modal haciendo clic fuera de él
rewardModal.addEventListener('click', function(e) {
if (e.target === rewardModal) {
closeModal();
}
});
// Cerrar modal con tecla Escape
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape' && !rewardModal.classList.contains('hidden')) {
closeModal();
}
});
// Manejar el reclamo del premio
claimRewardBtn.addEventListener('click', function() {
if (!this.disabled && acceptTermsCheckbox.checked) {
// Generar código de premio (puedes cambiar esta lógica)
// Mostrar mensaje de éxito
claimReward(userToken, userTable);
// Cerrar modal
closeModal();
updateProgress(0);
}
});
// Generar más confetti dinámicamente
console.log("Rewards initialized");
}
function closeSuccessRewardModal() {
const successRewardModal = document.getElementById("successRewardModal");
successRewardModal.classList.add("hidden");
document.body.style.overflow = ''; // Restaurar scroll
}
// Función para cerrar el modal
function closeModal() {
rewardModal.classList.add('hidden');
document.body.style.overflow = ''; // Restaurar scroll
// Reset del formulario al cerrar
acceptTermsCheckbox.checked = false;
claimRewardBtn.disabled = true;
claimRewardBtn.classList.add('opacity-50', 'cursor-not-allowed');
}
// Manejar el checkbox de términos y condiciones
acceptTermsCheckbox.addEventListener('change', function() {
if (this.checked) {
// Habilitar botón de reclamar
claimRewardBtn.disabled = false;
claimRewardBtn.classList.remove('opacity-50', 'cursor-not-allowed');
} else {
// Deshabilitar botón de reclamar
claimRewardBtn.disabled = true;
claimRewardBtn.classList.add('opacity-50', 'cursor-not-allowed');
}
});
// Función para mostrar mensaje de éxito con el código
function showRewardSuccess(code) {
// Crear elemento de notificación de éxito
const successToast = document.createElement('div');
successToast.className = `
fixed top-4 left-1/2 transform -translate-x-1/2
bg-green-500 text-white text-sm rounded-lg px-6 py-4
shadow-lg z-50 max-w-sm text-center
`;
successToast.innerHTML = `
🎉 ¡Premio Reclamado!
Tu código: ${code}
Muéstralo al mesero
`;
document.body.appendChild(successToast);
// Remover después de 5 segundos
setTimeout(() => {
if (successToast.parentNode) {
successToast.remove();
}
}, 5000);
}
// Función para resetear el progreso de recompensa
function resetRewardProgress() {
const progressBar = document.getElementById('progressBar');
const progressText = document.getElementById('progressText');
const rewardBtn = document.getElementById('rewardBtn');
// Resetear a 0%
progressBar.style.width = '0%';
progressText.textContent = '0%';
// Deshabilitar botón de recompensa
rewardBtn.disabled = true;
rewardBtn.classList.add('opacity-50', 'cursor-not-allowed');
rewardBtn.classList.remove('bg-yellow-500', 'hover:bg-yellow-600');
rewardBtn.textContent = '🎉 ¡Reclamar!';
}
//#endregion
// --- APP initialization ---
document.addEventListener("DOMContentLoaded", async () => {
createGlobalLoader();
initializeLoginModal();
hideGUI();
// initializeApp()
});