/* AutoBooking Admin Dashboard — admin-dashboard.js v1.0.0 */ (function () { 'use strict'; const CFG = window.AB_ADMIN_CFG || {}; const API = CFG.api_root + 'autobooking/v1/admin/'; const NONCE = CFG.nonce; let lang = 'es'; let currentTab = 'overview'; let driversPage = 1, companiesPage = 1, tripsPage = 1, incidentsPage = 1; let reasonCallback = null; let currentIncidentId = null; let zoneMap = null, zoneMarker = null, zoneCircle = null; let tripMap = null, incidentMap = null, zonesOverviewMap = null; /* ── API ── */ function apiFetch(path, opts) { opts = opts || {}; return fetch(API + path, Object.assign({ headers: { 'X-WP-Nonce': NONCE, 'Content-Type': 'application/json' } }, opts)).then(function(r){ return r.json(); }); } /* ── DOM ── */ function qs(sel, ctx) { return (ctx || document).querySelector(sel); } function setText(id, val) { var el = qs('#' + id); if (el) el.textContent = val; } function show(id) { var el = qs('#' + id); if (el) el.style.display = ''; } function hide(id) { var el = qs('#' + id); if (el) el.style.display = 'none'; } function applyLang() { document.querySelectorAll('[data-es]').forEach(function(el) { el.textContent = lang === 'en' ? (el.dataset.en || el.dataset.es) : el.dataset.es; }); var btn = qs('#abad-lang-btn'); if (btn) btn.textContent = lang === 'en' ? 'EN' : 'ES'; } function fmtMoney(v, currency) { return (currency || 'USD') + ' ' + parseFloat(v || 0).toFixed(2); } function fmtDate(dt) { if (!dt) return '—'; return new Date(dt).toLocaleString('es-CO', { dateStyle: 'short', timeStyle: 'short' }); } function escHtml(str) { if (str == null) return ''; return String(str).replace(/&/g,'&').replace(//g,'>').replace(/"/g,'"'); } function statusDot(online) { return ''; } function renderPagination(containerId, currentPage, total, perPage, onClick) { var container = qs('#' + containerId); if (!container) return; var totalPages = Math.ceil(total / perPage) || 1; container.innerHTML = ''; if (totalPages <= 1) return; function mkBtn(label, page, active) { var btn = document.createElement('button'); btn.className = 'abad-page-btn' + (active ? ' abad-page-btn--active' : ''); btn.textContent = label; btn.disabled = (page < 1 || page > totalPages); btn.onclick = function() { onClick(page); }; container.appendChild(btn); } mkBtn(lang === 'en' ? 'Prev' : 'Ant', currentPage - 1, false); for (var i = 1; i <= totalPages; i++) { if (totalPages > 7 && Math.abs(i - currentPage) > 2 && i !== 1 && i !== totalPages) continue; mkBtn(i, i, i === currentPage); } mkBtn(lang === 'en' ? 'Next' : 'Sig', currentPage + 1, false); } function darkMapStyle() { return [ { elementType: 'geometry', stylers: [{ color: '#212121' }] }, { elementType: 'labels.text.fill', stylers: [{ color: '#757575' }] }, { featureType: 'road', elementType: 'geometry', stylers: [{ color: '#2c2c2c' }] }, { featureType: 'water', elementType: 'geometry', stylers: [{ color: '#000000' }] }, ]; } function downloadCSV(csv, filename) { var blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' }); var url = URL.createObjectURL(blob); var a = document.createElement('a'); a.href = url; a.download = filename || 'export.csv'; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } /* ── Tabs ── */ function switchTab(tab) { currentTab = tab; document.querySelectorAll('.abad-tab').forEach(function(b) { b.classList.toggle('abad-tab--active', b.dataset.tab === tab); }); document.querySelectorAll('.abad-panel').forEach(function(p) { p.style.display = 'none'; }); var panel = qs('#panel-' + tab); if (panel) panel.style.display = ''; loadTab(tab); } function loadTab(tab) { if (tab === 'overview') loadOverview(); if (tab === 'drivers') { loadDrivers(); loadPending(); } if (tab === 'companies') loadCompanies(); if (tab === 'trips') loadTrips(); if (tab === 'incidents') loadIncidents(); if (tab === 'zones') loadZones(); if (tab === 'settings') loadSettings(); } /* ── Overview ── */ function loadOverview() { apiFetch('overview').then(function(data) { setText('kpi-trips-today', data.trips_today != null ? data.trips_today : '—'); setText('kpi-trips-week', data.trips_week != null ? data.trips_week : '—'); setText('kpi-active', data.active_trips != null ? data.active_trips : '—'); setText('kpi-online', data.drivers_online != null ? data.drivers_online : '—'); setText('kpi-revenue', '$' + parseFloat(data.revenue_today || 0).toFixed(2)); setText('kpi-sos', data.sos_open != null ? data.sos_open : 0); setText('kpi-pending', data.pending_drivers != null ? data.pending_drivers : 0); var sosWrap = qs('#kpi-sos-wrap'); var pendWrap = qs('#kpi-pending-wrap'); if (sosWrap) sosWrap.style.display = data.sos_open > 0 ? '' : 'none'; if (pendWrap) pendWrap.style.display = data.pending_drivers > 0 ? '' : 'none'; var sosInd = qs('#abad-sos-indicator'); if (sosInd) { sosInd.style.display = data.sos_open > 0 ? 'flex' : 'none'; setText('abad-sos-count', data.sos_open); } var sosBadge = qs('#tab-sos-badge'); var pendBadge = qs('#tab-pending-badge'); if (sosBadge) { sosBadge.style.display = data.sos_open > 0 ? '' : 'none'; sosBadge.textContent = data.sos_open; } if (pendBadge) { pendBadge.style.display = data.pending_drivers > 0 ? '' : 'none'; pendBadge.textContent = data.pending_drivers; } var inline = qs('#pending-count-inline'); if (inline) inline.textContent = data.pending_drivers > 0 ? data.pending_drivers : ''; renderChart(data.chart || []); }); } function renderChart(chart) { var container = qs('#abad-overview-chart'); if (!container) return; container.innerHTML = ''; if (!chart.length) { container.innerHTML = '

Sin datos.

'; return; } var max = Math.max.apply(null, chart.map(function(d){ return parseInt(d.total) || 0; })) || 1; chart.forEach(function(day) { var bar = document.createElement('div'); bar.className = 'abad-bar-chart__bar'; var pct = Math.max(4, Math.round((parseInt(day.total || 0) / max) * 100)); bar.style.height = pct + '%'; bar.title = day.the_date + ': ' + day.total + ' viajes | $' + parseFloat(day.revenue || 0).toFixed(2); container.appendChild(bar); }); } /* ── Drivers ── */ function loadDrivers(page) { if (page) driversPage = page; var search = (qs('#driver-search') || {}).value || ''; var filter = (qs('#driver-filter') || {}).value || ''; apiFetch('drivers?page=' + driversPage + '&search=' + encodeURIComponent(search) + '&filter=' + filter).then(function(data) { var tbody = qs('#drivers-tbody'); if (!tbody) return; if (!data.drivers || !data.drivers.length) { tbody.innerHTML = 'Sin conductores'; return; } tbody.innerHTML = data.drivers.map(function(d) { return '' + '' + (d.photo_url ? '' : '') + escHtml(d.name) + '
' + escHtml(d.email) + '' + '' + escHtml(d.vehicle_type || '—') + ' ' + escHtml(d.vehicle_plate || '') + '' + '' + statusDot(d.online) + (d.online ? 'Online' : 'Offline') + '' + '' + (d.avg_rating > 0 ? '★ ' + d.avg_rating : '—') + '' + '' + d.trips_total + '' + '' + ''; }).join(''); applyLang(); renderPagination('drivers-pagination', driversPage, data.total, data.per_page, loadDrivers); }); } function loadPending() { apiFetch('drivers/pending').then(function(data) { var list = qs('#abad-pending-list'); var count = (data.drivers || []).length; var badge = qs('#tab-pending-badge'); var inline = qs('#pending-count-inline'); if (badge) { badge.style.display = count > 0 ? '' : 'none'; badge.textContent = count; } if (inline) { inline.textContent = count > 0 ? count : ''; } if (!list) return; if (!count) { list.innerHTML = '

No hay conductores pendientes.

'; return; } list.innerHTML = data.drivers.map(function(d) { return '
' + '
' + escHtml(d.name) + '
' + '
' + escHtml(d.email) + ' | ' + escHtml(d.phone || '—') + '
' + 'Vehiculo: ' + escHtml(d.vehicle_type || '—') + ' ' + escHtml(d.vehicle_plate || '') + '
' + 'Registro: ' + fmtDate(d.registered) + '
' + 'TODO: documentos (ver CHANGES.md)' + '
' + '
' + '' + '' + '
' + '
'; }).join(''); }); } window.abadApproveDriver = function(uid) { if (!confirm(lang === 'en' ? 'Approve driver?' : '¿Aprobar conductor?')) return; apiFetch('drivers/approve', { method: 'POST', body: JSON.stringify({ user_id: uid }) }).then(function(r) { if (r.ok) { loadDrivers(); loadPending(); loadOverview(); } else alert(r.message || 'Error'); }); }; window.abadRejectDriver = function(uid, name) { showReasonModal((lang === 'en' ? 'Reject: ' : 'Rechazar: ') + name, function(reason) { apiFetch('drivers/reject', { method: 'POST', body: JSON.stringify({ user_id: uid, reason: reason }) }).then(function(r) { if (r.ok) { loadDrivers(); loadPending(); loadOverview(); } else alert(r.message || 'Error'); }); }); }; window.abadSuspendDriver = function(uid, name) { showReasonModal((lang === 'en' ? 'Suspend: ' : 'Suspender: ') + name, function(reason) { apiFetch('drivers/suspend', { method: 'POST', body: JSON.stringify({ user_id: uid, reason: reason }) }).then(function(r) { if (r.ok) loadDrivers(); else alert(r.message || 'Error'); }); }); }; /* ── Companies ── */ function loadCompanies(page) { if (page) companiesPage = page; var search = (qs('#company-search') || {}).value || ''; apiFetch('companies?page=' + companiesPage + '&search=' + encodeURIComponent(search)).then(function(data) { var tbody = qs('#companies-tbody'); if (!tbody) return; if (!data.companies || !data.companies.length) { tbody.innerHTML = 'Sin empresas'; return; } tbody.innerHTML = data.companies.map(function(c) { var isActive = c.active == 1; return '' + '' + escHtml(c.name) + '
' + escHtml(c.email) + '' + '' + escHtml(c.contact_name || '—') + '
' + escHtml(c.phone || '') + '' + '' + (c.trips_count || 0) + '' + '' + (isActive ? 'Activa' : 'Inactiva') + '' + '' + (isActive ? '' : '') + ' ' + '' + ''; }).join(''); renderPagination('companies-pagination', companiesPage, data.total, data.per_page, loadCompanies); }); } window.abadActivateCompany = function(cid) { apiFetch('companies/activate', { method: 'POST', body: JSON.stringify({ company_id: cid }) }).then(function(r) { if (r.ok) loadCompanies(); else alert(r.message || 'Error'); }); }; window.abadDeactivateCompany = function(cid) { if (!confirm(lang === 'en' ? 'Deactivate company?' : '¿Desactivar empresa?')) return; apiFetch('companies/deactivate', { method: 'POST', body: JSON.stringify({ company_id: cid }) }).then(function(r) { if (r.ok) loadCompanies(); else alert(r.message || 'Error'); }); }; window.abadCompanyInvoices = function(cid) { apiFetch(cid + '/invoices').then(function(data) { console.log('Facturas empresa ' + cid + ':', data.invoices); alert(lang === 'en' ? 'Invoices logged to console (' + (data.invoices || []).length + ' records).' : 'Facturas en consola (' + (data.invoices || []).length + ' registros).'); }); }; /* ── Trips ── */ function loadTrips(page) { if (page) tripsPage = page; var search = (qs('#trip-search') || {}).value || ''; var from = (qs('#trip-date-from') || {}).value || ''; var to = (qs('#trip-date-to') || {}).value || ''; var status = (qs('#trip-status-filter') || {}).value || ''; var params = 'page=' + tripsPage + '&search=' + encodeURIComponent(search) + '&date_from=' + from + '&date_to=' + to + '&status=' + status; apiFetch('trips?' + params).then(function(data) { var tbody = qs('#trips-tbody'); if (!tbody) return; if (!data.trips || !data.trips.length) { tbody.innerHTML = 'Sin viajes'; return; } tbody.innerHTML = data.trips.map(function(t) { return '' + '' + fmtDate(t.created_at) + '' + '' + escHtml((t.trip_uuid || '').slice(0,10)) + '…' + '' + escHtml(t.driver_name || '—') + '' + '' + escHtml(t.passenger_name || '—') + '' + '' + fmtMoney(t.fare_total_amount, t.currency) + '' + '' + escHtml(t.status) + '' + '' + (t.driver_rating ? '★ ' + t.driver_rating : '—') + '' + '' + ''; }).join(''); renderPagination('trips-pagination', tripsPage, data.total, data.per_page, loadTrips); }); } window.abadTripDetail = function(tid) { apiFetch('trips/' + tid).then(function(data) { var trip = data.trip || {}; show('modal-trip'); var content = qs('#modal-trip-content'); if (content) { content.innerHTML = '
' + '
Conductor
' + escHtml(trip.driver_name || '—') + '
' + '
Pasajero
' + escHtml(trip.passenger_name || '—') + '
' + '
Estado
' + escHtml(trip.status || '—') + '
' + '
Total
' + fmtMoney(trip.fare_total_amount, trip.currency) + '
' + '
Comision
' + fmtMoney(trip.platform_fee_amount, trip.currency) + '
' + '
Neto conductor
'+ fmtMoney(trip.driver_payout_amount, trip.currency) + '
' + '
Rating
' + (trip.driver_rating ? '★ ' + trip.driver_rating : '—') + '
' + '
Fecha
' + fmtDate(trip.created_at) + '
' + '
' + (data.chat && data.chat.length ? '
Chat
' + data.chat.map(function(m) { return '
' + escHtml(m.sender) + ': ' + escHtml(m.message) + '
'; }).join('') + '
' : ''); } setTimeout(function() { var mapEl = qs('#modal-trip-map'); if (!mapEl || !window.google || !data.positions || !data.positions.length) return; var mid = data.positions[Math.floor(data.positions.length / 2)]; tripMap = new google.maps.Map(mapEl, { zoom: 13, center: { lat: parseFloat(mid.lat), lng: parseFloat(mid.lng) }, styles: darkMapStyle() }); new google.maps.Polyline({ path: data.positions.map(function(p){ return { lat: parseFloat(p.lat), lng: parseFloat(p.lng) }; }), geodesic: true, strokeColor: '#FF6F00', strokeOpacity: 0.9, strokeWeight: 3, map: tripMap }); }, 150); }); }; /* ── Incidents ── */ function loadIncidents(page) { if (page) incidentsPage = page; var filter = (qs('#incident-filter') || {}).value || 'active'; apiFetch('incidents?filter=' + filter + '&page=' + incidentsPage).then(function(data) { var list = qs('#abad-incidents-list'); if (!list) return; if (!data.incidents || !data.incidents.length) { list.innerHTML = '

No hay incidentes.

'; return; } list.innerHTML = data.incidents.map(function(inc) { return '
' + '
' + escHtml(inc.driver_name || 'Conductor #' + inc.driver_id) + '
' + '
' + fmtDate(inc.incident_at) + ' | Estado: ' + escHtml(inc.status) + '' + (inc.has_audio ? ' | Audio' : '') + (inc.notes ? '
Nota: ' + escHtml(inc.notes) : '') + '
' + '
' + '' + (inc.status === 'active' ? '' + '' : '') + '
'; }).join(''); renderPagination('incidents-pagination', incidentsPage, data.total, 20, loadIncidents); }); } window.abadOpenIncident = function(id, lat, lng) { currentIncidentId = id; show('modal-incident'); var mapEl = qs('#modal-incident-map'); if (mapEl && window.google && lat && lng) { setTimeout(function() { incidentMap = new google.maps.Map(mapEl, { zoom: 15, center: { lat: parseFloat(lat), lng: parseFloat(lng) }, styles: darkMapStyle() }); new google.maps.Marker({ position: { lat: parseFloat(lat), lng: parseFloat(lng) }, map: incidentMap, icon: { path: google.maps.SymbolPath.CIRCLE, scale: 10, fillColor: '#ef4444', fillOpacity: 1, strokeColor: '#fff', strokeWeight: 2 } }); }, 150); } }; window.abadResolveIncident = function(id, status) { var note = (qs('#incident-note') || {}).value || ''; apiFetch('incidents/' + id + '/resolve', { method: 'POST', body: JSON.stringify({ status: status, note: note }) }).then(function(r) { if (r.ok) { hide('modal-incident'); loadIncidents(); loadOverview(); } else alert(r.message || 'Error'); }); }; /* ── Zones ── */ function loadZones() { apiFetch('zones').then(function(zones) { var list = qs('#abad-zones-list'); if (list) { list.innerHTML = zones.length ? zones.map(function(z) { return '
' + '
' + escHtml(z.title) + ' [' + escHtml(z.type) + ']
' + '
Radio: ' + z.radius_km + ' km | Bono: ' + (z.bonus_amount > 0 ? '$'+z.bonus_amount+' '+z.bonus_currency : 'ninguno') + (z.expires_at ? '
Expira: ' + fmtDate(z.expires_at) : '') + '
' + '
' + '' + (z.active ? '' : '') + '
'; }).join('') : '

No hay zonas configuradas.

'; } initZonesMap(zones); }); } function initZonesMap(zones) { var mapEl = qs('#abad-zones-map'); if (!mapEl || !window.google) return; setTimeout(function() { zonesOverviewMap = new google.maps.Map(mapEl, { zoom: 11, center: { lat: 4.7110, lng: -74.0721 }, styles: darkMapStyle() }); zones.filter(function(z){ return z.active && z.lat && z.lng; }).forEach(function(z) { new google.maps.Circle({ center: { lat: parseFloat(z.lat), lng: parseFloat(z.lng) }, radius: parseFloat(z.radius_km)*1000, map: zonesOverviewMap, strokeColor: '#FF6F00', strokeOpacity: 0.7, strokeWeight: 2, fillColor: '#FF6F00', fillOpacity: 0.12 }); }); }, 200); } window.abadEditZone = function(id) { apiFetch('zones').then(function(zones) { var z = null; for (var i = 0; i < zones.length; i++) { if (zones[i].id == id) { z = zones[i]; break; } } if (!z) return; qs('#zone-id').value = z.id; qs('#zone-title').value = z.title; qs('#zone-type').value = z.type; qs('#zone-description').value = z.description || ''; qs('#zone-radius').value = z.radius_km; qs('#zone-bonus').value = z.bonus_amount; qs('#zone-countdown').value = z.countdown_seconds; qs('#zone-expires').value = z.expires_at ? z.expires_at.replace(' ','T').slice(0,16) : ''; qs('#zone-lat').value = z.lat; qs('#zone-lng').value = z.lng; show('modal-zone'); initZoneModal(parseFloat(z.lat), parseFloat(z.lng), parseFloat(z.radius_km)); }); }; window.abadDeactivateZone = function(id) { if (!confirm(lang === 'en' ? 'Deactivate zone?' : '¿Desactivar zona?')) return; apiFetch('zones/' + id + '/deactivate', { method: 'POST' }).then(function(r) { if (r.ok) loadZones(); }); }; function initZoneModal(lat, lng, radiusKm) { var mapEl = qs('#zone-modal-map'); if (!mapEl || !window.google) return; setTimeout(function() { var center = (lat && lng) ? { lat: lat, lng: lng } : { lat: 4.7110, lng: -74.0721 }; zoneMap = new google.maps.Map(mapEl, { zoom: 12, center: center, styles: darkMapStyle() }); if (lat && lng) { zoneMarker = new google.maps.Marker({ position: center, map: zoneMap }); zoneCircle = new google.maps.Circle({ center: center, radius: (radiusKm||2)*1000, map: zoneMap, strokeColor: '#FF6F00', strokeOpacity: 0.8, strokeWeight: 2, fillColor: '#FF6F00', fillOpacity: 0.15 }); } zoneMap.addListener('click', function(e) { var pos = { lat: e.latLng.lat(), lng: e.latLng.lng() }; qs('#zone-lat').value = pos.lat; qs('#zone-lng').value = pos.lng; if (zoneMarker) zoneMarker.setMap(null); if (zoneCircle) zoneCircle.setMap(null); zoneMarker = new google.maps.Marker({ position: pos, map: zoneMap }); var r = parseFloat((qs('#zone-radius')||{}).value || 2) * 1000; zoneCircle = new google.maps.Circle({ center: pos, radius: r, map: zoneMap, strokeColor: '#FF6F00', strokeOpacity: 0.8, strokeWeight: 2, fillColor: '#FF6F00', fillOpacity: 0.15 }); }); }, 200); } /* ── Settings ── */ function loadSettings() { apiFetch('settings').then(function(s) { if (qs('#cfg-name')) qs('#cfg-name').value = s.platform_name || ''; if (qs('#cfg-logo')) qs('#cfg-logo').value = s.platform_logo || ''; if (qs('#cfg-sos-email')) qs('#cfg-sos-email').value = s.sos_email || ''; if (qs('#cfg-fee-pct')) qs('#cfg-fee-pct').value = (parseFloat(s.platform_fee_pct||0.20)*100).toFixed(1); }); loadFareConfig(); } function loadFareConfig() { apiFetch('fare-config').then(function(rows) { var list = qs('#abad-fare-list'); if (!list) return; if (!rows.length) { list.innerHTML = '

Sin tarifas.

'; return; } list.innerHTML = '' + rows.map(function(r) { return '' + '' + '' + '' + '' + ''; }).join('') + '
PaisMonedaBasekmminComisionMin.
' + escHtml(r.country_code) + '' + escHtml(r.currency) + '$' + parseFloat(r.base_fare).toFixed(2) + '$' + parseFloat(r.per_km).toFixed(3) + '$' + parseFloat(r.per_minute).toFixed(3) + '' + (parseFloat(r.platform_fee_pct)*100).toFixed(1) + '%$' + parseFloat(r.minimum_fare).toFixed(2) + '
'; }); } /* ── Reason modal ── */ function showReasonModal(title, callback) { reasonCallback = callback; var el = qs('#modal-reason-title'); if (el) el.textContent = title; var txt = qs('#modal-reason-text'); if (txt) txt.value = ''; show('modal-reason'); } /* ── Init ── */ document.addEventListener('DOMContentLoaded', function() { document.querySelectorAll('.abad-tab').forEach(function(btn) { btn.addEventListener('click', function(){ switchTab(btn.dataset.tab); }); }); var langBtn = qs('#abad-lang-btn'); if (langBtn) langBtn.addEventListener('click', function() { lang = lang === 'es' ? 'en' : 'es'; applyLang(); }); var driverSearch = qs('#driver-search'); if (driverSearch) driverSearch.addEventListener('keydown', function(e){ if(e.key==='Enter'){ driversPage=1; loadDrivers(); } }); var driverFilter = qs('#driver-filter'); if (driverFilter) driverFilter.addEventListener('change', function(){ driversPage=1; loadDrivers(); }); var btnPending = qs('#btn-show-pending'); if (btnPending) btnPending.addEventListener('click', function() { var sec = qs('#abad-pending-section'); if (sec) sec.style.display = sec.style.display === 'none' ? '' : 'none'; }); var companySearch = qs('#company-search'); if (companySearch) companySearch.addEventListener('keydown', function(e){ if(e.key==='Enter'){ companiesPage=1; loadCompanies(); } }); var btnTripSearch = qs('#btn-trip-search'); if (btnTripSearch) btnTripSearch.addEventListener('click', function(){ tripsPage=1; loadTrips(); }); var btnExportCSV = qs('#btn-export-trips-csv'); if (btnExportCSV) btnExportCSV.addEventListener('click', function() { var from = (qs('#trip-date-from')||{}).value||''; var to = (qs('#trip-date-to')||{}).value||''; var status = (qs('#trip-status-filter')||{}).value||''; apiFetch('trips/export-csv?date_from=' + from + '&date_to=' + to + '&status=' + status).then(function(r){ if(r.csv) downloadCSV(r.csv, r.filename||'viajes.csv'); }); }); var incidentFilter = qs('#incident-filter'); if (incidentFilter) incidentFilter.addEventListener('change', function(){ incidentsPage=1; loadIncidents(); }); var btnRefreshInc = qs('#btn-refresh-incidents'); if (btnRefreshInc) btnRefreshInc.addEventListener('click', loadIncidents); var incClose = qs('#modal-incident-close'); if (incClose) incClose.addEventListener('click', function(){ hide('modal-incident'); }); var btnAttended = qs('#btn-incident-attended'); if (btnAttended) btnAttended.addEventListener('click', function(){ if(currentIncidentId) window.abadResolveIncident(currentIncidentId,'attended'); }); var btnResolved = qs('#btn-incident-resolved'); if (btnResolved) btnResolved.addEventListener('click', function(){ if(currentIncidentId) window.abadResolveIncident(currentIncidentId,'resolved'); }); var tripClose = qs('#modal-trip-close'); if (tripClose) tripClose.addEventListener('click', function(){ hide('modal-trip'); }); var reasonConfirm = qs('#modal-reason-confirm'); if (reasonConfirm) reasonConfirm.addEventListener('click', function() { var reason = (qs('#modal-reason-text')||{}).value || ''; hide('modal-reason'); if (reasonCallback) { reasonCallback(reason); reasonCallback = null; } }); var reasonCancel = qs('#modal-reason-cancel'); if (reasonCancel) reasonCancel.addEventListener('click', function(){ hide('modal-reason'); reasonCallback=null; }); var btnNewZone = qs('#btn-new-zone'); if (btnNewZone) btnNewZone.addEventListener('click', function() { ['#zone-id','#zone-title','#zone-description','#zone-expires','#zone-lat','#zone-lng'].forEach(function(s){ var el=qs(s); if(el) el.value=''; }); qs('#zone-radius').value=2; qs('#zone-bonus').value=0; qs('#zone-countdown').value=180; show('modal-zone'); initZoneModal(0,0,2); }); var zoneClose = qs('#modal-zone-close'); var zonCancel = qs('#btn-zone-cancel'); if (zoneClose) zoneClose.addEventListener('click', function(){ hide('modal-zone'); }); if (zonCancel) zonCancel.addEventListener('click', function(){ hide('modal-zone'); }); var btnZoneSave = qs('#btn-zone-save'); if (btnZoneSave) btnZoneSave.addEventListener('click', function() { var lat = parseFloat((qs('#zone-lat')||{}).value||0); var lng = parseFloat((qs('#zone-lng')||{}).value||0); var title = (qs('#zone-title')||{}).value||''; if (!title || !lat || !lng) { alert(lang==='en'?'Click the map and enter a title.':'Haz clic en el mapa y escribe un titulo.'); return; } var expires = (qs('#zone-expires')||{}).value||''; var payload = { id: parseInt((qs('#zone-id')||{}).value||0), title: title, description: (qs('#zone-description')||{}).value||'', type: (qs('#zone-type')||{}).value||'hotspot', lat: lat, lng: lng, radius_km: parseFloat((qs('#zone-radius')||{}).value||2), bonus_amount: parseFloat((qs('#zone-bonus')||{}).value||0), countdown_seconds: parseInt((qs('#zone-countdown')||{}).value||180), expires_at: expires ? expires.replace('T',' ') : '', }; apiFetch('zones/save', { method: 'POST', body: JSON.stringify(payload) }).then(function(r){ if(r.ok){ hide('modal-zone'); loadZones(); } else alert(r.message||'Error'); }); }); var btnSaveSettings = qs('#btn-save-settings'); if (btnSaveSettings) btnSaveSettings.addEventListener('click', function() { var payload = { platform_name: (qs('#cfg-name')||{}).value||'', platform_logo: (qs('#cfg-logo')||{}).value||'', sos_email: (qs('#cfg-sos-email')||{}).value||'', platform_fee_pct: parseFloat((qs('#cfg-fee-pct')||{}).value||20)/100, }; apiFetch('settings/save', { method:'POST', body: JSON.stringify(payload) }).then(function(r){ if(r.ok){ var el=qs('#cfg-success'); if(el){ el.style.display=''; setTimeout(function(){ el.style.display='none'; }, 2500); } } }); }); var btnSaveFare = qs('#btn-save-fare'); if (btnSaveFare) btnSaveFare.addEventListener('click', function() { var payload = { country_code: ((qs('#fare-country')||{}).value||'').toUpperCase(), currency: ((qs('#fare-currency')||{}).value||'USD').toUpperCase(), base_fare: parseFloat((qs('#fare-base')||{}).value||3), per_km: parseFloat((qs('#fare-per-km')||{}).value||1.80), per_minute: parseFloat((qs('#fare-per-min')||{}).value||0.30), minimum_fare: parseFloat((qs('#fare-minimum')||{}).value||5), platform_fee_pct: parseFloat((qs('#cfg-fee-pct')||{}).value||20)/100, }; apiFetch('fare-config/save', { method:'POST', body: JSON.stringify(payload) }).then(function(r){ if(r.ok){ loadFareConfig(); var el=qs('#fare-success'); if(el){ el.style.display=''; setTimeout(function(){ el.style.display='none'; }, 2500); } } else alert(r.message||'Error'); }); }); applyLang(); switchTab('overview'); setInterval(function(){ if(currentTab==='overview') loadOverview(); }, 60000); }); // ── SECURITY TAB ────────────────────────────────────────── async function loadSecuritySettings() { try { const r = await apiFetch('blocked-ips'); const countEl = document.getElementById('sec-blocked-count'); if (countEl) countEl.textContent = r.blocked_24h + ' IPs bloqueadas en las últimas 24h'; const el = document.getElementById('sec-blocked-table'); if (!el) return; if (!r.blocked.length) { el.innerHTML = '

Sin IPs bloqueadas.

'; return; } el.innerHTML = '' + r.blocked.map(b => '').join('') + '
IPMotivoExpira
'+b.ip+''+b.reason+''+b.expires_at+'
'; } catch(e) {} } window.absUnblock = async function(id) { await apiFetch('blocked-ips/'+id+'/unblock',{method:'POST'}); loadSecuritySettings(); }; document.getElementById('btn-save-sec') && document.getElementById('btn-save-sec').addEventListener('click', async function() { await apiFetch('security-settings/save', {method:'POST', body: JSON.stringify({ whitelist: document.getElementById('sec-whitelist').value, hcaptcha_site_key: document.getElementById('sec-hcaptcha-site').value, hcaptcha_secret: document.getElementById('sec-hcaptcha-secret').value, })}); var el = document.getElementById('sec-saved'); el.style.display='block'; setTimeout(function(){el.style.display='none';},3000); }); document.querySelectorAll('.abad-tab').forEach(function(btn) { if (btn.dataset.tab === 'settings') btn.addEventListener('click', loadSecuritySettings); }); })();