OppoGlobe Wash
On-demand car care

Wash your car, wherever you are.

Choose mobile service or drive to a nearby wash.

Your wash locationFinding your location…
Finding nearby car wash providers…
VehicleChoose a vehicle added by this business
Select a provider first
ServiceOnly priced services for that vehicle are shown
Select your vehicle first
Service—
Travel—
Total—
Choose a nearby provider to continue.
Your car-wash account

Wash history

See active, completed and cancelled washes together with what you paid.

—All washes
—Completed
—Total paid
Loading your wash history…
Live tracking

Your active wash

When a mobile washer is en route, watch them approach on the map.

No active wash selected yet.
Checkout

My payments

Pay with Yoco or cash, then keep every wash payment in one place.

Payment history

Loading payments…
Live map connected
function recenterLiveMap(){mapUserMoved=false;if(currentTrackingData?.provider_location&&userPos){const p=currentTrackingData.provider_location;const bounds=new google.maps.LatLngBounds();bounds.extend(userPos);bounds.extend({lat:Number(p.latitude),lng:Number(p.longitude)});map.fitBounds(bounds,70)}else if(userPos){map.panTo(userPos);map.setZoom(14)}else if(IS_BUSINESS){focusCurrentRoleLocation()}} function startWorkspaceRefreshEngine(){ if(workspaceRefreshTimer)clearInterval(workspaceRefreshTimer);if(paymentRefreshTimer)clearInterval(paymentRefreshTimer); const sync=()=>{if(document.visibilityState!=='visible'||!navigator.onLine)return;if(IS_BUSINESS){loadBusinessRequests();loadWorkers();if(CAN_MANAGE)loadBusinessDashboard();}else{if(userPos)loadNearby(true);if(IS_AUTH)loadCustomerRequests();}}; workspaceRefreshTimer=setInterval(sync,IS_BUSINESS?7000:9000); paymentRefreshTimer=setInterval(()=>{if(document.visibilityState!=='visible'||!navigator.onLine)return;IS_BUSINESS&&CAN_MANAGE?loadBusinessPayments():IS_AUTH&&loadCustomerPayments();},30000); document.addEventListener('visibilitychange',()=>{if(document.visibilityState==='visible')sync();});window.addEventListener('focus',sync);window.addEventListener('online',()=>{setMapLiveBanner('Back online · syncing',true);sync();});window.addEventListener('offline',()=>setMapLiveBanner('Offline · showing last known activity',true)); } function clearPoliceMarkers(){policeMarkers.forEach(m=>m.setMap(null));policeMarkers=[]} function openSafetyCenter(context={}){window._carwashSafetyContext={...context};document.getElementById('carwashSafetyModal')?.classList.add('show');if(userPos)findNearestPolice(false)} function closeSafetyCenter(){document.getElementById('carwashSafetyModal')?.classList.remove('show');clearPoliceMarkers()} async function copySafetyLocation(){if(!userPos){locateMe();document.getElementById('safetyReportStatus').textContent='Finding your location…';return}const value=`${userPos.lat.toFixed(6)}, ${userPos.lng.toFixed(6)}`;try{await navigator.clipboard.writeText(value);document.getElementById('safetyReportStatus').textContent=`Location copied: ${value}`}catch(_){document.getElementById('safetyReportStatus').textContent=value}} function policeDistance(place){const loc=place.geometry?.location;if(!loc||!userPos)return Infinity;return distanceBetweenCoords(userPos.lat,userPos.lng,loc.lat(),loc.lng())} function findNearestPolice(showStatus=true){ const box=document.getElementById('policeList');if(!userPos){if(box)box.innerHTML='
Allow location access first so we can find police near you.
';locateMe();return} if(!google.maps.places){if(box)box.innerHTML='
Google Places is unavailable.
';return} if(showStatus&&box)box.innerHTML='
Finding nearby police…
'; new google.maps.places.PlacesService(map).nearbySearch({location:userPos,radius:15000,type:'police'},(results,status)=>{ if(status!==google.maps.places.PlacesServiceStatus.OK||!results?.length){if(box)box.innerHTML='
No police stations were returned nearby. In an emergency call 10111 or 112.
';return} const sorted=[...results].sort((a,b)=>policeDistance(a)-policeDistance(b)).slice(0,5);nearestPolice=sorted[0];clearPoliceMarkers(); sorted.forEach((p,i)=>{const pos={lat:p.geometry.location.lat(),lng:p.geometry.location.lng()};policeMarkers.push(new OppoMapMarker({position:pos,map,label:p.name||'Police station',subtitle:i===0?'Nearest police':'Police station',state:'police',icon:'fa-building-shield'}));}); if(box)box.innerHTML=sorted.map((p,i)=>{const lat=p.geometry.location.lat(),lng=p.geometry.location.lng();return `
${escapeHtml(p.name||'Police station')}${(policeDistance(p)/1000).toFixed(1)} km away${i===0?' · nearest':''}
Navigate
`}).join(''); }); } async function submitSafetyReport(){ if(!IS_AUTH)return;const ctx=window._carwashSafetyContext||{};const status=document.getElementById('safetyReportStatus');status.textContent='Submitting safety report…'; const providerLoc=currentTrackingData?.provider_location||null;const payload={request_id:ctx.requestId||activeRequestId||null,business_id:ctx.businessId||selectedProvider?.id||null,worker_id:ctx.workerId||selectedProvider?.available_worker_id||null,category:document.getElementById('safetyCategory').value,description:document.getElementById('safetyDescription').value.trim(),reporter_latitude:userPos?.lat??null,reporter_longitude:userPos?.lng??null,reported_latitude:providerLoc?.latitude??selectedProvider?.map_latitude??null,reported_longitude:providerLoc?.longitude??selectedProvider?.map_longitude??null,nearest_police_name:nearestPolice?.name||'',nearest_police_address:nearestPolice?.vicinity||'',nearest_police_place_id:nearestPolice?.place_id||''}; try{const data=await api('/carwash/api/safety/report/',{method:'POST',body:JSON.stringify(payload)});status.textContent=`Recorded. Reference: ${data.report?.id||'saved'}`;document.getElementById('safetyDescription').value='';}catch(e){status.textContent=e.message||'Could not submit the report.'} } let carwashKnownAlertIds=new Set(),carwashAlertsReady=false,carwashAlertTimer=null,carwashAudioUnlocked=false; function carwashAlertRows(data){if(Array.isArray(data))return data;for(const key of ['alerts','results','data','notifications'])if(Array.isArray(data?.[key]))return data[key];return[]} function carwashAlertId(a){return a?.id??a?.alert_id??a?.pk??null} function unlockCarwashAudio(){if(carwashAudioUnlocked)return;const a=document.getElementById('globalCarwashAlertAudio');if(!a)return;const p=a.play();if(p&&typeof p.then==='function')p.then(()=>{a.pause();a.currentTime=0;carwashAudioUnlocked=true}).catch(()=>{});} ['pointerdown','touchstart','keydown'].forEach(evt=>document.addEventListener(evt,unlockCarwashAudio,{once:false,passive:true})); function playCarwashAlertSound(){const a=document.getElementById('globalCarwashAlertAudio');if(!a)return;try{a.currentTime=0;const p=a.play();if(p)p.catch(()=>{});}catch(_){}} async function pollCarwashAlerts(){ if(!IS_AUTH||!navigator.onLine)return; try{const data=await api('/api/alerts/');const rows=carwashAlertRows(data);const ids=rows.map(carwashAlertId).filter(v=>v!=null).map(String);if(!carwashAlertsReady){carwashKnownAlertIds=new Set(ids);carwashAlertsReady=true;return}const fresh=rows.filter(a=>{const id=carwashAlertId(a);return id!=null&&!carwashKnownAlertIds.has(String(id))});carwashKnownAlertIds=new Set(ids);if(fresh.length){playCarwashAlertSound();setMapLiveBanner(fresh.length===1?'New OppoGlobe notification':`${fresh.length} new OppoGlobe notifications`,true);}} catch(_){/* never expose notification transport errors to the user */} } function startCarwashAlertListener(){if(!IS_AUTH)return;pollCarwashAlerts();if(carwashAlertTimer)clearInterval(carwashAlertTimer);carwashAlertTimer=setInterval(pollCarwashAlerts,15000)} document.addEventListener('DOMContentLoaded',startCarwashAlertListener); document.addEventListener('visibilitychange',()=>{if(document.visibilityState==='visible')pollCarwashAlerts()});window.addEventListener('focus',pollCarwashAlerts);