async function deliverOrder(orderId) { try { await supabaseRequest(`orders?id=eq.${orderId}`, 'PATCH', { status: 'entregado', delivered_at: new Date().toISOString() }); showNotification('Orden marcada como entregada', 'success'); loadDeliveryOrders(); } catch (error) { console.error('Error delivering order:', error); showNotification('Error al marcar como entregada', 'error'); } } async function returnOrder(orderId) { try { const orders = await supabaseRequest(`orders?id=eq.${orderId}&select=*,commerces(name)`); const order = orders[0]; if (!order) { showNotification('Orden no encontrada', 'error'); return; } if (confirm(`¿Estás seguro de regresar la orden ${order.order_number} a la tienda?`)) { await supabaseRequest(`orders?id=eq.${orderId}`, 'PATCH', { status: 'devuelto', returned_at: new Date().toISOString() }); showNotification(`Orden regresada a ${order.commerces.name}`, 'success'); loadDeliveryOrders(); } } catch (error) { console.error('Error returning order:', error); showNotification('Error al regresar la orden', 'error'); } } // Customer validation async function loadCustomerOrders() { const commerce = document.getElementById('customerCommerce').value; const orderSelect = document.getElementById('customerOrderSelect'); orderSelect.innerHTML = ''; if (!commerce) return; try { const commerces = await supabaseRequest('commerces'); const foundCommerce = commerces.find(c => c.name === commerce); if (!foundCommerce) return; const orders = await supabaseRequest(`orders?commerce_id=eq.${foundCommerce.id}&status=in.(pendiente,listo)`); orders.forEach(order => { orderSelect.innerHTML += ` `; }); } catch (error) { console.error('Error loading customer orders:', error); } } async function validateCustomer() { const orderId = document.getElementById('customerOrderSelect').value; const inputName = document.getElementById('customerValidationName').value; if (!orderId || !inputName) { showNotification('Selecciona una orden e ingresa tu nombre', 'error'); return; } try { const orders = await supabaseRequest(`orders?id=eq.${orderId}&select=*,commerces(name)`); const order = orders[0]; if (!order) { showNotification('Orden no encontrada', 'error'); return; } if (order.customer_name.toLowerCase() !== inputName.toLowerCase()) { showNotification('Nombre incorrecto', 'error'); return; } // Update order status await supabaseRequest(`orders?id=eq.${orderId}`, 'PATCH', { validated: true, status: 'listo' }); // Send notification to delivery person playNotificationSound(); showInAppPopup(order); showNotification('Validación exitosa. Notificación enviada al entregador', 'success'); // Clear form document.getElementById('customerCommerce').value = ''; document.getElementById('customerOrderSelect').value = ''; document.getElementById('customerValidationName').value = ''; loadCustomerOrders(); } catch (error) { console.error('Error validating customer:', error); showNotification('Error al validar el cliente', 'error'); } } // Commerce management async function createCommerce() { const commerceName = document.getElementById('newCommerce').value.trim(); if (!commerceName) { showNotification('El nombre del restaurante es obligatorio', 'error'); return; } try { const existingCommerces = await supabaseRequest('commerces'); const duplicate = existingCommerces.find(c => c.name.toLowerCase() === commerceName.toLowerCase()); if (duplicate) { showNotification('El restaurante ya existe', 'error'); return; } await supabaseRequest('commerces', 'POST', { name: commerceName }); showNotification('Restaurante agregado exitosamente', 'success'); // Clear form document.getElementById('newCommerce').value = ''; // Reload everything loadCommercesIntoSelect(); loadCommercesTable(); } catch (error) { console.error('Error creating commerce:', error); showNotification('Error al agregar el restaurante', 'error'); } } async function deleteCommerce(commerceName) { try { const commerces = await supabaseRequest('commerces'); const foundCommerce = commerces.find(c => c.name === commerceName); if (!foundCommerce) { showNotification('Restaurante no encontrado', 'error'); return; } const activeOrders = await supabaseRequest(`orders?commerce_id=eq.${foundCommerce.id}&status=in.(pendiente,listo)`); if (activeOrders.length > 0) { showNotification(`No se puede eliminar: ${commerceName} tiene ${activeOrders.length} órdenes activas`, 'error'); return; } if (confirm(`¿Estás seguro de eliminar ${commerceName}?`)) { await supabaseRequest(`commerces?id=eq.${foundCommerce.id}`, 'DELETE'); showNotification('Restaurante eliminado exitosamente', 'success'); loadCommercesIntoSelect(); loadCommercesTable(); } } catch (error) { console.error('Error deleting commerce:', error); showNotification('Error al eliminar el restaurante', 'error'); } } async function loadCommercesTable() { try { const commerces = await supabaseRequest('commerces'); const commercesTable = document.getElementById('commercesTable'); if (!commercesTable) return; commercesTable.innerHTML = ''; for (const commerce of commerces) { const activeOrders = await supabaseRequest(`orders?commerce_id=eq.${commerce.id}&status=in.(pendiente,listo)`); const activeCount = activeOrders.length; const row = document.createElement('tr'); row.innerHTML = ` ${commerce.name} ${activeCount} órdenes `; commercesTable.appendChild(row); } } catch (error) { console.error('Error loading commerces table:', error); } } // User management async function createUser() { const username = document.getElementById('newUsername').value; const password = document.getElementById('newPassword').value; const role = document.getElementById('userRole').value; if (!username || !password) { showNotification('Usuario y contraseña son obligatorios', 'error'); return; } try { const existingUsers = await supabaseRequest('users'); const duplicate = existingUsers.find(u => u.username.toLowerCase() === username.toLowerCase()); if (duplicate) { showNotification('El usuario ya existe', 'error'); return; } await supabaseRequest('users', 'POST', { username: username, password: password, role: role }); const roleText = role === 'admin' ? 'Administrador' : 'Entregador'; showNotification(`Usuario ${username} creado como ${roleText}`, 'success'); // Clear form document.getElementById('newUsername').value = ''; document.getElementById('newPassword').value = ''; document.getElementById('userRole').value = 'delivery'; loadUsers(); } catch (error) { console.error('Error creating user:', error); showNotification('Error al crear el usuario', 'error'); } } async function deleteUser(username) { try { const users = await supabaseRequest('users'); const user = users.find(u => u.username === username); if (user && user.role === 'admin') { const admins = users.filter(u => u.role === 'admin'); if (admins.length <= 1) { showNotification('No se puede eliminar: debe haber al menos un administrador', 'error'); return; } } if (confirm(`¿Estás seguro de eliminar el usuario ${username}?`)) { await supabaseRequest(`users?username=eq.${username}`, 'DELETE'); showNotification('Usuario eliminado', 'success'); loadUsers(); } } catch (error) { console.error('Error deleting user:', error); showNotification('Error al eliminar el usuario', 'error'); } } async function loadUsers() { try { const users = await supabaseRequest('users'); const usersTable = document.getElementById('usersTable'); const changePasswordSelect = document.getElementById('changePasswordUser'); if (usersTable) { usersTable.innerHTML = ''; } if (changePasswordSelect) { changePasswordSelect.innerHTML = ''; } users.forEach(user => { const roleIcon = user.role === 'admin' ? '👑' : '👤'; const roleText = user.role === 'admin' ? 'Administrador' : 'Entregador'; if (usersTable) { const row = document.createElement('tr'); row.innerHTML = ` ${user.username} ${roleIcon} ${roleText} `; usersTable.appendChild(row); } if (changePasswordSelect) { changePasswordSelect.innerHTML += ``; } }); } catch (error) { console.error('Error loading users:', error); } } async function changeUserPassword() { const username = document.getElementById('changePasswordUser').value; const newPassword = document.getElementById('newUserPassword').value; if (!username) { showNotification('Selecciona un usuario', 'error'); return; } if (!newPassword || newPassword.length < 3) { showNotification('La contraseña debe tener al menos 3 caracteres', 'error'); return; } if (confirm(`¿Estás seguro de cambiar la contraseña de ${username}?`)) { try { await supabaseRequest(`users?username=eq.${username}`, 'PATCH', { password: newPassword }); showNotification(`Contraseña de ${username} cambiada exitosamente`, 'success'); // Clear form document.getElementById('changePasswordUser').value = ''; document.getElementById('newUserPassword').value = ''; } catch (error) { console.error('Error changing password:', error); showNotification('Error al cambiar la contraseña', 'error'); } } } // Orders table async function loadOrdersTable() { try { const orders = await supabaseRequest('orders?select=*,commerces(name)&order=created_at.desc'); const ordersTable = document.getElementById('ordersTable'); if (!ordersTable) return; ordersTable.innerHTML = ''; orders.forEach(order => { const row = document.createElement('tr'); row.innerHTML = ` ${order.order_number} ${order.commerces.name} ${order.customer_name} ${order.amount ? order.amount.toFixed(2) : '0.00'} ${order.status.toUpperCase()} ${new Date(order.created_at).toLocaleDateString()} ${order.delivery_person} `; ordersTable.appendChild(row); }); } catch (error) { console.error('Error loading orders table:', error); } } async function exportToExcel() { try { const orders = await supabaseRequest('orders?select=*,commerces(name)&order=created_at.desc'); let csv = 'Orden,Comercio,Cliente,Monto,Estado,Fecha,Entregador\n'; orders.forEach(order => { csv += `${order.order_number},${order.commerces.name},${order.customer_name},${order.amount ? order.amount.toFixed(2) : '0.00'},${order.status},${new Date(order.created_at).toLocaleDateString()},${order.delivery_person}\n`; }); const blob = new Blob([csv], { type: 'text/csv' }); const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'ordenes.csv'; a.click(); window.URL.revokeObjectURL(url); } catch (error) { console.error('Error exporting to Excel:', error); showNotification('Error al exportar los datos', 'error'); } } // Notification functions function playNotificationSound() { try { const audioContext = new (window.AudioContext || window.webkitAudioContext)(); const oscillator = audioContext.createOscillator(); const gainNode = audioContext.createGain(); oscillator.connect(gainNode); gainNode.connect(audioContext.destination); oscillator.frequency.setValueAtTime(800, audioContext.currentTime); oscillator.type = 'sine'; gainNode.gain.setValueAtTime(0, audioContext.currentTime); gainNode.gain.linearRampToValueAtTime(0.3, audioContext.currentTime + 0.01); gainNode.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + 0.5); oscillator.start(audioContext.currentTime); oscillator.stop(audioContext.currentTime + 0.5); } catch (error) { console.log('No se pudo reproducir el sonido:', error); if (navigator.vibrate) { navigator.vibrate([200, 100, 200]); } } } function showInAppPopup(order) { const existingPopup = document.querySelector('.delivery-popup'); if (existingPopup) { existingPopup.remove(); } const overlay = document.createElement('div'); overlay.className = 'delivery-popup'; overlay.style.cssText = ` position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.8); z-index: 10000; display: flex; align-items: center; justify-content: center; animation: fadeIn 0.3s ease-out; `; const popup = document.createElement('div'); popup.style.cssText = ` background: white; padding: 30px; border-radius: 15px; text-align: center; max-width: 90%; width: 400px; box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); `; popup.innerHTML = `
🚚

