Estado
Repartiendo
1 pedido activo
Listo en aprox. 5 minutos
Muestra este número en la lavandería para retirar
Forma de pago
Efectivo al entregar
● PEDIDO ENTRANTE · LAVOBCN
4€
Tarifa garantizada
Lavado
Perfect Clean
Pg. del Taulat, 279A · Sant Martí
📍 0.8 km · 4 min
💵 Efectivo
Ref: —
Expira en 20s
Centro de ayuda
Gestión de entregas
🛍️Ayuda con un pedido
🆘Necesito otro repartidor
Otros incidentes
🚨Accidente / Vehículo averiado
📱Problema con la app
💵Pago o propina
🔐
Código de entrega
Pide al cliente los 2 dígitos
❌ Código incorrecto. Pide de nuevo al cliente.
Abrir navegación

Bienvenido a LavoBCN

Completa tu perfil para empezar

Foto de perfil (obligatorio)
🤳
Tomar selfie
Cámara frontal · Obligatorio
Datos personales
Vehículo
Documentación
🪪
DNI / NIE
Foto del documento
Subir
🛡️
Seguro del vehículo
Foto del seguro
Subir
Términos y condiciones
1. RELACIÓN LABORAL
El repartidor opera como colaborador independiente. LavoBCN no establece relación laboral ni obligación de horarios fijos.

2. COMISIONES
LavoBCN no cobra comisión. Las propinas pertenecen íntegramente al repartidor.

3. GPS Y UBICACIÓN
El repartidor autoriza el uso del GPS durante el tiempo que esté activo. La ubicación se comparte con el admin y el cliente.

4. ZONA DE REPARTO
La zona asignada es Barcelona. Salir de zona de forma reiterada puede suspender el acceso.

5. DOCUMENTACIÓN
El repartidor debe mantener vigente su documentación. LavoBCN puede solicitar documentación adicional en cualquier momento.

6. COMPORTAMIENTO
El repartidor se compromete a tratar con respeto a clientes y lavanderías.

7. CÓDIGO DE ENTREGA
El repartidor debe verificar el código de 2 dígitos con el cliente antes de confirmar cada entrega.

8. DATOS PERSONALES
Los datos se usan exclusivamente para la gestión de la plataforma.

9. MODIFICACIONES
LavoBCN puede modificar estos términos con aviso previo de 15 días.

