👤 Portal do Passageiro
🖥️ Portal Admin 🧑‍✈️ Portal Motorista

Olá, Passageiro

Solicite corridas, acompanhe sua carteira digital e revise seu histórico.

🗺️ Selecione origem e destino

Clique no mapa para definir os pontos ou use os botões abaixo.

Dica: clique primeiro na origem e depois no destino. Você pode arrastar os marcadores.

Carteira Digital

R$ 0,00

Saldo disponível

Estimativa de Corrida

R$ 0,00

Base R$ 5,00 + R$ 2,50/km + R$ 0,50/min

Favoritos

Motoristas com maior avaliação

Sem favoritos registrados.

Preferências

Formas de pagamento favoritas

📋 Histórico de Corridas

Data Motorista Origem Destino Valor Status Ações

📢 Comunicados

✅ Campanha de segurança ativa: use cinto e confirme a placa.
🎉 Você recebeu 10% de desconto na próxima corrida.

⚡ Ações Rápidas

`; const w=window.open('','_blank','width=720,height=720'); w.document.write(html); w.document.close(); } function verCupons(){ showToast('Você tem 2 cupons ativos. Use na próxima corrida!'); } function showToast(msg){ const toast = document.getElementById('toastPortal'); toast.textContent = msg; toast.classList.add('show'); setTimeout(()=>toast.classList.remove('show'), 2800); } function getStatusCache(){ try{ return JSON.parse(localStorage.getItem('tc_pass_status_cache')||'{}'); }catch(e){ return {}; } } function saveStatusCache(cache){ localStorage.setItem('tc_pass_status_cache', JSON.stringify(cache||{})); } function notifyStatus(c, prev){ const m=getMotorista(c.motorId); const veic=m?TC.veiculos.find(v=>v.motorId===m.id):null; const nome=m?m.nome.split(' ')[0]:'motorista'; const placa=veic?.placa?` (${veic.placa})`:''; let msg=''; if(prev?.motorId!==c.motorId && c.motorId){ msg=`Motorista ${nome}${placa} foi atribuído à sua corrida.`; } if(prev?.status!==c.status){ if(c.status==='aceita') msg=`Motorista a caminho! ${nome}${placa} aceitou sua corrida.`; if(c.status==='andamento') msg=`Sua corrida com ${nome}${placa} iniciou.`; if(c.status==='concluida') msg='Corrida concluída. Obrigado por viajar com a cooperativa!'; if(c.status==='cancelada') msg='Sua corrida foi cancelada.'; } if(msg){ showToast(msg); if(['aceita','andamento','concluida'].includes(c.status)) playNotifySound(); } } function sendProximoWhatsApp(c){ const p=getPassageiro(c.passId); const tel=(p?.tel || c.passTel || '').replace(/\D/g,''); if(!tel) return; const msg=`Olá ${p?.nome?.split(' ')[0]||'passageiro'}! 🚕\n\nSeu motorista está a aproximadamente 2 minutos de distância.\n\nAguarde no local combinado. Obrigado!`; const url=`https://wa.me/55${tel}?text=${encodeURIComponent(msg)}`; window.open(url,'_blank'); } function notifyTwoMinutes(c){ showToast('🚕 Seu motorista está a aproximadamente 2 minutos de distância!'); playNotifySound(); sendProximoWhatsApp(c); } function checkStatusChanges(corridas){ const cache=getStatusCache(); if(!cache.__init){ corridas.forEach(c=>{cache[c.id]={status:c.status,motorId:c.motorId,notif2min:c.notif2minSent};}); cache.__init=true; saveStatusCache(cache); return; } corridas.forEach(c=>{ const prev=cache[c.id]; if(!prev){ cache[c.id]={status:c.status,motorId:c.motorId,notif2min:c.notif2minSent}; return; } if(prev.status!==c.status || prev.motorId!==c.motorId){ notifyStatus(c, prev); cache[c.id]={status:c.status,motorId:c.motorId,notif2min:c.notif2minSent}; } if(c.etaMin<=2 && !c.notif2minSent && !prev.notif2min){ notifyTwoMinutes(c); c.notif2minSent=true; cache[c.id].notif2min=true; tcSave(); if(window.sbUpsertRows){ sbUpsertRows('corridas',[c]).catch(err=>showToast(`Erro Supabase: ${err.message||err}`)); } } }); saveStatusCache(cache); } function toast(msg){ showToast(msg); } let passMap=null; let passMarkers={origem:null,destino:null}; let mapMode='origem'; function setMapMode(mode){ mapMode=mode; document.getElementById('btnOrigem')?.classList.toggle('active', mode==='origem'); document.getElementById('btnDestino')?.classList.toggle('active', mode==='destino'); } function formatLatLng(latlng){ return `${latlng.lat.toFixed(5)}, ${latlng.lng.toFixed(5)}`; } function updateMapInput(type, latlng){ const input=document.getElementById(type==='origem'?'origem':'destino'); if(input){ input.value = formatLatLng(latlng); } } function setMarker(type, latlng){ if(passMarkers[type]){ passMarkers[type].setLatLng(latlng); } else { passMarkers[type]=L.marker(latlng,{draggable:true}); passMarkers[type].addTo(passMap); passMarkers[type].on('dragend', e=>updateMapInput(type, e.target.getLatLng())); } updateMapInput(type, latlng); } function clearMapPoints(){ Object.keys(passMarkers).forEach(k=>{ if(passMarkers[k]){ passMap.removeLayer(passMarkers[k]); passMarkers[k]=null; } }); const o=document.getElementById('origem'); const d=document.getElementById('destino'); if(o) o.value=''; if(d) d.value=''; setMapMode('origem'); } function initPassMap(){ const el=document.getElementById('passMap'); if(!el || passMap) return; passMap=L.map('passMap').setView([-15.7801,-47.9292], 4); L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',{ maxZoom:19, attribution:'© OpenStreetMap' }).addTo(passMap); passMap.on('click', e=>{ setMarker(mapMode, e.latlng); if(mapMode==='origem' && !passMarkers.destino){ setMapMode('destino'); } }); setMapMode('origem'); } function initPortal(){ const p=getPortalPassageiro(); if(p){ document.getElementById('passNome').value=p.nome||''; document.getElementById('passCpf').value=p.cpf||''; document.getElementById('passTel').value=p.tel||''; document.getElementById('passPgto').value=p.formaPgto||'pix'; } renderHistorico(); calcularEstimativa(); renderWallet(); initPassMap(); if(!window._portalTicker){ window._portalTicker=setInterval(()=>renderHistorico(),5000); } } window.addEventListener('load', ()=>{ setTimeout(initPortal, 400); });