Co-authored-by: eddgranados12 <eddgranados12@users.noreply.github.com>
Co-authored-by: MENDOZA BALLARDO GAEL RICARDO <gael-meb123@users.noreply.github.com> Co-authored-by: Azareth-Tr <Azareth-Tr@users.noreply.github.com> modificacion de las vistas principales para el usuario ciudadano, primer avance para el panel admin
This commit is contained in:
@@ -4,6 +4,7 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:flutter_map/flutter_map.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';
|
||||
@@ -30,8 +31,8 @@ class _CitizenHomeScreenState extends State<CitizenHomeScreen> {
|
||||
Future<void> _loadData() async {
|
||||
try {
|
||||
const storage = FlutterSecureStorage();
|
||||
final token = await storage.read(key: 'token') ?? '';
|
||||
|
||||
final token = await storage.read(key: authTokenStorageKey) ?? '';
|
||||
|
||||
if (token.isEmpty) {
|
||||
if (mounted) setState(() => _isLoading = false);
|
||||
return;
|
||||
@@ -53,7 +54,8 @@ class _CitizenHomeScreenState extends State<CitizenHomeScreen> {
|
||||
if (colRes.data is List) {
|
||||
for (var c in colRes.data) {
|
||||
final nombre = c['nombre'] ?? c['colonia'] ?? '';
|
||||
final horario = c['horario_estimado'] ?? c['schedule'] ?? 'Horario no definido';
|
||||
final horario =
|
||||
c['horario_estimado'] ?? c['schedule'] ?? 'Horario no definido';
|
||||
if (nombre.isNotEmpty) {
|
||||
_horarios[nombre] = horario;
|
||||
}
|
||||
@@ -67,14 +69,19 @@ class _CitizenHomeScreenState extends State<CitizenHomeScreen> {
|
||||
final res = await dio.get('/addresses');
|
||||
List<UIHouseModel> loadedCasas = [];
|
||||
if (res.data is List) {
|
||||
loadedCasas = (res.data as List).map((e) => UIHouseModel.fromJson(e)).toList();
|
||||
loadedCasas = (res.data as List)
|
||||
.map((e) => UIHouseModel.fromJson(e))
|
||||
.toList();
|
||||
}
|
||||
|
||||
// 3. Obtener ETA (Tiempo Estimado) para cada domicilio
|
||||
Map<String, String> loadedEtas = {};
|
||||
for (var casa in loadedCasas) {
|
||||
try {
|
||||
final etaRes = await dio.get('/eta', queryParameters: {'address_id': casa.id});
|
||||
final etaRes = await dio.get(
|
||||
'/eta',
|
||||
queryParameters: {'address_id': casa.id},
|
||||
);
|
||||
loadedEtas[casa.id] = etaRes.data['mensaje'] ?? 'Estado desconocido';
|
||||
} catch (e) {
|
||||
loadedEtas[casa.id] = 'Calculando...';
|
||||
@@ -110,29 +117,30 @@ class _CitizenHomeScreenState extends State<CitizenHomeScreen> {
|
||||
setState(() => _isLoading = true);
|
||||
_loadData();
|
||||
},
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
body: _isLoading
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: _casas.isEmpty
|
||||
? const Center(
|
||||
child: Text(
|
||||
'No tienes domicilios registrados.',
|
||||
style: TextStyle(color: AppTheme.textSecondary),
|
||||
),
|
||||
)
|
||||
: ListView.separated(
|
||||
padding: const EdgeInsets.all(16),
|
||||
itemCount: _casas.length,
|
||||
separatorBuilder: (_, __) => const SizedBox(height: 24),
|
||||
itemBuilder: (context, index) {
|
||||
final casa = _casas[index];
|
||||
final eta = _etas[casa.id] ?? 'Actualizando...';
|
||||
final horario = _horarios[casa.colonia] ?? 'Horario asignado a la ruta';
|
||||
return _HouseEtaCard(casa: casa, etaMsg: eta, horario: horario);
|
||||
},
|
||||
),
|
||||
? const Center(
|
||||
child: Text(
|
||||
'No tienes domicilios registrados.',
|
||||
style: TextStyle(color: AppTheme.textSecondary),
|
||||
),
|
||||
)
|
||||
: ListView.separated(
|
||||
padding: const EdgeInsets.all(16),
|
||||
itemCount: _casas.length,
|
||||
separatorBuilder: (_, __) => const SizedBox(height: 24),
|
||||
itemBuilder: (context, index) {
|
||||
final casa = _casas[index];
|
||||
final eta = _etas[casa.id] ?? 'Actualizando...';
|
||||
final horario =
|
||||
_horarios[casa.colonia] ?? 'Horario asignado a la ruta';
|
||||
return _HouseEtaCard(casa: casa, etaMsg: eta, horario: horario);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -151,13 +159,11 @@ class _HouseEtaCard extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final center = kColoniasCoordinates[casa.colonia] ?? const LatLng(20.5222, -100.8123);
|
||||
|
||||
// Restricción del mapa a la colonia (Privacidad por Diseño)
|
||||
final bounds = LatLngBounds(
|
||||
LatLng(center.latitude - 0.01, center.longitude - 0.01),
|
||||
LatLng(center.latitude + 0.01, center.longitude + 0.01),
|
||||
);
|
||||
// Si el usuario registró coordenadas, las usamos; si no, el centro de la colonia
|
||||
final coloniaCenter = kColoniaCenter(casa.colonia);
|
||||
final pin = (casa.lat != null && casa.lng != null)
|
||||
? LatLng(casa.lat!, casa.lng!)
|
||||
: coloniaCenter;
|
||||
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
@@ -170,15 +176,15 @@ class _HouseEtaCard extends StatelessWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
// ── Mapa Restringido ──
|
||||
// ── Mapa Restringido a la colonia ──
|
||||
SizedBox(
|
||||
height: 180,
|
||||
child: FlutterMap(
|
||||
options: MapOptions(
|
||||
initialCameraFit: CameraFit.bounds(bounds: bounds),
|
||||
cameraConstraint: CameraConstraint.contain(bounds: bounds),
|
||||
initialCenter: pin,
|
||||
initialZoom: 16.0,
|
||||
interactionOptions: const InteractionOptions(
|
||||
flags: InteractiveFlag.drag | InteractiveFlag.pinchZoom,
|
||||
flags: InteractiveFlag.none,
|
||||
),
|
||||
),
|
||||
children: [
|
||||
@@ -186,22 +192,24 @@ class _HouseEtaCard extends StatelessWidget {
|
||||
urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||
userAgentPackageName: 'com.onlineshack.recolecta',
|
||||
),
|
||||
CircleLayer(
|
||||
circles: [
|
||||
CircleMarker(
|
||||
point: center,
|
||||
color: AppTheme.primary.withValues(alpha: 0.15),
|
||||
borderColor: AppTheme.primary,
|
||||
borderStrokeWidth: 2,
|
||||
radius: 400,
|
||||
useRadiusInMeter: true,
|
||||
MarkerLayer(
|
||||
markers: [
|
||||
Marker(
|
||||
point: pin,
|
||||
width: 36,
|
||||
height: 36,
|
||||
child: const Icon(
|
||||
Icons.home_rounded,
|
||||
color: AppTheme.primary,
|
||||
size: 36,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
|
||||
// ── Recuadro de Información ──
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
@@ -223,11 +231,19 @@ class _HouseEtaCard extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 14),
|
||||
_InfoRow(icon: Icons.location_on_outlined, title: 'Dirección', value: casa.direccionCompleta),
|
||||
_InfoRow(
|
||||
icon: Icons.location_on_outlined,
|
||||
title: 'Dirección',
|
||||
value: casa.direccionCompleta,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_InfoRow(icon: Icons.schedule_outlined, title: 'Horario Habitual', value: horario),
|
||||
_InfoRow(
|
||||
icon: Icons.schedule_outlined,
|
||||
title: 'Horario Habitual',
|
||||
value: horario,
|
||||
),
|
||||
const SizedBox(height: 18),
|
||||
|
||||
|
||||
// ── Alerta de ETA en Tiempo Real ──
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
@@ -239,7 +255,10 @@ class _HouseEtaCard extends StatelessWidget {
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Icon(Icons.local_shipping_outlined, color: AppTheme.primaryDark),
|
||||
const Icon(
|
||||
Icons.local_shipping_outlined,
|
||||
color: AppTheme.primaryDark,
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Column(
|
||||
@@ -282,8 +301,12 @@ class _InfoRow extends StatelessWidget {
|
||||
final IconData icon;
|
||||
final String title;
|
||||
final String value;
|
||||
|
||||
const _InfoRow({required this.icon, required this.title, required this.value});
|
||||
|
||||
const _InfoRow({
|
||||
required this.icon,
|
||||
required this.title,
|
||||
required this.value,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -298,12 +321,20 @@ class _InfoRow extends StatelessWidget {
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: const TextStyle(fontSize: 12, color: AppTheme.textSecondary, fontWeight: FontWeight.w500),
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
color: AppTheme.textSecondary,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
value,
|
||||
style: const TextStyle(fontSize: 14, color: AppTheme.textPrimary, height: 1.3),
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
color: AppTheme.textPrimary,
|
||||
height: 1.3,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user