feat(propuesta): pasarela paso a paso con autodetección de ubicación y FAB
- Campo universal de ubicación (Google Maps/Waze/OSM/coordenadas/dirección) sobre proveedores extensibles (src/utils/ubicacion) con resolver e inferencia inversa Nominatim (nombre/provincia pre-rellenados, alias cooficiales) - FAB flotante abajo a la derecha que abre la pasarela en un modal accesible (Escape, foco contenido, responsive); sin descripción ni puntuación; avisos de duplicado y filtro de palabras dentro del modal - Aviso PWA reposicionado: tarjeta abajo a la izquierda en móvil, botón compacto arriba a la izquierda en PC (no tapa el FAB) - Suite de pruebas: 88 unitarias/componente (vitest + RTL, offline) y 10 de integración (supertest + SQLite :memory: y Nominatim real opt-in) - CI: workflow de Gitea Actions con npm test + build en push/PR e integración manual
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
# Proveedores de ubicación (integraciones extensible)
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Interfaz común para proveedores de ubicación
|
||||
Cada integración con un backend de ubicación SHALL implementarse como una clase que cumpla una misma interfaz: identificarse (`id`), indicar si reconoce una entrada (`detecta(entrada)`) y extraer la información disponible (`extrae(entrada)`).
|
||||
|
||||
#### Scenario: Contrato uniforme
|
||||
- **WHEN** se consulta cualquier proveedor registrado
|
||||
- **THEN** expone los métodos `detecta(entrada)` y `extrae(entrada)` con la misma firma y semántica, de modo que el consumidor no necesita conocer la clase concreta
|
||||
|
||||
### Requirement: Resultado de extracción extensible
|
||||
El método `extrae(entrada)` SHALL devolver un objeto de resultado extensible que incluya como mínimo las coordenadas (`lat`, `lng`) y el identificador del proveedor que las extrajo, pudiendo incorporar campos adicionales en el futuro sin romper a los consumidores.
|
||||
|
||||
#### Scenario: Extracción mínima
|
||||
- **WHEN** un proveedor extrae una ubicación válida
|
||||
- **THEN** el resultado contiene `lat`, `lng` y el identificador del proveedor
|
||||
|
||||
#### Scenario: Campos adicionales sin ruptura
|
||||
- **WHEN** en el futuro un proveedor añade nuevos campos al resultado (horarios, teléfono, fotos…)
|
||||
- **THEN** los consumidores existentes siguen funcionando sin modificación, ignorando los campos que no conocen
|
||||
|
||||
### Requirement: Autodetección del proveedor a partir de la entrada
|
||||
El sistema SHALL autodetectar automáticamente el proveedor adecuado para una entrada dada, recorriendo los proveedores registrados y usando el primero que la reconozca.
|
||||
|
||||
#### Scenario: Enlace de Google Maps
|
||||
- **WHEN** el usuario pega un enlace de Google Maps (formatos `@lat,lng`, `!3d…!4d…` o `?q=lat,lng`)
|
||||
- **THEN** el sistema lo resuelve con el proveedor de Google Maps y obtiene las coordenadas
|
||||
|
||||
#### Scenario: Enlace de Waze
|
||||
- **WHEN** el usuario pega un enlace de Waze (`waze.com/ul`, parámetro `ll=lat,lng` o búsqueda `q=`)
|
||||
- **THEN** el sistema lo resuelve con el proveedor de Waze y obtiene las coordenadas
|
||||
|
||||
#### Scenario: Enlace de OpenStreetMap
|
||||
- **WHEN** el usuario pega un enlace de openstreetmap.org (parámetros `mlat`/`mlon` o fragmento `#map=zoom/lat/lng`)
|
||||
- **THEN** el sistema lo resuelve con el proveedor de OpenStreetMap y obtiene las coordenadas
|
||||
|
||||
#### Scenario: Coordenadas geográficas sueltas
|
||||
- **WHEN** el usuario escribe coordenadas directamente (por ejemplo `41.3851, 2.1734`)
|
||||
- **THEN** el sistema lo resuelve con el proveedor de coordenadas y obtiene lat/lng
|
||||
|
||||
#### Scenario: Texto de dirección como fallback
|
||||
- **WHEN** la entrada no coincide con ningún enlace soportado y parece una dirección en texto
|
||||
- **THEN** el sistema la resuelve con el proveedor de geocodificación (Nominatim) realizando una búsqueda por texto
|
||||
|
||||
#### Scenario: Entrada no reconocida
|
||||
- **WHEN** la entrada no es reconocida por ningún proveedor ni resuelta como dirección
|
||||
- **THEN** el sistema informa de que no se pudo interpretar la ubicación y no avanza de pantalla
|
||||
|
||||
### Requirement: Inferencia de nombre y provincia
|
||||
Los proveedores SHALL extraer, cuando estén disponibles, los campos inferibles `nombre` y `provincia` a partir de la entrada resuelta (geocodificación inversa cuando solo hay coordenadas), normalizando la provincia al listado canónico de provincias de España.
|
||||
|
||||
#### Scenario: Inferencia desde coordenadas
|
||||
- **WHEN** la entrada se resuelve a coordenadas sin más contexto
|
||||
- **THEN** una geocodificación inversa obtiene un nombre descriptivo y la provincia canónica (coincidencia insensible a acentos y mayúsculas con el listado de PROVINCIAS)
|
||||
|
||||
#### Scenario: Provincia fuera del listado
|
||||
- **WHEN** la provincia inferida no coincide con ninguna provincia del listado canónico
|
||||
- **THEN** el campo provincia queda vacío para que el usuario lo seleccione manualmente
|
||||
|
||||
### Requirement: Extensibilidad sin modificar proveedores existentes
|
||||
Añadir un proveedor nuevo SHALL consistir en crear una nueva clase que implemente la interfaz y registrarla en el resolver, sin modificar las clases existentes ni la lógica de la pasarela (principio abierto/cerrado).
|
||||
|
||||
#### Scenario: Registro de un proveedor nuevo
|
||||
- **WHEN** se añade una clase `MiProveedor` con `detecta`/`extrae` y se registra en la lista de proveedores
|
||||
- **THEN** el campo universal empieza a aceptar las entradas que ese proveedor reconoce sin ningún otro cambio de código
|
||||
|
||||
### Requirement: Errores de red degradados
|
||||
Los proveedores SHALL tratar sus errores de red o de servicio devolviendo un fallo controlado, sin romper la pasarela ni propagar excepciones al consumidor.
|
||||
|
||||
#### Scenario: Fallo del servicio de geocodificación
|
||||
- **WHEN** Nominatim no responde o devuelve un error
|
||||
- **THEN** la pantalla de Ubicación muestra un aviso de error y el usuario puede reintentar o continuar sin inferencia
|
||||
Reference in New Issue
Block a user