Created
March 31, 2026 04:11
-
-
Save JuninhoFreitas/0fb229ad02a4022b0831f24f6f1f160e to your computer and use it in GitHub Desktop.
Uber Trips Export
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Aperta F12 -> clique na aba console -> copie e cole esse texto lá e aperte enter | |
| // No canto inferior direito vai surgir um botão de exportar dados das ultimas 50 corridas | |
| (function () { | |
| 'use strict'; | |
| const PAGE_SIZE = 50; | |
| // ─── Estado global ──────────────────────────────────────────────────────── | |
| let allRows = []; | |
| let currentCursor = null; | |
| let hasMore = false; | |
| // ─── UI ─────────────────────────────────────────────────────────────────── | |
| const container = document.createElement('div'); | |
| Object.assign(container.style, { | |
| position: 'fixed', | |
| bottom: '24px', | |
| right: '24px', | |
| zIndex: '99999', | |
| display: 'flex', | |
| flexDirection: 'column', | |
| alignItems: 'flex-end', | |
| gap: '8px', | |
| fontFamily: 'Arial, sans-serif', | |
| }); | |
| document.body.appendChild(container); | |
| const statusEl = document.createElement('div'); | |
| Object.assign(statusEl.style, { | |
| padding: '8px 14px', | |
| background: '#222', | |
| color: '#fff', | |
| borderRadius: '6px', | |
| fontSize: '13px', | |
| display: 'none', | |
| maxWidth: '320px', | |
| lineHeight: '1.5', | |
| }); | |
| container.appendChild(statusEl); | |
| const btnRow = document.createElement('div'); | |
| Object.assign(btnRow.style, { display: 'flex', gap: '8px' }); | |
| container.appendChild(btnRow); | |
| function makeBtn(label, bg) { | |
| const b = document.createElement('button'); | |
| b.innerText = label; | |
| Object.assign(b.style, { | |
| padding: '12px 18px', | |
| background: bg, | |
| color: '#fff', | |
| border: 'none', | |
| borderRadius: '8px', | |
| fontSize: '14px', | |
| fontWeight: 'bold', | |
| cursor: 'pointer', | |
| boxShadow: '0 4px 12px rgba(0,0,0,0.3)', | |
| whiteSpace: 'nowrap', | |
| }); | |
| return b; | |
| } | |
| const btnFetch = makeBtn('🚗 Buscar 50 corridas', '#000'); | |
| const btnMore = makeBtn('➕ Buscar mais 50', '#1a73e8'); | |
| const btnExport = makeBtn('📥 Exportar CSV', '#2e7d32'); | |
| btnMore.style.display = 'none'; | |
| btnExport.style.display = 'none'; | |
| btnRow.appendChild(btnFetch); | |
| btnRow.appendChild(btnMore); | |
| btnRow.appendChild(btnExport); | |
| // ─── Helpers ────────────────────────────────────────────────────────────── | |
| function setStatus(msg, color = '#fff') { | |
| statusEl.style.display = 'block'; | |
| statusEl.style.color = color; | |
| statusEl.innerText = msg; | |
| } | |
| function getHeaders() { | |
| return { | |
| 'accept': '*/*', | |
| 'content-type': 'application/json', | |
| 'x-csrf-token': 'x', | |
| }; | |
| } | |
| async function fetchTrips(cursor = null) { | |
| const body = { role: 'rider', options: { hidden: false } }; | |
| if (cursor) body.cursor = cursor; | |
| const res = await fetch('/api/getTrips?localeCode=pt_BR', { | |
| method: 'POST', | |
| headers: getHeaders(), | |
| body: JSON.stringify(body), | |
| credentials: 'include', | |
| }); | |
| return res.json(); | |
| } | |
| async function fetchTripDetail(uuid) { | |
| const res = await fetch('/api/getTrip?localeCode=pt_BR', { | |
| method: 'POST', | |
| headers: getHeaders(), | |
| body: JSON.stringify({ role: 'rider', uuid }), | |
| credentials: 'include', | |
| }); | |
| return res.json(); | |
| } | |
| function formatDate(ms) { | |
| return new Date(ms).toLocaleString('pt-BR', { | |
| timeZone: 'America/Sao_Paulo', | |
| day: '2-digit', month: '2-digit', year: 'numeric', | |
| hour: '2-digit', minute: '2-digit', | |
| }); | |
| } | |
| function formatDuration(seconds) { | |
| if (!seconds) return ''; | |
| return `${Math.floor(seconds / 60)}min ${seconds % 60}s`; | |
| } | |
| const STATUS_MAP = { | |
| ORDER_STATUS_COMPLETED: 'Concluída', | |
| ORDER_STATUS_REQUESTER_CANCELED: 'Cancelada', | |
| ORDER_STATUS_NO_DRIVERS_AVAILABLE: 'Sem motoristas', | |
| }; | |
| function escapeCSV(val) { | |
| if (val === null || val === undefined) return ''; | |
| const str = String(val); | |
| return (str.includes(',') || str.includes('"') || str.includes('\n')) | |
| ? '"' + str.replace(/"/g, '""') + '"' | |
| : str; | |
| } | |
| function toCSV(rows) { | |
| const headers = ['Data/Hora', 'Status', 'Produto', 'Origem', 'Destino', 'Distância (km)', 'Duração', 'Valor (BRL)']; | |
| const lines = [headers.map(escapeCSV).join(',')]; | |
| for (const r of rows) { | |
| lines.push([r.date, r.status, r.product, r.pickup, r.dropoff, r.distance, r.duration, r.fare] | |
| .map(escapeCSV).join(',')); | |
| } | |
| return lines.join('\n'); | |
| } | |
| function downloadCSV(rows) { | |
| const bom = '\uFEFF'; | |
| const blob = new Blob([bom + toCSV(rows)], { type: 'text/csv;charset=utf-8;' }); | |
| const url = URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = `corridas_uber_${new Date().toISOString().slice(0, 10)}.csv`; | |
| a.click(); | |
| URL.revokeObjectURL(url); | |
| } | |
| function updateUI() { | |
| btnMore.style.display = hasMore ? 'block' : 'none'; | |
| btnExport.style.display = allRows.length > 0 ? 'block' : 'none'; | |
| if (hasMore) btnMore.innerText = `➕ Buscar mais 50 (${allRows.length} carregadas)`; | |
| } | |
| function disableButtons(val) { | |
| btnFetch.disabled = val; | |
| btnMore.disabled = val; | |
| btnExport.disabled = val; | |
| } | |
| // ─── Coleta um lote: preenche batchTrips até PAGE_SIZE ─────────────────── | |
| async function collectBatch(startCursor) { | |
| const batchTrips = [...(window.__uberOverflow || [])]; | |
| window.__uberOverflow = []; | |
| let internalCursor = startCursor; | |
| while (batchTrips.length < PAGE_SIZE) { | |
| setStatus('📋 Buscando lista de corridas...'); | |
| const res = await fetchTrips(internalCursor); | |
| if (res.status !== 'success') throw new Error('Erro na API: ' + JSON.stringify(res)); | |
| const trips = res.data.trips || []; | |
| batchTrips.push(...trips); | |
| internalCursor = res.data.cursor || null; | |
| if (!internalCursor || trips.length === 0) break; | |
| } | |
| const toProcess = batchTrips.slice(0, PAGE_SIZE); | |
| const remainder = batchTrips.slice(PAGE_SIZE); | |
| hasMore = remainder.length > 0 || internalCursor !== null; | |
| currentCursor = internalCursor; | |
| window.__uberOverflow = remainder; | |
| return toProcess; | |
| } | |
| // ─── Detalha e converte uma lista de trips em rows ─────────────────────── | |
| async function detailTrips(trips) { | |
| const newRows = []; | |
| for (let i = 0; i < trips.length; i++) { | |
| const trip = trips[i]; | |
| setStatus(`🔍 Detalhando corrida ${allRows.length + i + 1} (${i + 1}/${trips.length})...`); | |
| try { | |
| const detail = await fetchTripDetail(trip.uuid); | |
| const d = detail.data || {}; | |
| newRows.push({ | |
| date: formatDate(d.requestedAt || trip.requestedAt), | |
| status: STATUS_MAP[d.status || trip.status] || (d.status || trip.status), | |
| product: d.product || trip.product, | |
| pickup: d.pickupAddress || '', | |
| dropoff: d.dropoffAddress || '', | |
| distance: d.distance != null ? d.distance.toFixed(2) : '', | |
| duration: formatDuration(d.duration), | |
| fare: d.fare != null ? d.fare.toFixed(2).replace('.', ',') : '', | |
| }); | |
| } catch { | |
| newRows.push({ | |
| date: formatDate(trip.requestedAt), status: trip.status, | |
| product: trip.product, pickup: '', dropoff: '', distance: '', duration: '', fare: '', | |
| }); | |
| } | |
| await new Promise(r => setTimeout(r, 300)); | |
| } | |
| return newRows; | |
| } | |
| // ─── Eventos dos botões ─────────────────────────────────────────────────── | |
| btnFetch.addEventListener('click', async () => { | |
| // Reset completo | |
| allRows = []; | |
| currentCursor = null; | |
| hasMore = false; | |
| window.__uberOverflow = []; | |
| disableButtons(true); | |
| try { | |
| const trips = await collectBatch(null); | |
| const rows = await detailTrips(trips); | |
| allRows.push(...rows); | |
| setStatus( | |
| `✅ ${allRows.length} corridas carregadas.${hasMore ? ' Há mais disponíveis.' : ' Todas carregadas!'}`, | |
| '#4caf50' | |
| ); | |
| updateUI(); | |
| } catch (err) { | |
| console.error(err); | |
| setStatus('❌ Erro: ' + err.message, '#f44336'); | |
| } finally { | |
| disableButtons(false); | |
| btnFetch.innerText = '🔄 Rebuscar tudo'; | |
| } | |
| }); | |
| btnMore.addEventListener('click', async () => { | |
| disableButtons(true); | |
| try { | |
| const trips = await collectBatch(currentCursor); | |
| const rows = await detailTrips(trips); | |
| allRows.push(...rows); | |
| setStatus( | |
| `✅ ${allRows.length} corridas carregadas.${hasMore ? ' Há mais disponíveis.' : ' Todas carregadas!'}`, | |
| '#4caf50' | |
| ); | |
| updateUI(); | |
| } catch (err) { | |
| console.error(err); | |
| setStatus('❌ Erro: ' + err.message, '#f44336'); | |
| } finally { | |
| disableButtons(false); | |
| } | |
| }); | |
| btnExport.addEventListener('click', () => { | |
| if (allRows.length === 0) return; | |
| downloadCSV(allRows); | |
| setStatus(`📥 CSV exportado com ${allRows.length} corridas!`, '#4caf50'); | |
| }); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment