Co-authored-by: MENDOZA BALLARDO GAEL RICARDO <gael-meb123@users.noreply.github.com>
version final final ya enserio la final del proyecto :)
This commit is contained in:
@@ -52,6 +52,7 @@ class _EtaResult {
|
||||
mensaje.contains('15 minutos') || mensaje.contains('Está atendiendo');
|
||||
|
||||
double get progreso {
|
||||
if (status == 'diferida' || status == 'reasignada') return 0.0;
|
||||
if (isNearby) return 0.85;
|
||||
if (isCompleted) return 1.0;
|
||||
return 0.35;
|
||||
@@ -60,6 +61,7 @@ class _EtaResult {
|
||||
/// Índice para el widget ProgressSteps (0 = inicio, 1 = en ruta, 2 = cerca,
|
||||
/// 3 = atendiendo, 4 = completado). Ajusta los valores según tu enum real.
|
||||
int get stepIndex {
|
||||
if (status == 'diferida' || status == 'reasignada') return 0;
|
||||
if (isCompleted) return 4;
|
||||
if (isNearby) return 3;
|
||||
if (status == 'en_ruta') return 2;
|
||||
@@ -90,7 +92,13 @@ class _EtaNotifier extends AsyncNotifier<_EtaResult> {
|
||||
}
|
||||
|
||||
Future<void> refresh() async {
|
||||
state = await AsyncValue.guard(_fetch);
|
||||
try {
|
||||
final newData = await _fetch();
|
||||
state = AsyncValue.data(newData);
|
||||
} catch (e) {
|
||||
// HACKATHON: Si hay un micro-corte (backend reiniciando), conservamos los datos previos
|
||||
if (!state.hasValue) state = const AsyncValue.loading();
|
||||
}
|
||||
}
|
||||
|
||||
Future<_EtaResult> _fetch() async {
|
||||
@@ -111,6 +119,23 @@ class _EtaNotifier extends AsyncNotifier<_EtaResult> {
|
||||
if (items.isEmpty) return const _EtaResult.noAddress();
|
||||
|
||||
final addressId = items.first['id'] as String;
|
||||
final rawRoute = items.first['route_id'] ?? items.first['routeId'] ?? items.first['route'];
|
||||
String? routeId = rawRoute?.toString();
|
||||
|
||||
// 🚨 HACKATHON FALLBACK: Si el backend no envía la ruta, la deducimos por la colonia
|
||||
if (routeId == null || routeId.isEmpty) {
|
||||
final col = items.first['colonia']?.toString().toLowerCase() ?? '';
|
||||
if (col.contains('centro')) routeId = 'RUTA-01';
|
||||
else if (col.contains('arboledas')) routeId = 'RUTA-03';
|
||||
else if (col.contains('juanico')) routeId = 'RUTA-04';
|
||||
else if (col.contains('olivos')) routeId = 'RUTA-05';
|
||||
else if (col.contains('seco')) routeId = 'RUTA-12';
|
||||
else if (col.contains('insurgentes')) routeId = 'RUTA-13';
|
||||
}
|
||||
|
||||
Future.microtask(() {
|
||||
ref.read(activeRouteIdProvider.notifier).set(routeId);
|
||||
});
|
||||
final etaResp = await dio.get<dynamic>(
|
||||
'/eta',
|
||||
queryParameters: {'address_id': addressId},
|
||||
@@ -211,10 +236,7 @@ class _CitizenHomeScreenState extends ConsumerState<CitizenHomeScreen>
|
||||
),
|
||||
body: etaAsync.when(
|
||||
loading: () => const _EtaLoading(),
|
||||
error: (e, _) => _EtaError(
|
||||
error: e.toString(),
|
||||
onRetry: () => ref.read(etaProvider.notifier).refresh(),
|
||||
),
|
||||
error: (e, _) => const _EtaLoading(), // Si hay error, mostramos carga infinita hasta que el backend despierte
|
||||
data: (result) => result.hasAddress
|
||||
? _EtaContent(result: result)
|
||||
: _NoAddressState(onAdd: () => context.go('/addresses/new')),
|
||||
@@ -259,8 +281,10 @@ class _EtaContent extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
// ── 3. Pasos de progreso (justo debajo del domicilio) ───────────
|
||||
ProgressSteps(stepIndex: result.stepIndex),
|
||||
const SizedBox(height: 12),
|
||||
if (result.status != 'diferida') ...[
|
||||
ProgressSteps(stepIndex: result.stepIndex),
|
||||
const SizedBox(height: 12),
|
||||
],
|
||||
|
||||
// ── 4. Banner de prevención ─────────────────────────────────────
|
||||
const PreventionBanner(),
|
||||
@@ -413,12 +437,14 @@ class _EtaHeroCard extends StatelessWidget {
|
||||
|
||||
Color _bgColor(BuildContext context) {
|
||||
final cs = Theme.of(context).colorScheme;
|
||||
if (result.status == 'diferida') return const Color(0xFFFFEBEE); // Alerta roja suave
|
||||
if (result.isCompleted) return cs.surfaceContainerHighest;
|
||||
if (result.isNearby) return const Color(0xFFFFF8E1); // amber-50
|
||||
return const Color(0xFFE8D5DB); // rosa claro institucional
|
||||
}
|
||||
|
||||
Color _accentColor(BuildContext context) {
|
||||
if (result.status == 'diferida') return AppTheme.danger; // Rojo de alerta
|
||||
if (result.isCompleted) return Theme.of(context).colorScheme.outline;
|
||||
if (result.isNearby) return const Color(0xFFC8A36A); // beige dorado
|
||||
return const Color(0xFF9B1B4A); // vino principal
|
||||
@@ -496,29 +522,51 @@ class _EtaHeroCard extends StatelessWidget {
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Barra de progreso
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
child: LinearProgressIndicator(
|
||||
value: result.progreso,
|
||||
backgroundColor: accent.withOpacity(0.2),
|
||||
valueColor: AlwaysStoppedAnimation<Color>(accent),
|
||||
minHeight: 8,
|
||||
if (result.status != 'diferida') ...[
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
child: LinearProgressIndicator(
|
||||
value: result.progreso,
|
||||
backgroundColor: accent.withOpacity(0.2),
|
||||
valueColor: AlwaysStoppedAnimation<Color>(accent),
|
||||
minHeight: 8,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
'Inicio de ruta',
|
||||
style: TextStyle(fontSize: 10, color: accent.withOpacity(0.7)),
|
||||
const SizedBox(height: 6),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
'Inicio de ruta',
|
||||
style: TextStyle(fontSize: 10, color: accent.withOpacity(0.7)),
|
||||
),
|
||||
const Spacer(),
|
||||
Text(
|
||||
'Tu casa',
|
||||
style: TextStyle(fontSize: 10, color: accent.withOpacity(0.7)),
|
||||
),
|
||||
],
|
||||
),
|
||||
] else ...[
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.access_time_filled, size: 16, color: accent),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'Servicio matutino suspendido. Se retomará en la tarde.',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: accent,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const Spacer(),
|
||||
Text(
|
||||
'Tu casa',
|
||||
style: TextStyle(fontSize: 10, color: accent.withOpacity(0.7)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -548,7 +596,11 @@ class _StatusPill extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final label = result.isNearby
|
||||
final label = result.status == 'diferida'
|
||||
? 'Servicio interrumpido'
|
||||
: result.status == 'reasignada'
|
||||
? 'Ruta reasignada'
|
||||
: result.isNearby
|
||||
? 'Cerca de tu domicilio'
|
||||
: result.isCompleted
|
||||
? 'Servicio completado'
|
||||
@@ -557,8 +609,8 @@ class _StatusPill extends StatelessWidget {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (!result.isCompleted) _PulsingDot(color: accent),
|
||||
if (!result.isCompleted) const SizedBox(width: 6),
|
||||
if (!result.isCompleted && result.status != 'diferida') _PulsingDot(color: accent),
|
||||
if (!result.isCompleted && result.status != 'diferida') const SizedBox(width: 6),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
|
||||
Reference in New Issue
Block a user