mascota en login,
This commit is contained in:
@@ -12,13 +12,17 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:flutter_map/flutter_map.dart';
|
||||
import 'package:latlong2/latlong.dart';
|
||||
|
||||
import '../../core/theme/app_theme.dart';
|
||||
import '../home/colonias_data.dart';
|
||||
import '../../core/widgets/app_widgets.dart';
|
||||
import '../../core/network/api_client.dart';
|
||||
import '../notifications/notification_service.dart';
|
||||
import '../../shared/widgets/prevention_banner.dart';
|
||||
import '../../shared/widgets/progress_steps.dart';
|
||||
import '../separation_guide/ai_pet_chat_screen.dart';
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Modelo de resultado ETA
|
||||
@@ -29,6 +33,8 @@ class _EtaResult {
|
||||
final String direccion;
|
||||
final String colonia;
|
||||
final bool hasAddress;
|
||||
final double? lat;
|
||||
final double? lng;
|
||||
|
||||
const _EtaResult({
|
||||
required this.mensaje,
|
||||
@@ -36,6 +42,8 @@ class _EtaResult {
|
||||
required this.direccion,
|
||||
required this.colonia,
|
||||
required this.hasAddress,
|
||||
this.lat,
|
||||
this.lng,
|
||||
});
|
||||
|
||||
const _EtaResult.noAddress()
|
||||
@@ -43,7 +51,9 @@ class _EtaResult {
|
||||
status = '',
|
||||
direccion = '',
|
||||
colonia = '',
|
||||
hasAddress = false;
|
||||
hasAddress = false,
|
||||
lat = null,
|
||||
lng = null;
|
||||
|
||||
// ── Utilidades derivadas ───────────────────────────────────────────────────
|
||||
|
||||
@@ -114,12 +124,14 @@ class _EtaNotifier extends AsyncNotifier<_EtaResult> {
|
||||
status: data['status'] as String? ?? '',
|
||||
direccion: items.first['calle'] as String? ?? '',
|
||||
colonia: items.first['colonia'] as String? ?? '',
|
||||
lat: (items.first['lat'] as num?)?.toDouble(),
|
||||
lng: (items.first['lng'] as num?)?.toDouble(),
|
||||
hasAddress: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final etaProvider = AsyncNotifierProvider.autoDispose<_EtaNotifier, _EtaResult>(
|
||||
final etaProvider = AsyncNotifierProvider<_EtaNotifier, _EtaResult>(
|
||||
_EtaNotifier.new,
|
||||
);
|
||||
|
||||
@@ -227,7 +239,13 @@ class _EtaContent extends StatelessWidget {
|
||||
trailing: AppStatusBadge.green('Activo'),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
|
||||
// ── 2.5. Mapa de ubicación ─────────────────────────────────
|
||||
_MapaUbicacion(
|
||||
colonia: result.colonia,
|
||||
lat: result.lat,
|
||||
lng: result.lng,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
// ── 3. Pasos de progreso (justo debajo del domicilio) ───────────
|
||||
ProgressSteps(stepIndex: result.stepIndex),
|
||||
const SizedBox(height: 12),
|
||||
@@ -236,11 +254,15 @@ class _EtaContent extends StatelessWidget {
|
||||
const PreventionBanner(),
|
||||
const SizedBox(height: 12),
|
||||
|
||||
// ── 5. Badge de suscripción FCM ─────────────────────────────────
|
||||
// ── 5. Banner del Chat IA (Eco) ─────────────────────────────────
|
||||
const _EcoChatBanner(),
|
||||
const SizedBox(height: 12),
|
||||
|
||||
// ── 6. Badge de suscripción FCM ─────────────────────────────────
|
||||
const _FcmStatusBadge(),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// ── 6. Horario semanal ──────────────────────────────────────────
|
||||
// ── 7. Horario semanal ──────────────────────────────────────────
|
||||
AppSectionTitle(title: 'Horario del camión'),
|
||||
_HorarioCard(),
|
||||
const SizedBox(height: 24),
|
||||
@@ -250,6 +272,122 @@ class _EtaContent extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Banner de Eco (Chat IA)
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
class _EcoChatBanner extends StatelessWidget {
|
||||
const _EcoChatBanner();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (_) => const AiPetChatScreen()),
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: AppTheme.primaryDark,
|
||||
borderRadius: BorderRadius.circular(AppTheme.radiusLg),
|
||||
boxShadow: AppTheme.softShadow,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.white24,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(Icons.pets, color: Colors.white, size: 28),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
const Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'¿Dudas sobre reciclaje?',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 4),
|
||||
Text(
|
||||
'Pregúntale a Eco, tu asistente inteligente',
|
||||
style: TextStyle(color: Colors.white70, fontSize: 13),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Icon(Icons.chevron_right, color: Colors.white),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Mapa de ubicación del domicilio (no interactivo)
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
class _MapaUbicacion extends StatelessWidget {
|
||||
final String colonia;
|
||||
final double? lat;
|
||||
final double? lng;
|
||||
const _MapaUbicacion({required this.colonia, this.lat, this.lng});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Usar coordenadas del usuario si están disponibles, sino usar centro de colonia
|
||||
final center = kColoniaCenter(colonia);
|
||||
final pin = (lat != null && lng != null) ? LatLng(lat!, lng!) : center;
|
||||
|
||||
return Container(
|
||||
height: 200,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(AppTheme.radiusLg),
|
||||
border: Border.all(color: AppTheme.border, width: 1),
|
||||
),
|
||||
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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Hero card: estado + ventana horaria + barra de progreso
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user