import 'package:flutter/material.dart'; import 'package:dio/dio.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:go_router/go_router.dart'; import 'package:latlong2/latlong.dart'; import '../../core/constants/auth_constants.dart'; import '../../core/theme/app_theme.dart'; import '../../core/models/ui_models.dart'; import 'colonias_data.dart'; import '../../core/widgets/app_widgets.dart'; class MyHouseScreen extends StatefulWidget { const MyHouseScreen({super.key}); @override State createState() => _MyHouseScreenState(); } class _MyHouseScreenState extends State { bool _isLoading = true; UIHouseModel? _casa; @override void initState() { super.initState(); _cargarDomicilio(); } Future _cargarDomicilio() async { try { const storage = FlutterSecureStorage(); final token = await storage.read(key: authTokenStorageKey) ?? ''; if (token.isEmpty) { setState(() => _isLoading = false); return; } final dio = Dio( BaseOptions( baseUrl: const String.fromEnvironment( 'API_BASE_URL', defaultValue: 'http://localhost:8000', ), headers: {'Authorization': 'Bearer $token'}, ), ); final res = await dio.get('/addresses'); if (res.data is List && (res.data as List).isNotEmpty) { final addr = res.data[0]; setState(() { _casa = UIHouseModel.fromJson(addr); _isLoading = false; }); } else { setState(() => _isLoading = false); } } catch (e) { setState(() => _isLoading = false); debugPrint('Error al cargar domicilio: $e'); } } @override Widget build(BuildContext context) { if (_isLoading) { return const Scaffold( backgroundColor: AppTheme.background, body: Center(child: CircularProgressIndicator()), ); } if (_casa == null) { return Scaffold( backgroundColor: AppTheme.background, body: Column( children: [ _buildPageHeader(context, showEdit: false), const Expanded( child: Center( child: Text( 'No tienes un domicilio registrado.', style: TextStyle(fontSize: 15, color: AppTheme.textSecondary), ), ), ), _buildAddressButton(context), ], ), ); } return Scaffold( backgroundColor: AppTheme.background, body: CustomScrollView( slivers: [ SliverToBoxAdapter(child: _buildPageHeader(context, showEdit: true)), SliverPadding( padding: const EdgeInsets.fromLTRB(24, 24, 24, 0), sliver: SliverList( delegate: SliverChildListDelegate([ const AppSectionTitle(title: 'Domicilio registrado'), _CasaCard(casa: _casa!), const SizedBox(height: 20), const AppSectionTitle(title: 'Mapa del Sector (Restringido)'), _MapaColoniaRestringido( colonia: _casa!.colonia, lat: _casa!.lat, lng: _casa!.lng, ), const SizedBox(height: 20), const AppSectionTitle(title: 'Radio de alerta'), _RadioAlertaCard( radioActual: _casa!.radioAlertaMetros, onChanged: (v) => setState( () => _casa = _casa!.copyWith(radioAlertaMetros: v), ), ), const SizedBox(height: 20), const AppSectionTitle(title: 'Notificaciones'), _NotificacionesCard( casa: _casa!, onAlertaCercanaChanged: (v) => setState(() => _casa = _casa!.copyWith(alertaCercana: v)), onAlertaMediaChanged: (v) => setState(() => _casa = _casa!.copyWith(alertaMedia: v)), onRecordatorioChanged: (v) => setState( () => _casa = _casa!.copyWith(recordatorioDiario: v), ), ), const SizedBox(height: 20), const AppSectionTitle(title: 'Horario del camión'), _HorarioCard(), const SizedBox(height: 24), ]), ), ), SliverToBoxAdapter(child: _buildAddressButton(context)), ], ), ); } Widget _buildPageHeader(BuildContext context, {required bool showEdit}) { return Container( padding: EdgeInsets.fromLTRB( 20, MediaQuery.of(context).padding.top + 12, 20, 24, ), decoration: const BoxDecoration( gradient: LinearGradient( colors: [Color(0xFF4A0E26), Color(0xFF9B1B4A)], begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: BorderRadius.only( bottomLeft: Radius.circular(28), bottomRight: Radius.circular(28), ), ), child: Row( children: [ Container( width: 44, height: 44, decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.15), borderRadius: BorderRadius.circular(12), ), child: const Icon( Icons.home_outlined, color: Colors.white, size: 24, ), ), const SizedBox(width: 14), const Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Mi Casa', style: TextStyle( fontSize: 20, fontWeight: FontWeight.w700, color: Colors.white, ), ), SizedBox(height: 2), Text( 'Domicilio registrado', style: TextStyle(fontSize: 13, color: Colors.white70), ), ], ), ), if (showEdit) GestureDetector( onTap: () => _mostrarEditarDireccion(context), child: Container( width: 36, height: 36, decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.15), borderRadius: BorderRadius.circular(10), ), child: const Icon( Icons.edit_outlined, color: Colors.white, size: 18, ), ), ), ], ), ); } Widget _buildAddressButton(BuildContext context) { return SafeArea( top: false, child: Padding( padding: const EdgeInsets.fromLTRB(24, 12, 24, 96), child: SizedBox( width: double.infinity, child: ElevatedButton.icon( onPressed: () async { final added = await context.push('/add-address'); if (added == true && mounted) { setState(() => _isLoading = true); _cargarDomicilio(); } }, icon: const Icon(Icons.add_home_outlined, size: 20), label: const Text('Agregar otra dirección'), ), ), ), ); } void _mostrarEditarDireccion(BuildContext context) { showModalBottomSheet( context: context, isScrollControlled: true, backgroundColor: AppTheme.surface, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical( top: Radius.circular(AppTheme.radiusXl), ), ), builder: (_) => _EditarDireccionSheet(casa: _casa!), ); } } // ── Tarjeta de la casa ──────────────────────────────────────────────────────── class _CasaCard extends StatelessWidget { final UIHouseModel casa; const _CasaCard({required this.casa}); @override Widget build(BuildContext context) { return Container( decoration: BoxDecoration( color: AppTheme.surface, borderRadius: BorderRadius.circular(AppTheme.radiusLg), border: Border.all(color: AppTheme.border, width: 0.5), boxShadow: AppTheme.softShadow, ), child: ClipRRect( borderRadius: BorderRadius.circular(AppTheme.radiusLg - 0.5), child: IntrinsicHeight( child: Row( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Container(width: 3, color: AppTheme.primary), Expanded( child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Container( width: 44, height: 44, decoration: BoxDecoration( color: AppTheme.primaryLight, borderRadius: BorderRadius.circular(12), ), child: const Icon( Icons.home_outlined, color: AppTheme.primary, size: 24, ), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( casa.alias, style: const TextStyle( fontSize: 15, fontWeight: FontWeight.w600, color: AppTheme.textPrimary, ), ), const SizedBox(height: 4), AppStatusBadge.green( casa.activa ? 'Activa' : 'Inactiva', ), ], ), ), ], ), const SizedBox(height: 14), const Divider(color: AppTheme.borderLight), const SizedBox(height: 10), _DetailRow( icon: Icons.location_on_outlined, text: casa.direccionCompleta, ), const SizedBox(height: 8), _DetailRow( icon: Icons.radar_outlined, text: 'Alerta a ${casa.radioAlertaMetros} m de distancia', ), ], ), ), ), ], ), ), ), ); } } // ── Mapa de Colonia (Restringido para Privacidad) ────────────────────────────── class _MapaColoniaRestringido extends StatelessWidget { final String colonia; final double? lat; final double? lng; const _MapaColoniaRestringido({required this.colonia, this.lat, this.lng}); @override Widget build(BuildContext context) { final center = kColoniasCoordinates[colonia] ?? const LatLng(20.5222, -100.8123); final pin = (lat != null && lng != null) ? LatLng(lat!, lng!) : center; return Container( height: 220, decoration: BoxDecoration( borderRadius: BorderRadius.circular(AppTheme.radiusLg), border: Border.all(color: AppTheme.border, width: 1), boxShadow: AppTheme.softShadow, ), clipBehavior: Clip.hardEdge, child: FlutterMap( options: MapOptions( initialCenter: pin, initialZoom: 16.0, interactionOptions: const InteractionOptions( flags: InteractiveFlag.none, ), ), children: [ TileLayer( urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', userAgentPackageName: 'com.onlineshack.recolecta', ), MarkerLayer( markers: [ Marker( point: pin, width: 36, height: 36, child: const Icon( Icons.home_rounded, color: AppTheme.primary, size: 36, ), ), ], ), ], ), ); } } class _DetailRow extends StatelessWidget { final IconData icon; final String text; const _DetailRow({required this.icon, required this.text}); @override Widget build(BuildContext context) { return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon(icon, size: 15, color: AppTheme.textSecondary), const SizedBox(width: 8), Expanded( child: Text( text, style: const TextStyle( fontSize: 13, color: AppTheme.textSecondary, height: 1.4, ), ), ), ], ); } } // ── Radio de alerta ─────────────────────────────────────────────────────────── class _RadioAlertaCard extends StatelessWidget { final int radioActual; final ValueChanged onChanged; const _RadioAlertaCard({required this.radioActual, required this.onChanged}); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: AppTheme.surface, borderRadius: BorderRadius.circular(AppTheme.radiusLg), border: Border.all(color: AppTheme.border, width: 0.5), boxShadow: AppTheme.softShadow, ), child: Column( children: [200, 400, 600].map((dist) { final selected = dist == radioActual; return GestureDetector( onTap: () => onChanged(dist), child: Container( margin: const EdgeInsets.only(bottom: 8), padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 11), decoration: BoxDecoration( color: selected ? AppTheme.primaryLight : AppTheme.background, borderRadius: BorderRadius.circular(AppTheme.radiusSm), border: Border.all( color: selected ? AppTheme.primary : AppTheme.border, width: selected ? 1.5 : 0.5, ), ), child: Row( children: [ Icon( selected ? Icons.radio_button_checked : Icons.radio_button_unchecked, color: selected ? AppTheme.primary : AppTheme.border, size: 18, ), const SizedBox(width: 10), Expanded( child: Text( '$dist metros', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, color: selected ? AppTheme.primaryDark : AppTheme.textPrimary, ), ), ), if (selected) Text( dist == 200 ? '~2-3 min' : dist == 400 ? '~4-5 min' : '~6-8 min', style: const TextStyle( fontSize: 12, color: AppTheme.primary, fontWeight: FontWeight.w500, ), ), ], ), ), ); }).toList(), ), ); } } // ── Notificaciones ──────────────────────────────────────────────────────────── class _NotificacionesCard extends StatelessWidget { final UIHouseModel casa; final ValueChanged onAlertaCercanaChanged; final ValueChanged onAlertaMediaChanged; final ValueChanged onRecordatorioChanged; const _NotificacionesCard({ required this.casa, required this.onAlertaCercanaChanged, required this.onAlertaMediaChanged, required this.onRecordatorioChanged, }); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: BoxDecoration( color: AppTheme.surface, borderRadius: BorderRadius.circular(AppTheme.radiusLg), border: Border.all(color: AppTheme.border, width: 0.5), boxShadow: AppTheme.softShadow, ), child: Column( children: [ AppLabeledSwitch( label: 'Alerta cuando el camión esté cerca', value: casa.alertaCercana, onChanged: onAlertaCercanaChanged, ), const Divider(height: 1, color: AppTheme.borderLight), AppLabeledSwitch( label: 'Alerta a distancia media', value: casa.alertaMedia, onChanged: onAlertaMediaChanged, ), const Divider(height: 1, color: AppTheme.borderLight), AppLabeledSwitch( label: 'Recordatorio diario del horario', value: casa.recordatorioDiario, onChanged: onRecordatorioChanged, ), ], ), ); } } // ── Horario ─────────────────────────────────────────────────────────────────── class _HorarioCard extends StatelessWidget { final List<_HorarioDia> _dias = const [ _HorarioDia(dia: 'Lunes', hora: '8:00 – 10:00 a.m.', activo: true), _HorarioDia(dia: 'Martes', hora: '8:00 – 10:00 a.m.', activo: true), _HorarioDia(dia: 'Miércoles', hora: 'Sin servicio', activo: false), _HorarioDia(dia: 'Jueves', hora: '8:00 – 10:00 a.m.', activo: true), _HorarioDia(dia: 'Viernes', hora: '8:00 – 10:00 a.m.', activo: true), _HorarioDia(dia: 'Sábado', hora: '9:00 – 11:00 a.m.', activo: true), _HorarioDia(dia: 'Domingo', hora: 'Sin servicio', activo: false), ]; @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: AppTheme.surface, borderRadius: BorderRadius.circular(AppTheme.radiusLg), border: Border.all(color: AppTheme.border, width: 0.5), boxShadow: AppTheme.softShadow, ), child: Column( children: _dias.map((d) { return Padding( padding: const EdgeInsets.symmetric(vertical: 7), child: Row( children: [ Text( d.dia, style: TextStyle( fontSize: 13, fontWeight: FontWeight.w500, color: d.activo ? AppTheme.textPrimary : AppTheme.textSecondary, ), ), const Spacer(), Text( d.hora, style: TextStyle( fontSize: 13, color: d.activo ? AppTheme.primary : AppTheme.textSecondary, ), ), ], ), ); }).toList(), ), ); } } class _HorarioDia { final String dia; final String hora; final bool activo; const _HorarioDia({ required this.dia, required this.hora, required this.activo, }); } // ── Sheet editar dirección ──────────────────────────────────────────────────── class _EditarDireccionSheet extends StatelessWidget { final UIHouseModel casa; const _EditarDireccionSheet({required this.casa}); @override Widget build(BuildContext context) { return Padding( padding: EdgeInsets.only( left: 24, right: 24, top: 24, bottom: MediaQuery.of(context).viewInsets.bottom + 24, ), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Center( child: Container( width: 36, height: 4, decoration: BoxDecoration( color: AppTheme.border, borderRadius: BorderRadius.circular(4), ), ), ), const SizedBox(height: 20), const Text( 'Editar dirección', style: TextStyle( fontSize: 17, fontWeight: FontWeight.w700, color: AppTheme.textPrimary, ), ), const SizedBox(height: 20), AppFormField(label: 'Calle y número', initialValue: casa.calle), const SizedBox(height: 14), AppFormField(label: 'Colonia', initialValue: casa.colonia), const SizedBox(height: 24), SizedBox( width: double.infinity, height: 50, child: ElevatedButton( onPressed: () => Navigator.pop(context), child: const Text('Guardar cambios'), ), ), ], ), ); } }