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