10. JURISDICCIÓN
Estos términos se rigen por la legislación española. Jurisdicción: Barcelona.
He leído y acepto los Términos y Condiciones de LavoBCN, incluyendo el uso del GPS y la // ═══════════════════════════════ // FIREBASE RIDER — ESCUCHA PEDIDOS // ═══════════════════════════════ var fbConfigR = { apiKey: "AIzaSyDaopojkUpu7kQ2-H8yEaG", authDomain: "lavobcn.firebaseapp.com", databaseURL: "https://lavobcn-default-rtdb.europe-west1.firebasedatabase.app", projectId: "lavobcn", storageBucket: "lavobcn.firebasestorage.app", messagingSenderId: "749157836989", appId: "1:749157836989:web:c1052afdf" }; var fbR = null, fbDBR = null, fbEscuchando = false; function initFirebaseRider(){ try { if(typeof firebase === 'undefined') return false; try { fbR = firebase.app(); } catch(e) { fbR = firebase.initializeApp(fbConfigR); } fbDBR = firebase.database(); console.log('✅ Firebase Rider conectado'); return true; } catch(e){ console.log('Firebase Rider error:', e); return false; } } function escucharPedidos(){ if(!fbDBR || fbEscuchando) return; fbEscuchando = true; fbDBR.ref('pedidos').orderByChild('estado').equalTo('pendiente').on('child_added', function(snap){ var p = snap.val(); if(!p || !p.ref) return; if(!stOn || pedActivo) return; console.log('🔔 Pedido Firebase:', p.ref); // Vibrar if(navigator.vibrate) navigator.vibrate([400,200,400,200,400]); // Mostrar pedido entrante pedPend = { ref: p.ref, lav: p.local || 'Perfect Clean', dir: p.dir || '', precio: p.total || '10€', serv: p.servicio === 'exp' ? 'Express 24h' : 'Estándar 72h', telCliente: p.tel || '', fase: 0, firebaseKey: snap.key }; // Mostrar incoming var lav = (typeof LAVS !== 'undefined') ? (LAVS.find(function(l){return l.n===pedPend.lav;}) || LAVS[0]) : {addr:'Barcelona'}; var ip = document.getElementById('ip-price'); var is = document.getElementById('ip-serv'); var il = document.getElementById('ip-lav'); var ia = document.getElementById('ip-addr'); if(ip) ip.textContent = pedPend.precio; if(is) is.textContent = pedPend.serv; if(il) il.textContent = pedPend.lav; if(ia) ia.textContent = lav ? lav.addr : pedPend.dir; var inc = document.getElementById('incoming'); if(inc) { inc.classList.add('show'); if(navigator.vibrate) navigator.vibrate([500,200,500]); // Countdown expSec=20; clearInterval(expIv); var fill = document.getElementById('ip-fill'); var expEl = document.getElementById('ip-exp'); expIv=setInterval(function(){ expSec--; if(fill) fill.style.width=((expSec/20)*100)+'%'; if(expEl) expEl.textContent=expSec+'s'; if(expSec<=0){clearInterval(expIv); if(typeof reject==='function') reject();} },1000); } }); } function marcarAceptado(fbKey){ if(!fbDBR || !fbKey) return; try { fbDBR.ref('pedidos/'+fbKey+'/estado').set('aceptado'); } catch(e){} } // ═══════════════════════════════ // FIREBASE RIDER — ESCUCHA PEDIDOS // ═══════════════════════════════ var firebaseConfigR = { apiKey: "AIzaSyDemo-LavoBCN-2025", authDomain: "lavobcn-demo.firebaseapp.com", databaseURL: "https://lavobcn-demo-default-rtdb.europe-west1.firebasedatabase.app", projectId: "lavobcn-demo" }; var fbAppR = null, fbDBR = null, fbEscuchando = false; function initFirebaseRider(){ try { // Evitar doble init if(firebase.apps.length > 0){ fbAppR = firebase.apps[0]; } else { fbAppR = firebase.initializeApp(firebaseConfigR); } fbDBR = firebase.database(); console.log('✅ Firebase Rider conectado'); return true; } catch(e){ console.log('Firebase Rider no disponible'); return false; } } function escucharPedidos(){ if(!fbDBR || fbEscuchando) return; fbEscuchando = true; fbDBR.ref('pedidos').orderByChild('estado').equalTo('pendiente').on('child_added', function(snap){ var pedido = snap.val(); if(!pedido || !pedido.ref) return; // Solo mostrar si el rider está conectado y libre if(!stOn || pedActivo) return; console.log('🔔 Nuevo pedido Firebase:', pedido.ref); // Vibrar if(navigator.vibrate) navigator.vibrate([400,200,400,200,400]); // Mostrar pedido entrante pedPend = { ref: pedido.ref, lav: pedido.local || 'Perfect Clean', dir: pedido.dir, precio: pedido.total || '10€', serv: 'Lavandería', telCliente: pedido.tel, fase: 0, firebaseKey: snap.key }; // Mostrar incoming if(typeof simPedido === 'function'){ mostrarPedidoEntrante(); } }); } function mostrarPedidoEntrante(){ if(!pedPend) return; var lav = LAVS.find(function(l){return l.n===pedPend.lav;}) || LAVS[0]; var inc = document.getElementById('incoming'); if(!inc) return; // Rellenar datos var ipPrice = document.getElementById('ip-price'); var ipServ = document.getElementById('ip-serv'); var ipLav = document.getElementById('ip-lav'); var ipAddr = document.getElementById('ip-addr'); if(ipPrice) ipPrice.textContent = pedPend.precio; if(ipServ) ipServ.textContent = pedPend.serv; if(ipLav) ipLav.textContent = pedPend.lav; if(ipAddr) ipAddr.textContent = lav ? lav.addr : pedPend.dir; inc.classList.add('show'); if(navigator.vibrate) navigator.vibrate([500,200,500]); // Countdown expSec=20; clearInterval(expIv); var fill = document.getElementById('ip-fill'); var expEl = document.getElementById('ip-exp'); expIv=setInterval(function(){ expSec--; if(fill) fill.style.width=((expSec/20)*100)+'%'; if(expEl) expEl.textContent=expSec+'s'; if(expSec<=0){clearInterval(expIv);reject();} },1000); } // Marcar pedido como aceptado en Firebase function aceptarEnFirebase(ref){ if(!fbDBR || !ref) return; try { fbDBR.ref('pedidos/'+ref+'/estado').set('aceptado'); fbDBR.ref('pedidos/'+ref+'/rider').set(nombre||'rider'); } catch(e){} } initFirebaseRider(); // ═══════════════════════════════════════ // LAVOBCN RIDER · SISTEMA LLAMADAS // ═══════════════════════════════════════ function mostrarLlamadaAuto(tel, ref, tipo){ tipo = tipo || 'pedido'; var old = document.getElementById('modal-llamada-rider'); if(old) old.remove(); var m = document.createElement('div'); m.id = 'modal-llamada-rider'; m.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.92);z-index:700;display:flex;align-items:center;justify-content:center;padding:20px;'; var msgWA = encodeURIComponent('Hola soy tu rider de LavoBCN. He llegado con tu ' + tipo + ' ' + ref + '. Estoy esperando abajo.'); var telLimpio = tel.replace(/[^0-9]/g,''); m.innerHTML = '
' + '
📞
' + '
¡CLIENTE ESPERANDO!
' + '
Llama al cliente
' + '
' + ref + ' · Ya estás en su dirección
' // LLAMAR — acción principal + '' + '📞 Llamar ahora' + '' // WHATSAPP + '' + '💬 WhatsApp' + '' // Info + '
' + '
⏱ Espera máximo 2 minutos
📵 Si no contesta: espera 1 min más
🔄 Sin respuesta: reagendamos con el cliente
' + '
' + '' + '
'; document.body.appendChild(m); if(navigator.vibrate) navigator.vibrate([300,150,300,150,300]); // Auto-llamar a los 2 segundos setTimeout(function(){ if(document.getElementById('modal-llamada-rider')){ window.location.href = 'tel:' + tel; } }, 2000); } function cerrarLlamadaRider(){ var m = document.getElementById('modal-llamada-rider'); if(m) m.remove(); } // CSS animación ring var rStyleEl = document.createElement('style'); rStyleEl.textContent = '@keyframes ring{0%,100%{transform:rotate(0deg)}20%{transform:rotate(-15deg)}40%{transform:rotate(15deg)}60%{transform:rotate(-10deg)}80%{transform:rotate(10deg)}}'; document.head.appendChild(rStyleEl); // ── CREDENCIALES ── var CREDS={'lavo2001':'102847','lavo2002':'203958','lavo2003':'314069','lavo2004':'425170','lavo2005':'536281','lavo2006':'647392','lavo2007':'758403','lavo2008':'869514','lavo2009':'970625','lavo2010':'081736'}; var NAMES={'lavo2001':'Ahmad M.','lavo2002':'Wei C.','lavo2003':'Carlos R.','lavo2004':'Khalid B.','lavo2005':'Sofía L.','lavo2006':'Marta G.','lavo2007':'Rajan P.','lavo2008':'Lucas F.','lavo2009':'Yusuf A.','lavo2010':'Elena V.'}; var LAVS=[{n:'Perfect Clean',addr:'Pg. del Taulat, 279A, 08019 Barcelona',lat:41.4006,lng:2.2073,tel:'+34930000001'},{n:'Tintorería Prim',addr:'Carrer del Maresme, 60, 08019 Barcelona',lat:41.4036,lng:2.1980,tel:'+34933030435'}]; var AUTO={'pedido':['✅ Dame más detalles. ¿Qué pedido?'],'rider':['🆘 Buscando rider disponible ahora mismo.'],'acc':['🚨 ¿Estás bien? ¿Necesitas ayuda urgente?'],'app':['📱 Cuéntame qué falla.'],'pago':['💵 ¿Qué duda tienes sobre el pago?']}; // ── ESTADO ── var nombre='Rider Demo',userKey='lavo2001',stOn=true,mapObj=null,rMark=null,aCircle=null; var pedActivo=null,pedPend=null,expIv=null,expSec=20,incMap=null,incMark=null; var viajes=0,euros=0,propinas=0,unread=0,tcOk=false,vehSel='',fueraZona=false; var ZONA={minLat:41.32,maxLat:41.47,minLng:2.05,maxLng:2.27}; function genRef(){var d=new Date();return 'LV-'+String(d.getFullYear()).slice(2)+String(d.getMonth()+1).padStart(2,'0')+String(d.getDate()).padStart(2,'0')+'-'+Math.floor(1000+Math.random()*9000);} function hora(){return new Date().toLocaleTimeString('es-ES',{hour:'2-digit',minute:'2-digit'});} // ── LOGIN ── document.getElementById('lbtn').addEventListener('click', doLogin); document.getElementById('lc').addEventListener('keyup', function(e){if(e.key==='Enter') doLogin();}); document.getElementById('lr').addEventListener('keyup', function(e){if(e.key==='Enter') document.getElementById('lc').focus();}); function doLogin(){ try { var u=(document.getElementById('lr').value||'').trim().toLowerCase(); var p=(document.getElementById('lc').value||'').trim(); var err=document.getElementById('lerr'); if(!CREDS[u]||CREDS[u]!==p){ if(err){err.textContent='Usuario o código incorrecto.';err.style.display='block';} return; } if(err) err.style.display='none'; nombre=NAMES[u]||u; userKey=u; var sl=document.getElementById('screen-login'); var ap=document.getElementById('app'); if(sl) sl.style.display='none'; if(ap) ap.style.display='block'; var sbn=document.getElementById('sb-name'); var sbu=document.getElementById('sb-user'); if(sbn) sbn.textContent=nombre; if(sbu) sbu.textContent=userKey; iniciarApp(); } catch(e){ console.log('Login error:',e); // Forzar entrada aunque falle var sl=document.getElementById('screen-login'); var ap=document.getElementById('app'); if(sl) sl.style.display='none'; if(ap) ap.style.display='block'; try { iniciarApp(); } catch(e2){} } } function logout(){nombre='';userKey='';document.getElementById('screen-login').style.display='none';document.getElementById('app').style.display='none';closeSB();} // ── ONBOARDING ── function processSelfie(inp){ if(inp.files&&inp.files[0]){var r=new FileReader();r.onload=function(e){var img=document.getElementById('selfie-prev');img.src=e.target.result;img.style.display='block';document.getElementById('selfie-hint').style.display='none';document.getElementById('selfie-box').classList.add('done');localStorage.setItem('selfie_'+userKey,e.target.result);};r.readAsDataURL(inp.files[0]);} } function selVeh(btn,v){document.querySelectorAll('.veh-btn').forEach(function(b){b.classList.remove('sel');});btn.classList.add('sel');vehSel=v;} function toggleTC(){tcOk=!tcOk;var ch=document.getElementById('tc-chk');ch.textContent=tcOk?'✓':'';ch.style.background=tcOk?'var(--blue)':'#fff';ch.style.borderColor=tcOk?'var(--blue)':'#e2e8f0';ch.style.color='#fff';} function guardarPerfil(){ if(!localStorage.getItem('selfie_'+userKey)){showToast('⚠ Selfie obligatoria');return;} if(!document.getElementById('ob-nombre').value.trim()){showToast('⚠ Nombre obligatorio');return;} if(!document.getElementById('ob-tel').value.trim()){showToast('⚠ Teléfono obligatorio');return;} if(!tcOk){showToast('⚠ Acepta los Términos y Condiciones');return;} var p={nombre:document.getElementById('ob-nombre').value+' '+document.getElementById('ob-apellido').value,tel:document.getElementById('ob-tel').value,email:document.getElementById('ob-email').value,dir:document.getElementById('ob-dir').value,vehiculo:vehSel||'🛵 Moto',matricula:document.getElementById('ob-mat').value,usuario:userKey,fecha:new Date().toISOString()}; localStorage.setItem('profile_'+userKey,JSON.stringify(p)); document.getElementById('onboarding').classList.remove('show'); iniciarApp(); showToast('✅ Perfil guardado. ¡Bienvenido!'); } // ── APP ── function iniciarApp(){ setTimeout(initMap,50); iniciarGPS(); pedirNotif(); initChat(); setTimeout(simIncoming,12000); } // ── MAPA ── function initMap(){ mapObj=L.map('map',{zoomControl:false,attributionControl:false,tap:false}); mapObj.setView([41.402,2.200],15); L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png',{maxZoom:19,subdomains:'abcd'}).addTo(mapObj); LAVS.forEach(function(l){ var html='
🏪
'; L.divIcon&&L.marker([l.lat,l.lng],{icon:L.divIcon({html:html,className:'',iconSize:[48,64],iconAnchor:[24,64]})}).addTo(mapObj).bindPopup(''+l.n+'
'+l.addr); }); L.rectangle([[ZONA.minLat,ZONA.minLng],[ZONA.maxLat,ZONA.maxLng]],{color:'#2563eb',weight:1,fill:true,fillColor:'#dbeafe',fillOpacity:.1,dashArray:'6 4'}).addTo(mapObj); setTimeout(function(){if(mapObj)mapObj.invalidateSize();},200); } function centrar(){if(mapObj&&rMark)mapObj.setView(rMark.getLatLng(),17);} // ── GPS ── function iniciarGPS(){ if(!navigator.geolocation)return; navigator.geolocation.watchPosition(function(pos){ var lat=pos.coords.latitude,lng=pos.coords.longitude,acc=pos.coords.accuracy; if(!rMark){ var ico=L.divIcon({html:'
',className:'',iconSize:[18,18],iconAnchor:[9,9]}); rMark=L.marker([lat,lng],{icon:ico}).addTo(mapObj); mapObj.setView([lat,lng],16); } else rMark.setLatLng([lat,lng]); if(aCircle)aCircle.remove(); aCircle=L.circle([lat,lng],{radius:acc,color:'#2563eb',fillColor:'#dbeafe',fillOpacity:.15,weight:1}).addTo(mapObj); var enZ=lat>=ZONA.minLat&&lat<=ZONA.maxLat&&lng>=ZONA.minLng&&lng<=ZONA.maxLng; if(!enZ&&!fueraZona){fueraZona=true;showToast('⚠️ Saliste de la zona');addAdmin('⚠️ '+nombre+' salió de la zona de reparto.');} else if(enZ&&fueraZona){fueraZona=false;addAdmin('✅ '+nombre+' volvió a la zona.');} },function(){},{enableHighAccuracy:true,maximumAge:3000,timeout:15000}); } // ── ESTADO ── function toggleSt(){stOn=!stOn;document.getElementById('tb-dot').className='tb-dot'+(stOn?'':' off');document.getElementById('tb-val').textContent=stOn?'Repartiendo':'Desconectado';document.getElementById('sb-st-lbl').textContent=stOn?'Desconectarme':'Conectarme';showToast(stOn?'✅ Recibes pedidos':'⏸ Desconectado'); if(stOn){ initFirebaseRider(); setTimeout(escucharPedidos, 800); } if(stOn) escucharPedidos();} // ── PEDIDO ACTIVO ── function mostrarPedido(p){ pedActivo=p; document.getElementById('bs-search').style.display='none'; document.getElementById('bs-ped').style.display='block'; var lav=LAVS.find(function(l){return l.n===p.lav;})||LAVS[0]; document.getElementById('ped-lav').textContent=p.lav; document.getElementById('ped-addr').textContent=lav.addr; document.getElementById('ped-emp').textContent='Empresa: '+p.lav; document.getElementById('ped-tiempo').textContent='Listo en aprox. '+Math.floor(3+Math.random()*8)+' minutos'; document.getElementById('ped-ref').textContent=p.ref; document.getElementById('ped-ref-sub').textContent='Muestra este número en '+p.lav+' para retirar'; var btn=document.getElementById('btn-ret');btn.textContent='Retirar';btn.className='btn-ret'; document.getElementById('fab-nav').classList.add('show'); if(mapObj)mapObj.setView([lav.lat,lav.lng],16); } function accion(){ if(!pedActivo)return; if(pedActivo.fase===0){ // FASE 0 → 1: Retirado de la lavandería, en camino pedActivo.fase=1; var btn=document.getElementById('btn-ret'); btn.textContent='📍 He llegado al cliente'; btn.className='btn-ret go'; showToast('📦 Retirado — en camino al cliente'); addAdmin('📦 '+nombre+' retiró '+pedActivo.ref); // Mostrar dirección cliente si existe var pedAddr = document.getElementById('ped-addr'); if(pedAddr && pedActivo.dirCliente) pedAddr.textContent = '📍 Entregar en: '+pedActivo.dirCliente; } else if(pedActivo.fase===1){ // FASE 1 → 2: Rider llega al cliente — LLAMADA AUTOMÁTICA pedActivo.fase=2; var btn=document.getElementById('btn-ret'); btn.textContent='✓ Entregado — introducir código'; btn.className='btn-ret go'; showToast('📞 Avisando al cliente...'); addAdmin('📍 '+nombre+' llegó al cliente · '+pedActivo.ref); // Llamar automáticamente al cliente var telCliente = pedActivo.telCliente || ''; if(telCliente){ // Mostrar modal de llamada mostrarLlamadaAuto(telCliente, pedActivo.ref); } } else { // FASE 2: Introducir código de entrega document.getElementById('cod-modal').classList.add('show'); document.getElementById('cod1').value='';document.getElementById('cod2').value=''; document.getElementById('cod-err').style.display='none'; setTimeout(function(){document.getElementById('cod1').focus();},300); } } function mostrarLlamadaAuto(tel, ref){ // Crear modal de llamada automática var old = document.getElementById('llamada-modal'); if(old) old.remove(); var modal = document.createElement('div'); modal.id = 'llamada-modal'; modal.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.9);z-index:600;display:flex;align-items:center;justify-content:center;padding:24px;'; modal.innerHTML = '
' + '
📞
' + '
¡Llama al cliente!
' + '
Pedido: ' + ref + '
' + '
El cliente no sabe que has llegado — llámale ahora
' + '📞 Llamar ahora' + '💬 WhatsApp al cliente' + '' + '
Si no contesta en 2 min → espera 1 min más y continúa
' + '
'; document.body.appendChild(modal); // Auto-intentar llamar después de 3 segundos setTimeout(function(){ if(document.getElementById('llamada-modal')){ window.location.href = 'tel:' + tel; } }, 3000); } function llamar(){var l=LAVS.find(function(x){return x.n===pedActivo&&pedActivo.lav;});if(l)window.location.href='tel:'+l.tel;} function copiarRef(){var r=document.getElementById('ped-ref').textContent;if(navigator.clipboard)navigator.clipboard.writeText(r).then(function(){showToast('📋 '+r+' copiado');});else showToast('📋 '+r);} // ── CÓDIGO ENTREGA ── function checkCod(){var d1=document.getElementById('cod1').value,d2=document.getElementById('cod2').value;if(d1&&d2)setTimeout(verificarCod,200);} function verificarCod(){ var cod=document.getElementById('cod1').value+document.getElementById('cod2').value; if(cod.length===2&&!isNaN(cod)&&parseInt(cod)>=10){ cerrarCod();completarEntrega(); } else { document.getElementById('cod-err').style.display='block'; document.getElementById('cod1').value='';document.getElementById('cod2').value=''; document.getElementById('cod1').focus(); if(navigator.vibrate)navigator.vibrate([100,50,100]); } } function cerrarCod(){document.getElementById('cod-modal').classList.remove('show');} function completarEntrega(){ viajes++;euros+=pedActivo.precio; document.getElementById('sb-v').textContent=viajes;document.getElementById('sb-e').textContent=euros+'€'; showToast('🎉 Entregado · +'+pedActivo.precio+'€'); addAdmin('🎉 '+nombre+' completó '+pedActivo.ref+'. +'+pedActivo.precio+'€'); document.getElementById('bs-ped').style.display='none';document.getElementById('bs-search').style.display='block'; document.getElementById('fab-nav').classList.remove('show'); pedActivo=null; setTimeout(simIncoming,18000); } // ── GPS NAV ── function openGPS(){if(pedActivo){var l=LAVS.find(function(x){return x.n===pedActivo.lav;});if(l){window._nl=l.lat;window._nlg=l.lng;}}document.getElementById('gps-modal').classList.add('show');} function cerrarGPS(){document.getElementById('gps-modal').classList.remove('show');} function abrirGPS(t){var lat=window._nl||41.4006,lng=window._nlg||2.2073,url='';if(t==='waze')url='https://waze.com/ul?ll='+lat+','+lng+'&navigate=yes';if(t==='google')url='https://www.google.com/maps/dir/?api=1&destination='+lat+','+lng+'&travelmode=bicycling';if(t==='apple')url='https://maps.apple.com/?daddr='+lat+','+lng+'&dirflg=d';window.open(url,'_blank');cerrarGPS();} // ── INCOMING ── function simIncoming(){ if(!stOn||!nombre)return; var lav=LAVS[Math.floor(Math.random()*LAVS.length)]; var precios=[3,4,6,8],servs=['Lavado','Planchado','Express','Premium']; var pi=Math.floor(Math.random()*4); var ref=genRef(); // Teléfonos demo de clientes — en producción vienen del cliente al pedir var telsDemoCliente = ['+34612345678','+34698765432','+34677889900','+34655443322','+34644556677']; var telClienteDemo = telsDemoCliente[Math.floor(Math.random()*telsDemoCliente.length)]; pedPend={lav:lav.n,lat:lav.lat,lng:lav.lng,addr:lav.addr,precio:precios[pi],serv:servs[pi],ref:ref,fase:0,telCliente:telClienteDemo}; document.getElementById('ip-price').textContent=precios[pi]+'€'; document.getElementById('ip-serv').textContent=servs[pi]; document.getElementById('ip-lav').textContent=lav.n; document.getElementById('ip-addr').textContent=lav.addr; document.getElementById('ip-dist').textContent='📍 '+(Math.random()+0.3).toFixed(1)+' km · '+Math.floor(3+Math.random()*6)+' min'; document.getElementById('ip-ref').textContent='Ref: '+ref; document.getElementById('ip-fill').style.width='100%'; document.getElementById('ip-exp').textContent='20s'; document.getElementById('incoming').classList.add('show'); initIncMap(lav.lat,lav.lng); if(navigator.vibrate)navigator.vibrate([300,100,300,100,300]); pedirNotifPedido(pedPend); expSec=20;clearInterval(expIv); expIv=setInterval(function(){ expSec--; document.getElementById('ip-exp').textContent=expSec+'s'; document.getElementById('ip-fill').style.width=((expSec/20)*100)+'%'; if(expSec<=0){clearInterval(expIv);reject();addAdmin('⚠️ Pedido '+pedPend.ref+' expiró.');} },1000); } function initIncMap(lat,lng){ if(!incMap){var el=document.getElementById('inc-map');el.style.height='100%';incMap=L.map('inc-map',{zoomControl:false,attributionControl:false,dragging:false,tap:false});L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png',{maxZoom:19}).addTo(incMap);} incMap.setView([lat,lng],16); if(incMark)incMark.remove(); var html='
🏪
'; incMark=L.marker([lat,lng],{icon:L.divIcon({html:html,className:'',iconSize:[48,48],iconAnchor:[24,24]})}).addTo(incMap); setTimeout(function(){if(incMap)incMap.invalidateSize();},100); } function accept(){ clearInterval(expIv);document.getElementById('incoming').classList.remove('show'); if(!pedPend)return; mostrarPedido(pedPend); aceptarEnFirebase(pedPend.ref); showToast('✅ Pedido aceptado'); addAdmin('✅ '+nombre+' aceptó '+pedPend.ref+' · '+pedPend.lav); pedPend=null; } function reject(){clearInterval(expIv);document.getElementById('incoming').classList.remove('show');pedPend=null;} // ── NOTIFICACIONES ── function pedirNotif(){if('Notification' in window&&Notification.permission==='default')Notification.requestPermission();} function notif(t,b){if('Notification' in window&&Notification.permission==='granted')new Notification(t,{body:b,icon:'icons/icon-192.png',tag:'lavobcn'});} function pedirNotifPedido(p){notif('🛵 Nuevo pedido — '+p.precio+'€',p.lav+' · '+p.addr+'\n📍 '+((Math.random()+0.3).toFixed(1))+' km · Pago efectivo\n⏱ Expira en 20s');} // ── CHAT ── function initChat(){ var feed=document.getElementById('chat-feed'); var tw=document.createElement('div');tw.className='tw';tw.id='tw'; tw.innerHTML='
'; feed.appendChild(tw); addAdmin('Hola '+nombre+' 👋 Estoy aquí. Escríbeme lo que necesites.'); unread=1;var b=document.getElementById('sup-badge');b.textContent='1';b.classList.add('show'); } function addAdmin(txt,h){ var feed=document.getElementById('chat-feed'),tw=document.getElementById('tw'); var d=document.createElement('div');d.className='cmsg a'; d.innerHTML='
Admin LavoBCN
'+txt+'
'+(h||hora())+'
'; feed.insertBefore(d,tw); if(!document.getElementById('soporte').classList.contains('open')){unread++;var b=document.getElementById('sup-badge');b.textContent=unread;b.classList.add('show');} scrollFeed(); } function sendMsg(){ var inp=document.getElementById('cinp'),txt=inp.value.trim();if(!txt)return; var feed=document.getElementById('chat-feed'),tw=document.getElementById('tw'); var d=document.createElement('div');d.className='cmsg r'; d.innerHTML='
'+nombre+'
'+txt+'
'+hora()+'
'; feed.insertBefore(d,tw);inp.value='';inp.style.height=''; tw.style.display='flex';scrollFeed(); var RESP={'pedido':'✅ ¿Qué pedido? Dame el número.','rider':'🆘 Buscando disponible ahora.','acc':'🚨 ¿Estás bien? ¿Necesitas ambulancia?','app':'📱 Cuéntame qué falla.','pago':'💵 ¿Qué duda tienes?'}; var resp='👍 Recibido.'; var l=txt.toLowerCase(); if(l.includes('perfect')||l.includes('prim'))resp='✅ Anotado. ¿Recoges o entregas?'; else if(l.includes('entrega')||l.includes('complet'))resp='🎉 Anotado. Bien hecho.'; else if(l.includes('problem'))resp='⚠️ Cuéntame qué pasó.'; else if(l.includes('pausa'))resp='⏸ Ok, descansa. Avísame al volver.'; setTimeout(function(){tw.style.display='none';addAdmin(resp);},1000+Math.random()*600); } function qs(t){document.getElementById('cinp').value=t;sendMsg();} function ayuda(t){var m={pedido:'🛍️ Necesito ayuda con un pedido.',rider:'🆘 Necesito otro repartidor.',acc:'🚨 URGENTE — Accidente o vehículo averiado.',app:'📱 La app tiene un problema.',pago:'💵 Tengo una duda con el pago.'};document.getElementById('cinp').value=m[t]||'Necesito ayuda';sendMsg();} function scrollFeed(){var f=document.getElementById('chat-feed');if(f)setTimeout(function(){f.scrollTop=f.scrollHeight;},60);} // ── UI ── function openSB(){document.getElementById('sidebar').classList.add('open');} function closeSB(){document.getElementById('sidebar').classList.remove('open');} function openSop(){document.getElementById('soporte').classList.add('open');unread=0;var b=document.getElementById('sup-badge');b.textContent='';b.classList.remove('show');scrollFeed();} function closeSop(){document.getElementById('soporte').classList.remove('open');} function showToast(m){var t=document.getElementById('toast');t.textContent=m;t.classList.add('show');setTimeout(function(){t.classList.remove('show');},2800);} // Animacion typing dots var style=document.createElement('style');style.textContent='@keyframes td{0%,60%,100%{transform:translateY(0);}30%{transform:translateY(-5px);}}';document.head.appendChild(style); var style=document.createElement('style');style.textContent='@keyframes td{0%,60%,100%{transform:translateY(0);}30%{transform:translateY(-5px);}}';document.head.appendChild(style);