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();