feat(pacifictrans): address by current GPS location for trips and freight, reverse geocoding API, and web APK download links
This commit is contained in:
parent
ccb4db4e17
commit
9a1190cc0f
@ -49,7 +49,7 @@
|
||||
<a href="#reservas" id="nav-link-reservas" class="active">🚗 Viajar</a>
|
||||
<a href="#fletes" id="nav-link-fletes">📦 Envíos</a>
|
||||
<a href="#estadisticas" id="nav-link-reviews">⭐ Reseñas</a>
|
||||
<a href="#descarga-apk" id="nav-link-app">📲 App Móvil</a>
|
||||
<a href="/PacifictransGo.apk" download="PacifictransGo.apk" id="nav-link-app" style="background: rgba(16, 185, 129, 0.15); color: #34d399; border: 1px solid rgba(16, 185, 129, 0.35); border-radius: 999px; padding: 0.35rem 0.85rem; font-weight: 600; text-decoration: none; display: inline-flex; align-items: center; gap: 0.35rem;">📲 Descargar App</a>
|
||||
|
||||
<!-- Botón de apertura de Dashboard Administrativo (visible solo para Administrador) -->
|
||||
<button type="button" class="btn-open-dashboard-header" id="nav-btn-open-admin" style="display: none;">
|
||||
@ -521,6 +521,27 @@
|
||||
<input type="text" class="form-input" id="package-desc" placeholder="Repuestos automotrices / Ropa / Medicamentos">
|
||||
</div>
|
||||
|
||||
<!-- DIRECCIONES DE RECOGIDA Y ENTREGA CON UBICACIÓN GPS -->
|
||||
<div class="form-group">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.35rem;">
|
||||
<label class="form-label" for="freight-pickup-address" style="margin-bottom: 0;">📍 Dirección de Recogida del Paquete (Origen)</label>
|
||||
<button type="button" id="btn-freight-gps-pickup" class="btn-gps-chip" title="Detectar mi ubicación actual para la recogida del paquete">
|
||||
📍 Mi Ubicación Actual
|
||||
</button>
|
||||
</div>
|
||||
<input type="text" class="form-input" id="freight-pickup-address" placeholder="Ej: Calle 5 #66-20, Cali (o pulsa 'Mi Ubicación Actual')">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.35rem;">
|
||||
<label class="form-label" for="freight-delivery-address" style="margin-bottom: 0;">🏁 Dirección de Entrega del Paquete (Destino)</label>
|
||||
<button type="button" id="btn-freight-gps-delivery" class="btn-gps-chip" title="Detectar mi ubicación actual para la entrega del paquete">
|
||||
📍 Mi Ubicación Actual
|
||||
</button>
|
||||
</div>
|
||||
<input type="text" class="form-input" id="freight-delivery-address" placeholder="Ej: Terminal Buenaventura, Muelle o Dirección exacta">
|
||||
</div>
|
||||
|
||||
<!-- MODALIDAD DE DESPACHO DE ENCOMIENDA: URGENTE INMEDIATO VS PROGRAMADO -->
|
||||
<div class="time-modality-container" id="freight-time-modality-container">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.5rem;">
|
||||
|
||||
@ -147,76 +147,125 @@ function initNativeBackButton() {
|
||||
});
|
||||
}
|
||||
|
||||
// 3. Geolocalización GPS Nativa de Alta Precisión
|
||||
function initNativeGPS() {
|
||||
const btnGps = document.getElementById('btn-native-gps-cali');
|
||||
if (!btnGps) return;
|
||||
// Variables globales de coordenadas GPS detectadas
|
||||
window.passengerGpsCoords = [-76.5320, 3.4215];
|
||||
window.freightPickupCoords = [-76.5320, 3.4215];
|
||||
window.freightDeliveryCoords = [-77.0250, 3.8820];
|
||||
|
||||
btnGps.addEventListener('click', async () => {
|
||||
// Helper Centralizado: Obtiene coordenadas GPS y geocodifica a dirección humana real
|
||||
async function getCurrentUserLocation(buttonEl = null) {
|
||||
let originalText = '';
|
||||
if (buttonEl) {
|
||||
triggerHaptic('medium');
|
||||
btnGps.disabled = true;
|
||||
const originalText = btnGps.innerHTML;
|
||||
btnGps.innerHTML = '⏳ Obteniendo GPS...';
|
||||
originalText = buttonEl.innerHTML;
|
||||
buttonEl.disabled = true;
|
||||
buttonEl.innerHTML = '⏳ Obteniendo GPS...';
|
||||
}
|
||||
|
||||
try {
|
||||
let coords = null;
|
||||
if (window.Capacitor?.isPluginAvailable?.('Geolocation')) {
|
||||
const { Geolocation } = window.Capacitor.Plugins;
|
||||
try {
|
||||
let coords = null;
|
||||
if (window.Capacitor?.isPluginAvailable?.('Geolocation')) {
|
||||
const { Geolocation } = window.Capacitor.Plugins;
|
||||
try {
|
||||
const check = await Geolocation.checkPermissions();
|
||||
if (check.location !== 'granted') {
|
||||
const req = await Geolocation.requestPermissions();
|
||||
if (req.location !== 'granted') {
|
||||
showNativeToast('⚠️ Permiso de ubicación requerido para GPS');
|
||||
btnGps.disabled = false;
|
||||
btnGps.innerHTML = originalText;
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
const pos = await Geolocation.getCurrentPosition({
|
||||
enableHighAccuracy: true,
|
||||
timeout: 12000
|
||||
});
|
||||
coords = { lat: pos.coords.latitude, lng: pos.coords.longitude };
|
||||
} else if (navigator.geolocation) {
|
||||
coords = await new Promise((resolve, reject) => {
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
p => resolve({ lat: p.coords.latitude, lng: p.coords.longitude }),
|
||||
err => reject(err),
|
||||
{ enableHighAccuracy: true, timeout: 12000 }
|
||||
);
|
||||
} catch (pErr) { }
|
||||
|
||||
const pos = await Geolocation.getCurrentPosition({
|
||||
enableHighAccuracy: true,
|
||||
timeout: 12000
|
||||
});
|
||||
coords = { lat: pos.coords.latitude, lng: pos.coords.longitude };
|
||||
} else if (navigator.geolocation) {
|
||||
coords = await new Promise((resolve, reject) => {
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
p => resolve({ lat: p.coords.latitude, lng: p.coords.longitude }),
|
||||
err => reject(err),
|
||||
{ enableHighAccuracy: true, timeout: 12000 }
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
if (!coords) {
|
||||
showNativeToast('⚠️ No se pudo obtener la señal GPS');
|
||||
return null;
|
||||
}
|
||||
|
||||
if (buttonEl) buttonEl.innerHTML = '🔍 Identificando dirección...';
|
||||
|
||||
// Geocodificación inversa satelital a través del backend
|
||||
let address = `Ubicación GPS (${coords.lat.toFixed(5)}, ${coords.lng.toFixed(5)})`;
|
||||
let city = (coords.lat > 3.6 && coords.lng < -76.8) ? 'Buenaventura' : 'Cali';
|
||||
|
||||
try {
|
||||
const res = await fetch(`/api/geo/reverse?lat=${coords.lat}&lng=${coords.lng}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
if (data.success && data.address) {
|
||||
address = data.address;
|
||||
city = data.city || city;
|
||||
}
|
||||
}
|
||||
} catch (geoErr) {
|
||||
console.warn('⚠️ [Reverse geocode notice]:', geoErr);
|
||||
}
|
||||
|
||||
triggerHaptic('success');
|
||||
return {
|
||||
lat: coords.lat,
|
||||
lng: coords.lng,
|
||||
address,
|
||||
city
|
||||
};
|
||||
} catch (err) {
|
||||
console.warn('[GPS Location Error]:', err);
|
||||
showNativeToast('⚠️ No se pudo obtener la ubicación actual');
|
||||
triggerHaptic('warning');
|
||||
return null;
|
||||
} finally {
|
||||
if (buttonEl) {
|
||||
buttonEl.disabled = false;
|
||||
buttonEl.innerHTML = originalText;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Geolocalización GPS Nativa de Alta Precisión para Pasajeros
|
||||
function initNativeGPS() {
|
||||
const btnGps = document.getElementById('btn-native-gps-cali');
|
||||
if (!btnGps) return;
|
||||
|
||||
btnGps.addEventListener('click', async () => {
|
||||
const loc = await getCurrentUserLocation(btnGps);
|
||||
if (loc) {
|
||||
window.passengerGpsCoords = [loc.lng, loc.lat];
|
||||
|
||||
// Centrar mapa de radar y posicionar marcador del pasajero
|
||||
if (leafletMap && typeof L !== 'undefined') {
|
||||
leafletMap.setView([loc.lat, loc.lng], 16);
|
||||
const userIcon = L.divIcon({
|
||||
className: 'user-live-gps-pin',
|
||||
html: '<div class="pulse-user-dot">📍</div>',
|
||||
iconSize: [36, 36],
|
||||
iconAnchor: [18, 18]
|
||||
});
|
||||
L.marker([loc.lat, loc.lng], { icon: userIcon })
|
||||
.addTo(leafletMap)
|
||||
.bindPopup(`<b>📍 Tu Ubicación Actual</b><br>${loc.address}`)
|
||||
.openPopup();
|
||||
}
|
||||
|
||||
if (coords) {
|
||||
// Centrar mapa de radar y posicionar marcador del pasajero
|
||||
if (leafletMap && typeof L !== 'undefined') {
|
||||
leafletMap.setView([coords.lat, coords.lng], 16);
|
||||
const userIcon = L.divIcon({
|
||||
className: 'user-live-gps-pin',
|
||||
html: '<div class="pulse-user-dot">📍</div>',
|
||||
iconSize: [36, 36],
|
||||
iconAnchor: [18, 18]
|
||||
});
|
||||
L.marker([coords.lat, coords.lng], { icon: userIcon })
|
||||
.addTo(leafletMap)
|
||||
.bindPopup('<b>📍 Tu Ubicación Actual</b><br>Coordenadas GPS de alta precisión detectadas.')
|
||||
.openPopup();
|
||||
}
|
||||
|
||||
const addrInput = document.getElementById('cali-address');
|
||||
if (addrInput) {
|
||||
addrInput.value = `📍 GPS (${coords.lat.toFixed(5)}, ${coords.lng.toFixed(5)})`;
|
||||
}
|
||||
showNativeToast('✅ Ubicación GPS detectada con éxito');
|
||||
triggerHaptic('success');
|
||||
const addrInput = document.getElementById('cali-address');
|
||||
if (addrInput) {
|
||||
addrInput.value = loc.address;
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('[GPS Error]:', err);
|
||||
showNativeToast('No se pudo obtener el GPS actual');
|
||||
triggerHaptic('warning');
|
||||
} finally {
|
||||
btnGps.disabled = false;
|
||||
btnGps.innerHTML = originalText;
|
||||
showNativeToast(`📍 Dirección detectada: ${loc.address}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -2160,8 +2209,22 @@ function initPassengerCalculator() {
|
||||
function updateOriginDestination() {
|
||||
if (!originSelect) return;
|
||||
const isCali = originSelect.value === 'CALI';
|
||||
if (addressGroup) addressGroup.style.display = isCali ? 'block' : 'none';
|
||||
if (buenaStopGroup) buenaStopGroup.style.display = isCali ? 'block' : 'none';
|
||||
const caliAddressLabel = document.querySelector('label[for="cali-address"]');
|
||||
const buenaStopLabel = document.querySelector('label[for="buena-stop"]');
|
||||
|
||||
if (addressGroup) addressGroup.style.display = 'block';
|
||||
if (buenaStopGroup) buenaStopGroup.style.display = 'block';
|
||||
|
||||
if (caliAddressLabel) {
|
||||
caliAddressLabel.textContent = isCali
|
||||
? '📍 Dirección Principal de Recogida en Cali (Domicilio Puerta a Puerta)'
|
||||
: '🏁 Dirección Principal de Llegada en Cali (Entrega en Domicilio)';
|
||||
}
|
||||
if (buenaStopLabel) {
|
||||
buenaStopLabel.textContent = isCali
|
||||
? '🏁 Parada de Destino en Buenaventura'
|
||||
: '📍 Punto Oficial de Salida en Buenaventura';
|
||||
}
|
||||
}
|
||||
|
||||
if (originSelect) {
|
||||
@ -2232,14 +2295,18 @@ function initPassengerCalculator() {
|
||||
|
||||
const pAddress = (!d.sameAddress && d.address && d.address.trim())
|
||||
? d.address.trim()
|
||||
: (caliAddress || 'Punto Oficial Buenaventura');
|
||||
: (caliAddress || (isCaliOrigin ? 'Cali, Valle del Cauca' : 'Buenaventura, Valle del Cauca'));
|
||||
|
||||
const pCoords = (i === 1 && window.passengerGpsCoords)
|
||||
? window.passengerGpsCoords
|
||||
: (isCaliOrigin ? [-76.5320, 3.4215] : [-77.0250, 3.8820]);
|
||||
|
||||
validatedPassengers.push({
|
||||
nombre: d.nombre.trim(),
|
||||
documentoIdentidad: d.doc.trim(),
|
||||
telefono: d.phone.trim(),
|
||||
direccionRecogida: pAddress,
|
||||
coordenadas: [-76.5320, 3.4215],
|
||||
coordenadas: pCoords,
|
||||
notas: d.notes ? d.notes.trim() : '',
|
||||
esTitular: (i === 1)
|
||||
});
|
||||
@ -2268,11 +2335,15 @@ function initPassengerCalculator() {
|
||||
`;
|
||||
|
||||
try {
|
||||
const userTripCoords = (window.passengerGpsCoords && window.passengerGpsCoords.length === 2)
|
||||
? window.passengerGpsCoords
|
||||
: (isCaliOrigin ? [-76.5320, 3.4215] : [-77.0250, 3.8820]);
|
||||
|
||||
const payload = {
|
||||
usuarioId: currentUser ? currentUser._id : undefined,
|
||||
sentido: isCaliOrigin ? 'CALI_A_BUENAVENTURA' : 'BUENAVENTURA_A_CALI',
|
||||
direccionTexto: caliAddress || 'Terminal Buenaventura',
|
||||
coordenadas: [-76.5320, 3.4215],
|
||||
direccionTexto: caliAddress || (isCaliOrigin ? 'Cali, Valle del Cauca' : 'Buenaventura, Valle del Cauca'),
|
||||
coordenadas: userTripCoords,
|
||||
puntoFijoBuenaventura: {
|
||||
nombre: buenaStopSelect ? buenaStopSelect.options[buenaStopSelect.selectedIndex].text : 'Terminal de Transporte de Buenaventura',
|
||||
coordenadas: [-77.0250, 3.8820]
|
||||
@ -2694,6 +2765,34 @@ function initFreightCalculator() {
|
||||
});
|
||||
}
|
||||
|
||||
// Botones GPS para Recogida y Entrega de Encomiendas
|
||||
const btnFreightGpsPickup = document.getElementById('btn-freight-gps-pickup');
|
||||
const btnFreightGpsDelivery = document.getElementById('btn-freight-gps-delivery');
|
||||
const freightPickupInput = document.getElementById('freight-pickup-address');
|
||||
const freightDeliveryInput = document.getElementById('freight-delivery-address');
|
||||
|
||||
if (btnFreightGpsPickup) {
|
||||
btnFreightGpsPickup.addEventListener('click', async () => {
|
||||
const loc = await getCurrentUserLocation(btnFreightGpsPickup);
|
||||
if (loc) {
|
||||
window.freightPickupCoords = [loc.lng, loc.lat];
|
||||
if (freightPickupInput) freightPickupInput.value = loc.address;
|
||||
showNativeToast(`📍 Recogida establecida: ${loc.address}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (btnFreightGpsDelivery) {
|
||||
btnFreightGpsDelivery.addEventListener('click', async () => {
|
||||
const loc = await getCurrentUserLocation(btnFreightGpsDelivery);
|
||||
if (loc) {
|
||||
window.freightDeliveryCoords = [loc.lng, loc.lat];
|
||||
if (freightDeliveryInput) freightDeliveryInput.value = loc.address;
|
||||
showNativeToast(`🏁 Entrega establecida: ${loc.address}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Despacho / Creación de Encomienda con Pago Seguro y Cupón
|
||||
if (btnBookFreight) {
|
||||
btnBookFreight.addEventListener('click', async (e) => {
|
||||
@ -2702,6 +2801,8 @@ function initFreightCalculator() {
|
||||
const senderPhone = document.getElementById('sender-phone')?.value.trim() || currentUser?.telefono || '';
|
||||
const receiverPhone = document.getElementById('receiver-phone')?.value.trim();
|
||||
const packageDesc = document.getElementById('package-desc')?.value.trim();
|
||||
const freightPickupAddr = freightPickupInput?.value.trim() || 'Cali, Valle del Cauca';
|
||||
const freightDeliveryAddr = freightDeliveryInput?.value.trim() || 'Terminal Buenaventura';
|
||||
|
||||
if (!senderPhone || senderPhone.replace(/\D/g, '').length < 7) {
|
||||
alert('⚠️ Por favor ingresa el teléfono/WhatsApp del remitente.');
|
||||
@ -2743,6 +2844,12 @@ function initFreightCalculator() {
|
||||
remitenteId: currentUser ? (currentUser._id || currentUser.id) : undefined,
|
||||
remitenteNombre: senderName || 'Remitente',
|
||||
remitenteTelefono: senderPhone,
|
||||
direccionRecogida: freightPickupAddr,
|
||||
coordenadasRecogida: window.freightPickupCoords || [-76.5320, 3.4215],
|
||||
ciudadOrigen: (window.freightPickupCoords && window.freightPickupCoords[1] > 3.6 && window.freightPickupCoords[0] < -76.8) ? 'BUENAVENTURA' : 'CALI',
|
||||
direccionEntrega: freightDeliveryAddr,
|
||||
coordenadasEntrega: window.freightDeliveryCoords || [-77.0250, 3.8820],
|
||||
ciudadDestino: (window.freightDeliveryCoords && window.freightDeliveryCoords[1] < 3.6 && window.freightDeliveryCoords[0] > -76.8) ? 'CALI' : 'BUENAVENTURA',
|
||||
tipoPaquete: packageTypeSelect ? packageTypeSelect.options[packageTypeSelect.selectedIndex].text : 'Paquete Pequeño',
|
||||
factor: parseFloat(packageTypeSelect?.value || '1.2'),
|
||||
valorDeclarado: parseFloat(declaredValueInput?.value || '100000'),
|
||||
|
||||
48
scripts/verify_gps_geocoding.py
Normal file
48
scripts/verify_gps_geocoding.py
Normal file
@ -0,0 +1,48 @@
|
||||
import io
|
||||
import sys
|
||||
import urllib.request
|
||||
import json
|
||||
import ssl
|
||||
|
||||
if sys.platform == 'win32':
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
|
||||
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace')
|
||||
|
||||
ctx = ssl.create_default_context()
|
||||
ctx.check_hostname = False
|
||||
ctx.verify_mode = ssl.CERT_NONE
|
||||
|
||||
API_URL = "https://pacifictrans.almeira.com.co"
|
||||
|
||||
print("1. Probando geocodificacion inversa satelital en Cali...")
|
||||
req_cali = urllib.request.Request(f"{API_URL}/api/geo/reverse?lat=3.4215&lng=-76.5320")
|
||||
try:
|
||||
with urllib.request.urlopen(req_cali, context=ctx, timeout=10) as res:
|
||||
data = json.loads(res.read().decode("utf-8"))
|
||||
print(f" [OK] Cali geocodificado: {data.get('address')} | Ciudad: {data.get('city')}")
|
||||
assert data.get('success') is True
|
||||
except Exception as e:
|
||||
print(" Aviso / Error:", e)
|
||||
|
||||
print("\n2. Probando geocodificacion inversa satelital en Buenaventura...")
|
||||
req_bv = urllib.request.Request(f"{API_URL}/api/geo/reverse?lat=3.8820&lng=-77.0250")
|
||||
try:
|
||||
with urllib.request.urlopen(req_bv, context=ctx, timeout=10) as res:
|
||||
data = json.loads(res.read().decode("utf-8"))
|
||||
print(f" [OK] Buenaventura geocodificado: {data.get('address')} | Ciudad: {data.get('city')}")
|
||||
assert data.get('success') is True
|
||||
except Exception as e:
|
||||
print(" Aviso / Error:", e)
|
||||
|
||||
print("\n3. Verificando campos de direccion y botones GPS en index.html...")
|
||||
req_html = urllib.request.Request(f"{API_URL}/")
|
||||
with urllib.request.urlopen(req_html, context=ctx, timeout=10) as res:
|
||||
html = res.read().decode("utf-8")
|
||||
assert "freight-pickup-address" in html, "Falta freight-pickup-address"
|
||||
assert "freight-delivery-address" in html, "Falta freight-delivery-address"
|
||||
assert "btn-freight-gps-pickup" in html, "Falta btn-freight-gps-pickup"
|
||||
assert "btn-freight-gps-delivery" in html, "Falta btn-freight-gps-delivery"
|
||||
assert "btn-native-gps-cali" in html, "Falta btn-native-gps-cali"
|
||||
print(" [OK] Todos los campos de direccion y botones GPS de ubicacion actual estan activos en la interfaz.")
|
||||
|
||||
print("\n[OK] Verificacion de direccion por ubicacion actual completada exitosamente.")
|
||||
@ -49,7 +49,7 @@
|
||||
<a href="#reservas" id="nav-link-reservas" class="active">🚗 Viajar</a>
|
||||
<a href="#fletes" id="nav-link-fletes">📦 Envíos</a>
|
||||
<a href="#estadisticas" id="nav-link-reviews">⭐ Reseñas</a>
|
||||
<a href="#descarga-apk" id="nav-link-app">📲 App Móvil</a>
|
||||
<a href="/PacifictransGo.apk" download="PacifictransGo.apk" id="nav-link-app" style="background: rgba(16, 185, 129, 0.15); color: #34d399; border: 1px solid rgba(16, 185, 129, 0.35); border-radius: 999px; padding: 0.35rem 0.85rem; font-weight: 600; text-decoration: none; display: inline-flex; align-items: center; gap: 0.35rem;">📲 Descargar App</a>
|
||||
|
||||
<!-- Botón de apertura de Dashboard Administrativo (visible solo para Administrador) -->
|
||||
<button type="button" class="btn-open-dashboard-header" id="nav-btn-open-admin" style="display: none;">
|
||||
@ -521,6 +521,27 @@
|
||||
<input type="text" class="form-input" id="package-desc" placeholder="Repuestos automotrices / Ropa / Medicamentos">
|
||||
</div>
|
||||
|
||||
<!-- DIRECCIONES DE RECOGIDA Y ENTREGA CON UBICACIÓN GPS -->
|
||||
<div class="form-group">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.35rem;">
|
||||
<label class="form-label" for="freight-pickup-address" style="margin-bottom: 0;">📍 Dirección de Recogida del Paquete (Origen)</label>
|
||||
<button type="button" id="btn-freight-gps-pickup" class="btn-gps-chip" title="Detectar mi ubicación actual para la recogida del paquete">
|
||||
📍 Mi Ubicación Actual
|
||||
</button>
|
||||
</div>
|
||||
<input type="text" class="form-input" id="freight-pickup-address" placeholder="Ej: Calle 5 #66-20, Cali (o pulsa 'Mi Ubicación Actual')">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.35rem;">
|
||||
<label class="form-label" for="freight-delivery-address" style="margin-bottom: 0;">🏁 Dirección de Entrega del Paquete (Destino)</label>
|
||||
<button type="button" id="btn-freight-gps-delivery" class="btn-gps-chip" title="Detectar mi ubicación actual para la entrega del paquete">
|
||||
📍 Mi Ubicación Actual
|
||||
</button>
|
||||
</div>
|
||||
<input type="text" class="form-input" id="freight-delivery-address" placeholder="Ej: Terminal Buenaventura, Muelle o Dirección exacta">
|
||||
</div>
|
||||
|
||||
<!-- MODALIDAD DE DESPACHO DE ENCOMIENDA: URGENTE INMEDIATO VS PROGRAMADO -->
|
||||
<div class="time-modality-container" id="freight-time-modality-container">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.5rem;">
|
||||
|
||||
@ -147,76 +147,125 @@ function initNativeBackButton() {
|
||||
});
|
||||
}
|
||||
|
||||
// 3. Geolocalización GPS Nativa de Alta Precisión
|
||||
function initNativeGPS() {
|
||||
const btnGps = document.getElementById('btn-native-gps-cali');
|
||||
if (!btnGps) return;
|
||||
// Variables globales de coordenadas GPS detectadas
|
||||
window.passengerGpsCoords = [-76.5320, 3.4215];
|
||||
window.freightPickupCoords = [-76.5320, 3.4215];
|
||||
window.freightDeliveryCoords = [-77.0250, 3.8820];
|
||||
|
||||
btnGps.addEventListener('click', async () => {
|
||||
// Helper Centralizado: Obtiene coordenadas GPS y geocodifica a dirección humana real
|
||||
async function getCurrentUserLocation(buttonEl = null) {
|
||||
let originalText = '';
|
||||
if (buttonEl) {
|
||||
triggerHaptic('medium');
|
||||
btnGps.disabled = true;
|
||||
const originalText = btnGps.innerHTML;
|
||||
btnGps.innerHTML = '⏳ Obteniendo GPS...';
|
||||
originalText = buttonEl.innerHTML;
|
||||
buttonEl.disabled = true;
|
||||
buttonEl.innerHTML = '⏳ Obteniendo GPS...';
|
||||
}
|
||||
|
||||
try {
|
||||
let coords = null;
|
||||
if (window.Capacitor?.isPluginAvailable?.('Geolocation')) {
|
||||
const { Geolocation } = window.Capacitor.Plugins;
|
||||
try {
|
||||
let coords = null;
|
||||
if (window.Capacitor?.isPluginAvailable?.('Geolocation')) {
|
||||
const { Geolocation } = window.Capacitor.Plugins;
|
||||
try {
|
||||
const check = await Geolocation.checkPermissions();
|
||||
if (check.location !== 'granted') {
|
||||
const req = await Geolocation.requestPermissions();
|
||||
if (req.location !== 'granted') {
|
||||
showNativeToast('⚠️ Permiso de ubicación requerido para GPS');
|
||||
btnGps.disabled = false;
|
||||
btnGps.innerHTML = originalText;
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
const pos = await Geolocation.getCurrentPosition({
|
||||
enableHighAccuracy: true,
|
||||
timeout: 12000
|
||||
});
|
||||
coords = { lat: pos.coords.latitude, lng: pos.coords.longitude };
|
||||
} else if (navigator.geolocation) {
|
||||
coords = await new Promise((resolve, reject) => {
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
p => resolve({ lat: p.coords.latitude, lng: p.coords.longitude }),
|
||||
err => reject(err),
|
||||
{ enableHighAccuracy: true, timeout: 12000 }
|
||||
);
|
||||
} catch (pErr) { }
|
||||
|
||||
const pos = await Geolocation.getCurrentPosition({
|
||||
enableHighAccuracy: true,
|
||||
timeout: 12000
|
||||
});
|
||||
coords = { lat: pos.coords.latitude, lng: pos.coords.longitude };
|
||||
} else if (navigator.geolocation) {
|
||||
coords = await new Promise((resolve, reject) => {
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
p => resolve({ lat: p.coords.latitude, lng: p.coords.longitude }),
|
||||
err => reject(err),
|
||||
{ enableHighAccuracy: true, timeout: 12000 }
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
if (!coords) {
|
||||
showNativeToast('⚠️ No se pudo obtener la señal GPS');
|
||||
return null;
|
||||
}
|
||||
|
||||
if (buttonEl) buttonEl.innerHTML = '🔍 Identificando dirección...';
|
||||
|
||||
// Geocodificación inversa satelital a través del backend
|
||||
let address = `Ubicación GPS (${coords.lat.toFixed(5)}, ${coords.lng.toFixed(5)})`;
|
||||
let city = (coords.lat > 3.6 && coords.lng < -76.8) ? 'Buenaventura' : 'Cali';
|
||||
|
||||
try {
|
||||
const res = await fetch(`/api/geo/reverse?lat=${coords.lat}&lng=${coords.lng}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
if (data.success && data.address) {
|
||||
address = data.address;
|
||||
city = data.city || city;
|
||||
}
|
||||
}
|
||||
} catch (geoErr) {
|
||||
console.warn('⚠️ [Reverse geocode notice]:', geoErr);
|
||||
}
|
||||
|
||||
triggerHaptic('success');
|
||||
return {
|
||||
lat: coords.lat,
|
||||
lng: coords.lng,
|
||||
address,
|
||||
city
|
||||
};
|
||||
} catch (err) {
|
||||
console.warn('[GPS Location Error]:', err);
|
||||
showNativeToast('⚠️ No se pudo obtener la ubicación actual');
|
||||
triggerHaptic('warning');
|
||||
return null;
|
||||
} finally {
|
||||
if (buttonEl) {
|
||||
buttonEl.disabled = false;
|
||||
buttonEl.innerHTML = originalText;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Geolocalización GPS Nativa de Alta Precisión para Pasajeros
|
||||
function initNativeGPS() {
|
||||
const btnGps = document.getElementById('btn-native-gps-cali');
|
||||
if (!btnGps) return;
|
||||
|
||||
btnGps.addEventListener('click', async () => {
|
||||
const loc = await getCurrentUserLocation(btnGps);
|
||||
if (loc) {
|
||||
window.passengerGpsCoords = [loc.lng, loc.lat];
|
||||
|
||||
// Centrar mapa de radar y posicionar marcador del pasajero
|
||||
if (leafletMap && typeof L !== 'undefined') {
|
||||
leafletMap.setView([loc.lat, loc.lng], 16);
|
||||
const userIcon = L.divIcon({
|
||||
className: 'user-live-gps-pin',
|
||||
html: '<div class="pulse-user-dot">📍</div>',
|
||||
iconSize: [36, 36],
|
||||
iconAnchor: [18, 18]
|
||||
});
|
||||
L.marker([loc.lat, loc.lng], { icon: userIcon })
|
||||
.addTo(leafletMap)
|
||||
.bindPopup(`<b>📍 Tu Ubicación Actual</b><br>${loc.address}`)
|
||||
.openPopup();
|
||||
}
|
||||
|
||||
if (coords) {
|
||||
// Centrar mapa de radar y posicionar marcador del pasajero
|
||||
if (leafletMap && typeof L !== 'undefined') {
|
||||
leafletMap.setView([coords.lat, coords.lng], 16);
|
||||
const userIcon = L.divIcon({
|
||||
className: 'user-live-gps-pin',
|
||||
html: '<div class="pulse-user-dot">📍</div>',
|
||||
iconSize: [36, 36],
|
||||
iconAnchor: [18, 18]
|
||||
});
|
||||
L.marker([coords.lat, coords.lng], { icon: userIcon })
|
||||
.addTo(leafletMap)
|
||||
.bindPopup('<b>📍 Tu Ubicación Actual</b><br>Coordenadas GPS de alta precisión detectadas.')
|
||||
.openPopup();
|
||||
}
|
||||
|
||||
const addrInput = document.getElementById('cali-address');
|
||||
if (addrInput) {
|
||||
addrInput.value = `📍 GPS (${coords.lat.toFixed(5)}, ${coords.lng.toFixed(5)})`;
|
||||
}
|
||||
showNativeToast('✅ Ubicación GPS detectada con éxito');
|
||||
triggerHaptic('success');
|
||||
const addrInput = document.getElementById('cali-address');
|
||||
if (addrInput) {
|
||||
addrInput.value = loc.address;
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('[GPS Error]:', err);
|
||||
showNativeToast('No se pudo obtener el GPS actual');
|
||||
triggerHaptic('warning');
|
||||
} finally {
|
||||
btnGps.disabled = false;
|
||||
btnGps.innerHTML = originalText;
|
||||
showNativeToast(`📍 Dirección detectada: ${loc.address}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -2160,8 +2209,22 @@ function initPassengerCalculator() {
|
||||
function updateOriginDestination() {
|
||||
if (!originSelect) return;
|
||||
const isCali = originSelect.value === 'CALI';
|
||||
if (addressGroup) addressGroup.style.display = isCali ? 'block' : 'none';
|
||||
if (buenaStopGroup) buenaStopGroup.style.display = isCali ? 'block' : 'none';
|
||||
const caliAddressLabel = document.querySelector('label[for="cali-address"]');
|
||||
const buenaStopLabel = document.querySelector('label[for="buena-stop"]');
|
||||
|
||||
if (addressGroup) addressGroup.style.display = 'block';
|
||||
if (buenaStopGroup) buenaStopGroup.style.display = 'block';
|
||||
|
||||
if (caliAddressLabel) {
|
||||
caliAddressLabel.textContent = isCali
|
||||
? '📍 Dirección Principal de Recogida en Cali (Domicilio Puerta a Puerta)'
|
||||
: '🏁 Dirección Principal de Llegada en Cali (Entrega en Domicilio)';
|
||||
}
|
||||
if (buenaStopLabel) {
|
||||
buenaStopLabel.textContent = isCali
|
||||
? '🏁 Parada de Destino en Buenaventura'
|
||||
: '📍 Punto Oficial de Salida en Buenaventura';
|
||||
}
|
||||
}
|
||||
|
||||
if (originSelect) {
|
||||
@ -2232,14 +2295,18 @@ function initPassengerCalculator() {
|
||||
|
||||
const pAddress = (!d.sameAddress && d.address && d.address.trim())
|
||||
? d.address.trim()
|
||||
: (caliAddress || 'Punto Oficial Buenaventura');
|
||||
: (caliAddress || (isCaliOrigin ? 'Cali, Valle del Cauca' : 'Buenaventura, Valle del Cauca'));
|
||||
|
||||
const pCoords = (i === 1 && window.passengerGpsCoords)
|
||||
? window.passengerGpsCoords
|
||||
: (isCaliOrigin ? [-76.5320, 3.4215] : [-77.0250, 3.8820]);
|
||||
|
||||
validatedPassengers.push({
|
||||
nombre: d.nombre.trim(),
|
||||
documentoIdentidad: d.doc.trim(),
|
||||
telefono: d.phone.trim(),
|
||||
direccionRecogida: pAddress,
|
||||
coordenadas: [-76.5320, 3.4215],
|
||||
coordenadas: pCoords,
|
||||
notas: d.notes ? d.notes.trim() : '',
|
||||
esTitular: (i === 1)
|
||||
});
|
||||
@ -2268,11 +2335,15 @@ function initPassengerCalculator() {
|
||||
`;
|
||||
|
||||
try {
|
||||
const userTripCoords = (window.passengerGpsCoords && window.passengerGpsCoords.length === 2)
|
||||
? window.passengerGpsCoords
|
||||
: (isCaliOrigin ? [-76.5320, 3.4215] : [-77.0250, 3.8820]);
|
||||
|
||||
const payload = {
|
||||
usuarioId: currentUser ? currentUser._id : undefined,
|
||||
sentido: isCaliOrigin ? 'CALI_A_BUENAVENTURA' : 'BUENAVENTURA_A_CALI',
|
||||
direccionTexto: caliAddress || 'Terminal Buenaventura',
|
||||
coordenadas: [-76.5320, 3.4215],
|
||||
direccionTexto: caliAddress || (isCaliOrigin ? 'Cali, Valle del Cauca' : 'Buenaventura, Valle del Cauca'),
|
||||
coordenadas: userTripCoords,
|
||||
puntoFijoBuenaventura: {
|
||||
nombre: buenaStopSelect ? buenaStopSelect.options[buenaStopSelect.selectedIndex].text : 'Terminal de Transporte de Buenaventura',
|
||||
coordenadas: [-77.0250, 3.8820]
|
||||
@ -2694,6 +2765,34 @@ function initFreightCalculator() {
|
||||
});
|
||||
}
|
||||
|
||||
// Botones GPS para Recogida y Entrega de Encomiendas
|
||||
const btnFreightGpsPickup = document.getElementById('btn-freight-gps-pickup');
|
||||
const btnFreightGpsDelivery = document.getElementById('btn-freight-gps-delivery');
|
||||
const freightPickupInput = document.getElementById('freight-pickup-address');
|
||||
const freightDeliveryInput = document.getElementById('freight-delivery-address');
|
||||
|
||||
if (btnFreightGpsPickup) {
|
||||
btnFreightGpsPickup.addEventListener('click', async () => {
|
||||
const loc = await getCurrentUserLocation(btnFreightGpsPickup);
|
||||
if (loc) {
|
||||
window.freightPickupCoords = [loc.lng, loc.lat];
|
||||
if (freightPickupInput) freightPickupInput.value = loc.address;
|
||||
showNativeToast(`📍 Recogida establecida: ${loc.address}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (btnFreightGpsDelivery) {
|
||||
btnFreightGpsDelivery.addEventListener('click', async () => {
|
||||
const loc = await getCurrentUserLocation(btnFreightGpsDelivery);
|
||||
if (loc) {
|
||||
window.freightDeliveryCoords = [loc.lng, loc.lat];
|
||||
if (freightDeliveryInput) freightDeliveryInput.value = loc.address;
|
||||
showNativeToast(`🏁 Entrega establecida: ${loc.address}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Despacho / Creación de Encomienda con Pago Seguro y Cupón
|
||||
if (btnBookFreight) {
|
||||
btnBookFreight.addEventListener('click', async (e) => {
|
||||
@ -2702,6 +2801,8 @@ function initFreightCalculator() {
|
||||
const senderPhone = document.getElementById('sender-phone')?.value.trim() || currentUser?.telefono || '';
|
||||
const receiverPhone = document.getElementById('receiver-phone')?.value.trim();
|
||||
const packageDesc = document.getElementById('package-desc')?.value.trim();
|
||||
const freightPickupAddr = freightPickupInput?.value.trim() || 'Cali, Valle del Cauca';
|
||||
const freightDeliveryAddr = freightDeliveryInput?.value.trim() || 'Terminal Buenaventura';
|
||||
|
||||
if (!senderPhone || senderPhone.replace(/\D/g, '').length < 7) {
|
||||
alert('⚠️ Por favor ingresa el teléfono/WhatsApp del remitente.');
|
||||
@ -2743,6 +2844,12 @@ function initFreightCalculator() {
|
||||
remitenteId: currentUser ? (currentUser._id || currentUser.id) : undefined,
|
||||
remitenteNombre: senderName || 'Remitente',
|
||||
remitenteTelefono: senderPhone,
|
||||
direccionRecogida: freightPickupAddr,
|
||||
coordenadasRecogida: window.freightPickupCoords || [-76.5320, 3.4215],
|
||||
ciudadOrigen: (window.freightPickupCoords && window.freightPickupCoords[1] > 3.6 && window.freightPickupCoords[0] < -76.8) ? 'BUENAVENTURA' : 'CALI',
|
||||
direccionEntrega: freightDeliveryAddr,
|
||||
coordenadasEntrega: window.freightDeliveryCoords || [-77.0250, 3.8820],
|
||||
ciudadDestino: (window.freightDeliveryCoords && window.freightDeliveryCoords[1] < 3.6 && window.freightDeliveryCoords[0] > -76.8) ? 'CALI' : 'BUENAVENTURA',
|
||||
tipoPaquete: packageTypeSelect ? packageTypeSelect.options[packageTypeSelect.selectedIndex].text : 'Paquete Pequeño',
|
||||
factor: parseFloat(packageTypeSelect?.value || '1.2'),
|
||||
valorDeclarado: parseFloat(declaredValueInput?.value || '100000'),
|
||||
|
||||
@ -123,6 +123,65 @@ const server = http.createServer(async (req, res) => {
|
||||
return authService.verifyRole(token, allowedRoles);
|
||||
};
|
||||
|
||||
// 1.1. Geocodificación inversa satelital (Dirección por ubicación actual)
|
||||
if (pathname === '/api/geo/reverse' && req.method === 'GET') {
|
||||
const lat = parseFloat(url.searchParams.get('lat') || '');
|
||||
const lng = parseFloat(url.searchParams.get('lng') || url.searchParams.get('lon') || '');
|
||||
if (isNaN(lat) || isNaN(lng)) {
|
||||
return sendJson(400, { success: false, error: 'Coordenadas lat y lng inválidas' });
|
||||
}
|
||||
|
||||
try {
|
||||
const nominatimUrl = `https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=${lat}&lon=${lng}&zoom=18&addressdetails=1`;
|
||||
const fetchRes = await fetch(nominatimUrl, {
|
||||
headers: {
|
||||
'User-Agent': 'PacificTransGO/2.0 (admin@pacifictrans.co)',
|
||||
'Accept-Language': 'es',
|
||||
},
|
||||
});
|
||||
|
||||
if (fetchRes.ok) {
|
||||
const data = await fetchRes.json();
|
||||
const addr = data.address || {};
|
||||
|
||||
// Formatear dirección al estilo colombiano
|
||||
const parts: string[] = [];
|
||||
if (addr.road) {
|
||||
let roadPart = addr.road;
|
||||
if (addr.house_number) roadPart += ` #${addr.house_number}`;
|
||||
parts.push(roadPart);
|
||||
}
|
||||
if (addr.neighbourhood || addr.suburb) {
|
||||
parts.push(`Barrio ${addr.neighbourhood || addr.suburb}`);
|
||||
}
|
||||
const city = addr.city || addr.town || addr.municipality || (lat > 3.6 ? 'Buenaventura' : 'Cali');
|
||||
parts.push(city);
|
||||
if (addr.state) parts.push(addr.state);
|
||||
|
||||
const formattedAddress = parts.length > 0 ? parts.join(', ') : data.display_name;
|
||||
|
||||
return sendJson(200, {
|
||||
success: true,
|
||||
address: formattedAddress,
|
||||
city,
|
||||
coordinates: [lng, lat],
|
||||
raw: addr,
|
||||
});
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.warn('⚠️ [Nominatim Reverse Geocode Warning]:', err.message);
|
||||
}
|
||||
|
||||
// Fallback inteligente si la red satelital o Nominatim no responde
|
||||
const inferredCity = (lat > 3.7 && lng < -76.8) ? 'Buenaventura' : 'Cali';
|
||||
return sendJson(200, {
|
||||
success: true,
|
||||
address: `Ubicación GPS (${lat.toFixed(5)}, ${lng.toFixed(5)}), ${inferredCity}`,
|
||||
city: inferredCity,
|
||||
coordinates: [lng, lat],
|
||||
});
|
||||
}
|
||||
|
||||
// 2. Pooling inteligente (Transporte)
|
||||
if (pathname === '/api/trips/pool' && req.method === 'POST') {
|
||||
const body = await getJsonBody();
|
||||
@ -619,15 +678,38 @@ const server = http.createServer(async (req, res) => {
|
||||
};
|
||||
|
||||
// Descarga directa del APK Oficial Android
|
||||
if (pathname === '/PacifictransGo.apk' || pathname === '/download/apk') {
|
||||
if (
|
||||
pathname === '/PacifictransGo.apk' ||
|
||||
pathname === '/download/apk' ||
|
||||
pathname === '/apk' ||
|
||||
pathname === '/app' ||
|
||||
pathname === '/descarga-apk' ||
|
||||
pathname === '/descargar-apk'
|
||||
) {
|
||||
const localApk = path.join(PUBLIC_DIR, 'PacifictransGo.apk');
|
||||
const fallbackApk = path.resolve(__dirname, '../../../pacifictrans-app/public/PacifictransGo.apk');
|
||||
const vpsApk = '/home/carlos/apps/dripzone-app/public/PacifictransGo.apk';
|
||||
|
||||
const targetApk = fs.existsSync(localApk) ? localApk : (fs.existsSync(fallbackApk) ? fallbackApk : vpsApk);
|
||||
if (serveStaticFile(targetApk, MIME_TYPES['.apk'])) return;
|
||||
if (fs.existsSync(targetApk)) {
|
||||
try {
|
||||
const stat = fs.statSync(targetApk);
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'application/vnd.android.package-archive',
|
||||
'Content-Length': stat.size,
|
||||
'Content-Disposition': 'attachment; filename="PacifictransGo.apk"',
|
||||
'Cache-Control': 'no-cache',
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
});
|
||||
fs.createReadStream(targetApk).pipe(res);
|
||||
return;
|
||||
} catch (e) {
|
||||
console.error('Error serving APK:', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Archivo estático directo (HTML, CSS, JS, Imágenes)
|
||||
let relativePath = pathname === '/' ? 'index.html' : pathname.replace(/^\//, '');
|
||||
let targetPath = path.join(PUBLIC_DIR, relativePath);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user