import { useEffect, useRef, useState } from "react"; import { ETIQUETAS_PROVEEDOR, inferirDesdeCoords, resolverUbicacion } from "../utils/ubicacion/index.js"; import { inputStyle } from "../styles/shared.js"; const DEBOUNCE_MS = 600; // Proveedores cuya entrada es un enlace (se guarda como enlace del local) const PROVEEDORES_ENLACE = new Set(["google-maps", "waze", "osm"]); // Paso 1 de la pasarela: un único campo universal de ubicación que acepta // enlaces de Google Maps / Waze / OpenStreetMap, coordenadas sueltas o una // dirección en texto. Con debounce resuelve la entrada con el primer // proveedor que la reconozca y, si solo hay coordenadas, lanza la // geocodificación inversa para pre-rellenar nombre/provincia/dirección. export default function PasoUbicacion({ form, setForm }) { const [texto, setTexto] = useState(form.entradaUbicacion || ""); // fase: inactivo | resolviendo | resuelto | noreconocido const [estado, setEstado] = useState(() => form.lat != null && form.lng != null ? { fase: "resuelto", resolucion: { proveedor: form.proveedorUbicacion, lat: form.lat, lng: form.lng }, aviso: "" } : { fase: "inactivo", resolucion: null, aviso: "" }, ); const debounceRef = useRef(null); // Identificador de resolución en curso: descarta respuestas asíncronas // obsoletas si el usuario sigue escribiendo. const idResolucionRef = useRef(0); useEffect(() => () => clearTimeout(debounceRef.current), []); const resolver = async (entrada, id) => { if (id !== idResolucionRef.current) return; setEstado({ fase: "resolviendo", resolucion: null, aviso: "" }); let resultado = null; try { resultado = await resolverUbicacion(entrada); } catch { resultado = null; // fallo controlado: la pasarela nunca se rompe } if (id !== idResolucionRef.current) return; if (!resultado) { setEstado({ fase: "noreconocido", resolucion: null, aviso: "No se pudo interpretar la ubicación. Si pegaste un enlace acortado, copia la URL completa; si escribiste una dirección, sé más específico.", }); return; } const esEnlace = PROVEEDORES_ENLACE.has(resultado.proveedor); setEstado({ fase: "resuelto", resolucion: resultado, aviso: "" }); setForm((f) => ({ ...f, entradaUbicacion: entrada, proveedorUbicacion: resultado.proveedor, lat: resultado.lat, lng: resultado.lng, enlaceGoogleMaps: esEnlace ? entrada : "", direccion: resultado.direccion || "", nombre: resultado.nombre || "", provincia: resultado.provincia || "", })); // Inferencia por geocodificación inversa solo si el resultado no trajo ya // nombre y provincia (una sola llamada a Nominatim por resolución). if (!resultado.nombre || !resultado.provincia) { const inferido = await inferirDesdeCoords(resultado.lat, resultado.lng); if (id !== idResolucionRef.current) return; if (inferido) { setForm((f) => ({ ...f, nombre: inferido.nombre || f.nombre, provincia: inferido.provincia || f.provincia, direccion: f.direccion || inferido.direccion || "", })); } else { setEstado((e) => ({ ...e, aviso: "No se pudieron inferir el nombre y la provincia automáticamente (posible fallo de red). Podrás rellenarlos a mano en los pasos siguientes.", })); } } }; const handleCambio = (val) => { setTexto(val); clearTimeout(debounceRef.current); const id = ++idResolucionRef.current; const limpio = val.trim(); // En cuanto cambia el texto, la resolución anterior deja de ser válida: // se limpia el chip y las coordenadas del form hasta la nueva resolución setEstado({ fase: "inactivo", resolucion: null, aviso: "" }); setForm((f) => ({ ...f, entradaUbicacion: val, proveedorUbicacion: "", lat: null, lng: null })); if (!limpio) return; debounceRef.current = setTimeout(() => resolver(limpio, id), DEBOUNCE_MS); }; const { fase, resolucion, aviso } = estado; const etiqueta = resolucion ? ETIQUETAS_PROVEEDOR[resolucion.proveedor] || resolucion.proveedor : ""; return (
Pega el enlace del local, escribe sus coordenadas (p. ej. 41.3851, 2.1734) o su dirección.
{fase === "resolviendo" && (