diff --git a/lib/providers/cities_provider.dart b/lib/providers/cities_provider.dart index 607c5ff..9f0c924 100644 --- a/lib/providers/cities_provider.dart +++ b/lib/providers/cities_provider.dart @@ -18,6 +18,24 @@ class CitiesProvider extends ChangeNotifier { return null; } + // Busca una ciudad que coincida con el nombre geocodificado (comparación flexible) + City? findMatchingCity(String geocodedName) { + if (geocodedName.isEmpty || cities.isEmpty) return null; + final normalized = geocodedName.toLowerCase().trim(); + // Intento exacto primero + for (final c in cities) { + if (c.cityName.toLowerCase() == normalized) return c; + } + // Coincidencia parcial: el nombre geocodificado contiene el nombre de ciudad + for (final c in cities) { + final cn = c.cityName.toLowerCase(); + if (normalized.contains(cn) || cn.contains(normalized.split(',').first.trim())) return c; + } + return null; + } + + bool isCityAvailable(String geocodedName) => findMatchingCity(geocodedName) != null; + getCities() async { try { // GET /locations/countries returns nested {regions: [{cities: [...]}]} diff --git a/lib/ui/views/dashboard_view.dart b/lib/ui/views/dashboard_view.dart index f747b88..a1dc2bb 100644 --- a/lib/ui/views/dashboard_view.dart +++ b/lib/ui/views/dashboard_view.dart @@ -12,6 +12,7 @@ import 'package:prosapp_web_app/models/service_status.dart'; import 'package:prosapp_web_app/models/usuario.dart'; import 'package:prosapp_web_app/models/usuario_profesional.dart'; import 'package:prosapp_web_app/providers/auth_provider.dart'; +import 'package:prosapp_web_app/providers/cities_provider.dart'; import 'package:prosapp_web_app/router/router.dart'; import 'package:prosapp_web_app/services/api_service.dart'; import 'package:prosapp_web_app/services/maps_service.dart'; @@ -56,6 +57,10 @@ class _DashboardViewState extends State { // Throttle reverse geocode on camera idle DateTime _lastGeocode = DateTime(0); + // Ciudad detectada y disponibilidad + String _detectedCity = ''; + bool _cityAvailable = true; + @override void initState() { super.initState(); @@ -121,10 +126,18 @@ class _DashboardViewState extends State { final results = data['results'] as List?; if (results != null && results.isNotEmpty) { final address = results[0]['formatted_address'] as String; + final components = results[0]['address_components'] as List? ?? []; + final city = _extractCity(components); + + final citiesProvider = Provider.of(context, listen: false); + final available = city.isEmpty || citiesProvider.isCityAvailable(city); + if (mounted) { setState(() { _currentAddress = address; _searchController.text = address; + _detectedCity = city; + _cityAvailable = available; }); } } @@ -133,6 +146,16 @@ class _DashboardViewState extends State { if (mounted) setState(() => _geocoding = false); } + String _extractCity(List components) { + for (final c in components) { + final types = List.from(c['types'] ?? []); + if (types.contains('locality') || types.contains('administrative_area_level_2')) { + return c['long_name'] as String? ?? ''; + } + } + return ''; + } + Future _geocodeAndMoveMap(String address) async { if (_mapsApiKey == null || _mapsApiKey!.isEmpty) return; try { @@ -579,10 +602,48 @@ class _DashboardViewState extends State { const SizedBox(height: 12), + // Aviso ciudad no disponible + if (!_cityAvailable && _detectedCity.isNotEmpty) + Container( + width: double.infinity, + margin: const EdgeInsets.only(bottom: 10), + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12), + decoration: BoxDecoration( + color: const Color(0xFFFFF3CD), + borderRadius: BorderRadius.circular(10), + border: Border.all(color: const Color(0xFFFFC107)), + ), + child: Row( + children: [ + const Icon(Icons.location_off_outlined, color: Color(0xFF856404), size: 20), + const SizedBox(width: 10), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Servicio no disponible en tu ciudad', + style: TextStyle( + color: Color(0xFF856404), + fontWeight: FontWeight.w600, + fontSize: 13, + ), + ), + Text( + 'Actualmente no operamos en $_detectedCity. Mueve el mapa a una ciudad disponible.', + style: const TextStyle(color: Color(0xFF856404), fontSize: 11), + ), + ], + ), + ), + ], + ), + ), + SizedBox( width: double.infinity, child: ElevatedButton( - onPressed: _requesting ? null : _requestService, + onPressed: (_requesting || !_cityAvailable) ? null : _requestService, style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF42A4EF), foregroundColor: Colors.white,