¡ORDEN LISTA!

${order.customer_name}

Orden: ${order.order_number}
Comercio: ${order.commerces.name}
Monto: ${order.amount ? order.amount.toFixed(2) : '0.00'}

`; overlay.appendChild(popup); document.body.appendChild(overlay); setTimeout(() => { if (overlay.parentNode) { overlay.remove(); } }, 10000); } function requestNotificationPermissions() { if ('Notification' in window && Notification.permission === 'default') { Notification.requestPermission().then(permission => { if (permission === 'granted') { showNotification('Notificaciones activadas para alertas de órdenes', 'success'); } }); } } // Initialize app function init() { const urlParams = new URLSearchParams(window.location.search); const section = urlParams.get('section'); if (section === 'customer') { showSection('customer'); } else { showSection('login'); } } // Start the app init(); Plataforma de Pickup

🚚 Plataforma de Pickup

Iniciar Sesión

Panel de Entrega

🔔 Las notificaciones sonoras están activadas. Recibirás alertas cuando los clientes validen sus órdenes.

Validación de Cliente

No necesitas crear una cuenta. Solo selecciona tu orden y valida tu nombre.

Panel de Administración

🔒 Acceso Restringido: Solo usuarios administradores pueden acceder a esta sección.

👥 Administración de Usuarios

Usuarios Existentes

Usuario Rol Acciones

🔑 Cambiar Contraseña

🏪 Administración de Restaurantes

Restaurantes Existentes

Restaurante Órdenes Activas Acciones

Todas las Órdenes

Orden Comercio Cliente Monto Estado Fecha Entregador