feat: geolocalización GPS con Haversine para detectar zona automáticamente

This commit is contained in:
2026-05-23 02:20:04 -06:00
parent 147b4ecb20
commit 9f0a0221fc

View File

@@ -2,6 +2,7 @@ import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View, TextInput, TouchableOpacity, ActivityIndicator, ScrollView, Alert, RefreshControl } from 'react-native';
import { useState, useEffect, useCallback } from 'react';
import AsyncStorage from '@react-native-async-storage/async-storage';
import * as Location from 'expo-location';
const API_URL = 'http://10.137.112.65:8000';
@@ -32,6 +33,7 @@ export default function App() {
completed: true,
});
const [alertaOperativa, setAlertaOperativa] = useState(null);
const [localizando, setLocalizando] = useState(false);
useEffect(() => {
cargarSesion();
@@ -260,6 +262,38 @@ export default function App() {
} catch {}
};
const usarUbicacion = async () => {
setLocalizando(true);
try {
const { status } = await Location.requestForegroundPermissionsAsync();
if (status !== 'granted') {
Alert.alert('Permiso denegado', 'Necesitamos acceso a tu ubicación para encontrar tu zona de recolección');
setLocalizando(false);
return;
}
const location = await Location.getCurrentPositionAsync({ accuracy: Location.Accuracy.High });
const { latitude, longitude } = location.coords;
const res = await fetch(
`${API_URL}/domicilios/ruta-por-coordenadas?lat=${latitude}&lng=${longitude}`,
{ headers: { 'Authorization': `Bearer ${token}` } }
);
const data = await res.json();
if (data.cobertura) {
setColoniaSeleccionada(data.colonia_sugerida);
Alert.alert(
'📍 Ubicación detectada',
`Se detectó tu zona: ${data.colonia_sugerida}\nRuta: ${data.nombre_ruta}\nDistancia: ${data.distancia_metros}m`
);
} else {
Alert.alert('Sin cobertura', 'Tu ubicación no está dentro de las zonas de servicio de Celaya');
}
} catch {
Alert.alert('Error', 'No se pudo obtener tu ubicación');
}
setLocalizando(false);
};
if (screen === 'splash') return (
<View style={styles.splashContainer}>
<Text style={styles.splashEmoji}>🚛</Text>
@@ -319,6 +353,15 @@ export default function App() {
value={direccion} onChangeText={setDireccion} />
<TextInput style={styles.input} placeholder="Código postal (ej: 38000)"
value={codigoPostal} onChangeText={setCodigoPostal} keyboardType="numeric" />
<TouchableOpacity
style={[styles.btnUbicacion, localizando && { opacity: 0.6 }]}
onPress={usarUbicacion}
disabled={localizando}>
{localizando
? <ActivityIndicator color="#1a7a4a" size="small" />
: <Text style={styles.btnUbicacionText}>📡 Usar mi ubicación actual</Text>
}
</TouchableOpacity>
<TouchableOpacity style={[styles.input, styles.combobox]}
onPress={() => setMostrarColonias(!mostrarColonias)}>
<Text style={{ color: coloniaSeleccionada ? '#1a1a1a' : '#999', fontSize: 15 }}>
@@ -553,4 +596,6 @@ const styles = StyleSheet.create({
alertaBox: { width: '100%', backgroundColor: '#fff3e0', borderRadius: 10, padding: 14, marginBottom: 16, borderWidth: 1, borderColor: '#ff9800' },
alertaTitulo: { fontSize: 14, fontWeight: 'bold', color: '#e65100', marginBottom: 4 },
alertaMensaje: { fontSize: 13, color: '#bf360c' },
btnUbicacion: { width: '100%', borderRadius: 10, padding: 14, alignItems: 'center', marginBottom: 12, borderWidth: 1.5, borderColor: '#1a7a4a', borderStyle: 'dashed', backgroundColor: '#e8f5ee' },
btnUbicacionText: { color: '#1a7a4a', fontWeight: '500', fontSize: 14 },
});