mascota en login,
This commit is contained in:
@@ -1078,706 +1078,5 @@ void _confirmAndDelete(
|
||||
);
|
||||
}
|
||||
|
||||
// ── Legacy stubs (no longer used; kept enum to avoid breaking imports) ────────
|
||||
enum _LegacyTruckStatus { disponible, enRuta, mantenimiento, detenido }
|
||||
// EOF
|
||||
|
||||
extension TruckStatusX on TruckStatus {
|
||||
String get label => switch (this) {
|
||||
TruckStatus.disponible => 'Disponible',
|
||||
TruckStatus.enRuta => 'En ruta',
|
||||
TruckStatus.mantenimiento => 'Mantenimiento',
|
||||
TruckStatus.detenido => 'Detenido',
|
||||
};
|
||||
|
||||
AppStatusBadge get badge => switch (this) {
|
||||
TruckStatus.disponible => AppStatusBadge.green(label),
|
||||
TruckStatus.enRuta => AppStatusBadge.amber(label),
|
||||
TruckStatus.mantenimiento => AppStatusBadge.gray(label),
|
||||
TruckStatus.detenido => AppStatusBadge.gray(label),
|
||||
};
|
||||
}
|
||||
|
||||
class _AdminUser {
|
||||
final String id, nombre, apellido, email, telefono;
|
||||
const _AdminUser({
|
||||
required this.id,
|
||||
required this.nombre,
|
||||
required this.apellido,
|
||||
required this.email,
|
||||
required this.telefono,
|
||||
});
|
||||
String get nombreCompleto => '$nombre $apellido';
|
||||
String get iniciales =>
|
||||
'${nombre.isNotEmpty ? nombre[0] : ''}${apellido.isNotEmpty ? apellido[0] : ''}'
|
||||
.toUpperCase();
|
||||
_AdminUser copyWith({
|
||||
String? nombre,
|
||||
String? apellido,
|
||||
String? email,
|
||||
String? telefono,
|
||||
}) => _AdminUser(
|
||||
id: id,
|
||||
nombre: nombre ?? this.nombre,
|
||||
apellido: apellido ?? this.apellido,
|
||||
email: email ?? this.email,
|
||||
telefono: telefono ?? this.telefono,
|
||||
);
|
||||
}
|
||||
|
||||
class _AdminRoute {
|
||||
final String id, nombre, zona;
|
||||
final bool activa;
|
||||
const _AdminRoute({
|
||||
required this.id,
|
||||
required this.nombre,
|
||||
required this.zona,
|
||||
this.activa = true,
|
||||
});
|
||||
_AdminRoute copyWith({String? nombre, String? zona, bool? activa}) =>
|
||||
_AdminRoute(
|
||||
id: id,
|
||||
nombre: nombre ?? this.nombre,
|
||||
zona: zona ?? this.zona,
|
||||
activa: activa ?? this.activa,
|
||||
);
|
||||
}
|
||||
|
||||
class _AdminTruck {
|
||||
final String id, placas, modelo, conductor, rutaId;
|
||||
final TruckStatus status;
|
||||
const _AdminTruck({
|
||||
required this.id,
|
||||
required this.placas,
|
||||
required this.modelo,
|
||||
required this.conductor,
|
||||
required this.status,
|
||||
required this.rutaId,
|
||||
});
|
||||
_AdminTruck copyWith({
|
||||
String? placas,
|
||||
String? modelo,
|
||||
String? conductor,
|
||||
TruckStatus? status,
|
||||
String? rutaId,
|
||||
}) => _AdminTruck(
|
||||
id: id,
|
||||
placas: placas ?? this.placas,
|
||||
modelo: modelo ?? this.modelo,
|
||||
conductor: conductor ?? this.conductor,
|
||||
status: status ?? this.status,
|
||||
rutaId: rutaId ?? this.rutaId,
|
||||
);
|
||||
}
|
||||
|
||||
// ── Pantalla ──────────────────────────────────────────────────────────────────
|
||||
class AdminScreen extends StatefulWidget {
|
||||
const AdminScreen({super.key});
|
||||
|
||||
@override
|
||||
State<AdminScreen> createState() => _AdminScreenState();
|
||||
}
|
||||
|
||||
class _AdminScreenState extends State<AdminScreen>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late final TabController _tabController;
|
||||
int _activeTab = 0;
|
||||
|
||||
final List<_AdminUser> _usuarios = [
|
||||
const _AdminUser(
|
||||
id: 'u-01',
|
||||
nombre: 'Laura',
|
||||
apellido: 'Gómez',
|
||||
email: 'laura@recolecta.com',
|
||||
telefono: '+52 461 987 1234',
|
||||
),
|
||||
const _AdminUser(
|
||||
id: 'u-02',
|
||||
nombre: 'Miguel',
|
||||
apellido: 'Sánchez',
|
||||
email: 'miguel@recolecta.com',
|
||||
telefono: '+52 461 123 7890',
|
||||
),
|
||||
];
|
||||
|
||||
final List<_AdminRoute> _rutas = [
|
||||
const _AdminRoute(id: 'RUTA-01', nombre: 'Ruta Norte', zona: 'Zona Norte'),
|
||||
const _AdminRoute(
|
||||
id: 'RUTA-02',
|
||||
nombre: 'Ruta Sur',
|
||||
zona: 'Zona Sur',
|
||||
activa: false,
|
||||
),
|
||||
];
|
||||
|
||||
final List<_AdminTruck> _camiones = [
|
||||
const _AdminTruck(
|
||||
id: 't-01',
|
||||
placas: 'GTO-101',
|
||||
modelo: 'Volvo FH',
|
||||
conductor: 'Javier Pérez',
|
||||
status: TruckStatus.enRuta,
|
||||
rutaId: 'RUTA-01',
|
||||
),
|
||||
const _AdminTruck(
|
||||
id: 't-02',
|
||||
placas: 'GTO-103',
|
||||
modelo: 'Mercedes 1830',
|
||||
conductor: 'Ana Díaz',
|
||||
status: TruckStatus.disponible,
|
||||
rutaId: 'RUTA-02',
|
||||
),
|
||||
];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_tabController = TabController(length: 3, vsync: this)
|
||||
..addListener(() {
|
||||
if (!_tabController.indexIsChanging) {
|
||||
setState(() => _activeTab = _tabController.index);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_tabController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: AppTheme.background,
|
||||
appBar: AppBar(
|
||||
title: const Text('Panel de administración'),
|
||||
bottom: TabBar(
|
||||
controller: _tabController,
|
||||
indicatorColor: Colors.white,
|
||||
labelColor: Colors.white,
|
||||
unselectedLabelColor: Colors.white70,
|
||||
tabs: const [
|
||||
Tab(text: 'Usuarios'),
|
||||
Tab(text: 'Rutas'),
|
||||
Tab(text: 'Camiones'),
|
||||
],
|
||||
),
|
||||
),
|
||||
body: TabBarView(
|
||||
controller: _tabController,
|
||||
children: [_buildUsersTab(), _buildRoutesTab(), _buildTrucksTab()],
|
||||
),
|
||||
floatingActionButton: FloatingActionButton.extended(
|
||||
onPressed: () {
|
||||
if (_activeTab == 0)
|
||||
_showUserForm();
|
||||
else if (_activeTab == 1)
|
||||
_showRouteForm();
|
||||
else
|
||||
_showTruckForm();
|
||||
},
|
||||
backgroundColor: AppTheme.primary,
|
||||
label: Text(
|
||||
_activeTab == 0
|
||||
? 'Nuevo usuario'
|
||||
: _activeTab == 1
|
||||
? 'Nueva ruta'
|
||||
: 'Nuevo camión',
|
||||
),
|
||||
icon: const Icon(Icons.add),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// ── Tab usuarios ────────────────────────────────────────────────────────────
|
||||
Widget _buildUsersTab() {
|
||||
if (_usuarios.isEmpty) return _emptyState('No hay usuarios registrados.');
|
||||
return ListView.separated(
|
||||
padding: const EdgeInsets.all(16),
|
||||
itemCount: _usuarios.length,
|
||||
separatorBuilder: (_, i) => const SizedBox(height: 12),
|
||||
itemBuilder: (context, i) {
|
||||
final u = _usuarios[i];
|
||||
return AppCard(
|
||||
child: Row(
|
||||
children: [
|
||||
CircleAvatar(
|
||||
backgroundColor: AppTheme.primaryLight,
|
||||
foregroundColor: AppTheme.primary,
|
||||
child: Text(u.iniciales),
|
||||
),
|
||||
const SizedBox(width: 14),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
u.nombreCompleto,
|
||||
style: const TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
u.email,
|
||||
style: const TextStyle(
|
||||
fontSize: 13,
|
||||
color: AppTheme.textSecondary,
|
||||
),
|
||||
),
|
||||
Text(u.telefono, style: const TextStyle(fontSize: 13)),
|
||||
],
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.edit_outlined, color: AppTheme.primary),
|
||||
onPressed: () => _showUserForm(user: u),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.delete_outline, color: AppTheme.danger),
|
||||
onPressed: () => _confirmDelete(
|
||||
'usuario',
|
||||
() => setState(
|
||||
() => _usuarios.removeWhere((x) => x.id == u.id),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// ── Tab rutas ───────────────────────────────────────────────────────────────
|
||||
Widget _buildRoutesTab() {
|
||||
if (_rutas.isEmpty) return _emptyState('No hay rutas registradas.');
|
||||
return ListView.separated(
|
||||
padding: const EdgeInsets.all(16),
|
||||
itemCount: _rutas.length,
|
||||
separatorBuilder: (_, i) => const SizedBox(height: 12),
|
||||
itemBuilder: (context, i) {
|
||||
final r = _rutas[i];
|
||||
return AppCard(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
r.nombre,
|
||||
style: const TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
r.activa
|
||||
? AppStatusBadge.green('Activa')
|
||||
: AppStatusBadge.gray('Inactiva'),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Text(
|
||||
r.zona,
|
||||
style: const TextStyle(
|
||||
fontSize: 13,
|
||||
color: AppTheme.textSecondary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton.icon(
|
||||
onPressed: () => _showRouteForm(route: r),
|
||||
icon: const Icon(Icons.edit_outlined, size: 18),
|
||||
label: const Text('Editar'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
TextButton.icon(
|
||||
onPressed: () => _confirmDelete(
|
||||
'ruta',
|
||||
() => setState(
|
||||
() => _rutas.removeWhere((x) => x.id == r.id),
|
||||
),
|
||||
),
|
||||
icon: const Icon(Icons.delete_outline, size: 18),
|
||||
label: const Text('Eliminar'),
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: AppTheme.danger,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// ── Tab camiones ────────────────────────────────────────────────────────────
|
||||
Widget _buildTrucksTab() {
|
||||
if (_camiones.isEmpty) return _emptyState('No hay camiones registrados.');
|
||||
return ListView.separated(
|
||||
padding: const EdgeInsets.all(16),
|
||||
itemCount: _camiones.length,
|
||||
separatorBuilder: (_, i) => const SizedBox(height: 12),
|
||||
itemBuilder: (context, i) {
|
||||
final t = _camiones[i];
|
||||
final ruta = _rutas.firstWhere(
|
||||
(r) => r.id == t.rutaId,
|
||||
orElse: () => const _AdminRoute(id: '', nombre: 'Sin ruta', zona: ''),
|
||||
);
|
||||
return AppCard(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
t.placas,
|
||||
style: const TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
t.status.badge,
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Text(
|
||||
'${t.modelo} · ${t.conductor}',
|
||||
style: const TextStyle(fontSize: 13),
|
||||
),
|
||||
Text(
|
||||
'Ruta: ${ruta.nombre}',
|
||||
style: const TextStyle(
|
||||
fontSize: 13,
|
||||
color: AppTheme.textSecondary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton.icon(
|
||||
onPressed: () => _showTruckForm(truck: t),
|
||||
icon: const Icon(Icons.edit_outlined, size: 18),
|
||||
label: const Text('Editar'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
TextButton.icon(
|
||||
onPressed: () => _confirmDelete(
|
||||
'camión',
|
||||
() => setState(
|
||||
() => _camiones.removeWhere((x) => x.id == t.id),
|
||||
),
|
||||
),
|
||||
icon: const Icon(Icons.delete_outline, size: 18),
|
||||
label: const Text('Eliminar'),
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: AppTheme.danger,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _emptyState(String msg) => Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Text(
|
||||
msg,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(fontSize: 15, color: AppTheme.textSecondary),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// ── Confirmación de borrado ─────────────────────────────────────────────────
|
||||
void _confirmDelete(String tipo, VoidCallback onConfirm) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (ctx) => AlertDialog(
|
||||
backgroundColor: AppTheme.surface,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(AppTheme.radiusLg),
|
||||
),
|
||||
title: Text('Eliminar $tipo'),
|
||||
content: Text('¿Deseas eliminar este $tipo?'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(ctx),
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: AppTheme.textSecondary,
|
||||
),
|
||||
child: const Text('Cancelar'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
onConfirm();
|
||||
Navigator.pop(ctx);
|
||||
},
|
||||
style: TextButton.styleFrom(foregroundColor: AppTheme.danger),
|
||||
child: const Text('Eliminar'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// ── Formulario usuario ──────────────────────────────────────────────────────
|
||||
void _showUserForm({_AdminUser? user}) {
|
||||
final nombreCtrl = TextEditingController(text: user?.nombre);
|
||||
final apellidoCtrl = TextEditingController(text: user?.apellido);
|
||||
final emailCtrl = TextEditingController(text: user?.email);
|
||||
final telefonoCtrl = TextEditingController(text: user?.telefono);
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (ctx) => AlertDialog(
|
||||
backgroundColor: AppTheme.surface,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(AppTheme.radiusLg),
|
||||
),
|
||||
title: Text(user == null ? 'Nuevo usuario' : 'Editar usuario'),
|
||||
content: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
TextField(
|
||||
controller: nombreCtrl,
|
||||
decoration: const InputDecoration(labelText: 'Nombre'),
|
||||
),
|
||||
TextField(
|
||||
controller: apellidoCtrl,
|
||||
decoration: const InputDecoration(labelText: 'Apellido'),
|
||||
),
|
||||
TextField(
|
||||
controller: emailCtrl,
|
||||
decoration: const InputDecoration(labelText: 'Correo'),
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
),
|
||||
TextField(
|
||||
controller: telefonoCtrl,
|
||||
decoration: const InputDecoration(labelText: 'Teléfono'),
|
||||
keyboardType: TextInputType.phone,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(ctx),
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: AppTheme.textSecondary,
|
||||
),
|
||||
child: const Text('Cancelar'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
final nuevo = _AdminUser(
|
||||
id: user?.id ?? 'u-${DateTime.now().millisecondsSinceEpoch}',
|
||||
nombre: nombreCtrl.text.trim(),
|
||||
apellido: apellidoCtrl.text.trim(),
|
||||
email: emailCtrl.text.trim(),
|
||||
telefono: telefonoCtrl.text.trim(),
|
||||
);
|
||||
setState(() {
|
||||
if (user == null) {
|
||||
_usuarios.add(nuevo);
|
||||
} else {
|
||||
final idx = _usuarios.indexWhere((x) => x.id == user.id);
|
||||
if (idx >= 0) _usuarios[idx] = nuevo;
|
||||
}
|
||||
});
|
||||
Navigator.pop(ctx);
|
||||
},
|
||||
child: Text(user == null ? 'Crear' : 'Guardar'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// ── Formulario ruta ─────────────────────────────────────────────────────────
|
||||
void _showRouteForm({_AdminRoute? route}) {
|
||||
final nombreCtrl = TextEditingController(text: route?.nombre);
|
||||
final zonaCtrl = TextEditingController(text: route?.zona);
|
||||
bool activa = route?.activa ?? true;
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (ctx) => StatefulBuilder(
|
||||
builder: (ctx, setInner) => AlertDialog(
|
||||
backgroundColor: AppTheme.surface,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(AppTheme.radiusLg),
|
||||
),
|
||||
title: Text(route == null ? 'Nueva ruta' : 'Editar ruta'),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
TextField(
|
||||
controller: nombreCtrl,
|
||||
decoration: const InputDecoration(labelText: 'Nombre de ruta'),
|
||||
),
|
||||
TextField(
|
||||
controller: zonaCtrl,
|
||||
decoration: const InputDecoration(labelText: 'Zona'),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
const Expanded(child: Text('Ruta activa')),
|
||||
Switch.adaptive(
|
||||
value: activa,
|
||||
onChanged: (v) => setInner(() => activa = v),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(ctx),
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: AppTheme.textSecondary,
|
||||
),
|
||||
child: const Text('Cancelar'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
final nueva = _AdminRoute(
|
||||
id: route?.id ?? 'r-${DateTime.now().millisecondsSinceEpoch}',
|
||||
nombre: nombreCtrl.text.trim(),
|
||||
zona: zonaCtrl.text.trim(),
|
||||
activa: activa,
|
||||
);
|
||||
setState(() {
|
||||
if (route == null) {
|
||||
_rutas.add(nueva);
|
||||
} else {
|
||||
final idx = _rutas.indexWhere((x) => x.id == route.id);
|
||||
if (idx >= 0) _rutas[idx] = nueva;
|
||||
}
|
||||
});
|
||||
Navigator.pop(ctx);
|
||||
},
|
||||
child: Text(route == null ? 'Crear' : 'Guardar'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// ── Formulario camión ───────────────────────────────────────────────────────
|
||||
void _showTruckForm({_AdminTruck? truck}) {
|
||||
final placasCtrl = TextEditingController(text: truck?.placas);
|
||||
final modeloCtrl = TextEditingController(text: truck?.modelo);
|
||||
final conductorCtrl = TextEditingController(text: truck?.conductor);
|
||||
TruckStatus status = truck?.status ?? TruckStatus.disponible;
|
||||
String selectedRuta =
|
||||
truck?.rutaId ?? (_rutas.isNotEmpty ? _rutas.first.id : '');
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (ctx) => StatefulBuilder(
|
||||
builder: (ctx, setInner) => AlertDialog(
|
||||
backgroundColor: AppTheme.surface,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(AppTheme.radiusLg),
|
||||
),
|
||||
title: Text(truck == null ? 'Nuevo camión' : 'Editar camión'),
|
||||
content: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
TextField(
|
||||
controller: placasCtrl,
|
||||
decoration: const InputDecoration(labelText: 'Placas'),
|
||||
),
|
||||
TextField(
|
||||
controller: modeloCtrl,
|
||||
decoration: const InputDecoration(labelText: 'Modelo'),
|
||||
),
|
||||
TextField(
|
||||
controller: conductorCtrl,
|
||||
decoration: const InputDecoration(labelText: 'Conductor'),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
DropdownButtonFormField<String>(
|
||||
value: selectedRuta.isEmpty ? null : selectedRuta,
|
||||
decoration: const InputDecoration(labelText: 'Ruta'),
|
||||
items: _rutas
|
||||
.map(
|
||||
(r) => DropdownMenuItem(
|
||||
value: r.id,
|
||||
child: Text(r.nombre),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
onChanged: (v) {
|
||||
if (v != null) setInner(() => selectedRuta = v);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
DropdownButtonFormField<TruckStatus>(
|
||||
value: status,
|
||||
decoration: const InputDecoration(labelText: 'Estatus'),
|
||||
items: TruckStatus.values
|
||||
.map(
|
||||
(s) => DropdownMenuItem(value: s, child: Text(s.label)),
|
||||
)
|
||||
.toList(),
|
||||
onChanged: (v) {
|
||||
if (v != null) setInner(() => status = v);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(ctx),
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: AppTheme.textSecondary,
|
||||
),
|
||||
child: const Text('Cancelar'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
final nuevo = _AdminTruck(
|
||||
id: truck?.id ?? 't-${DateTime.now().millisecondsSinceEpoch}',
|
||||
placas: placasCtrl.text.trim(),
|
||||
modelo: modeloCtrl.text.trim(),
|
||||
conductor: conductorCtrl.text.trim(),
|
||||
status: status,
|
||||
rutaId: selectedRuta,
|
||||
);
|
||||
setState(() {
|
||||
if (truck == null) {
|
||||
_camiones.add(nuevo);
|
||||
} else {
|
||||
final idx = _camiones.indexWhere((x) => x.id == truck.id);
|
||||
if (idx >= 0) _camiones[idx] = nuevo;
|
||||
}
|
||||
});
|
||||
Navigator.pop(ctx);
|
||||
},
|
||||
child: Text(truck == null ? 'Crear' : 'Guardar'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user