Files
EM2_ERP/em2rp/lib/views/widgets/data_management/depot_management.dart
T

234 lines
8.4 KiB
Dart

import 'package:flutter/material.dart';
import 'package:em2rp/models/depot_model.dart';
import 'package:em2rp/services/travel_service.dart';
import 'package:em2rp/utils/colors.dart';
import 'package:em2rp/views/widgets/inputs/address_autocomplete_field.dart';
class DepotManagement extends StatefulWidget {
const DepotManagement({super.key});
@override
State<DepotManagement> createState() => _DepotManagementState();
}
class _DepotManagementState extends State<DepotManagement> {
final _service = TravelService();
bool _isLoading = false;
void _showDepotDialog({DepotModel? depot}) {
final nameCtrl = TextEditingController(text: depot?.name ?? '');
final addressCtrl = TextEditingController(text: depot?.address ?? '');
final formKey = GlobalKey<FormState>();
showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: Text(depot == null ? 'Ajouter un dépôt' : 'Modifier le dépôt'),
content: SizedBox(
width: 420,
child: Form(
key: formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextFormField(
controller: nameCtrl,
decoration: const InputDecoration(
labelText: 'Nom du dépôt *',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.warehouse_outlined),
hintText: 'ex: Dépôt principal',
),
validator: (v) =>
v == null || v.trim().isEmpty ? 'Requis' : null,
),
const SizedBox(height: 16),
AddressAutocompleteField(
controller: addressCtrl,
label: 'Adresse du dépôt *',
validator: (v) =>
v == null || v.trim().isEmpty ? 'Requis' : null,
),
],
),
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(ctx),
child: const Text('Annuler'),
),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.rouge,
foregroundColor: Colors.white,
),
onPressed: () async {
if (!formKey.currentState!.validate()) return;
Navigator.pop(ctx);
setState(() => _isLoading = true);
try {
if (depot == null) {
await _service.addDepot(DepotModel(
id: '',
name: nameCtrl.text.trim(),
address: addressCtrl.text.trim(),
));
} else {
await _service.updateDepot(depot.copyWith(
name: nameCtrl.text.trim(),
address: addressCtrl.text.trim(),
));
}
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Erreur: $e'), backgroundColor: Colors.red),
);
}
} finally {
if (mounted) setState(() => _isLoading = false);
}
},
child: Text(depot == null ? 'Ajouter' : 'Enregistrer'),
),
],
),
);
}
Future<void> _delete(DepotModel depot) async {
final confirm = await showDialog<bool>(
context: context,
builder: (ctx) => AlertDialog(
title: const Text('Supprimer le dépôt'),
content: Text('Supprimer "${depot.name}" ?'),
actions: [
TextButton(onPressed: () => Navigator.pop(ctx, false), child: const Text('Annuler')),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
foregroundColor: Colors.white,
),
onPressed: () => Navigator.pop(ctx, true),
child: const Text('Supprimer'),
),
],
),
);
if (confirm == true) {
setState(() => _isLoading = true);
try {
await _service.deleteDepot(depot.id);
} finally {
if (mounted) setState(() => _isLoading = false);
}
}
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(Icons.warehouse_outlined, color: AppColors.rouge, size: 28),
const SizedBox(width: 12),
Text(
'Dépôts',
style: Theme.of(context)
.textTheme
.headlineSmall
?.copyWith(fontWeight: FontWeight.bold),
),
const Spacer(),
ElevatedButton.icon(
icon: const Icon(Icons.add),
label: const Text('Ajouter un dépôt'),
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.rouge,
foregroundColor: Colors.white,
),
onPressed: () => _showDepotDialog(),
),
],
),
const SizedBox(height: 8),
Text(
'Définissez les adresses de départ pour le calcul des frais de déplacement.',
style: Theme.of(context).textTheme.bodyMedium?.copyWith(color: Colors.grey[600]),
),
const SizedBox(height: 24),
if (_isLoading) const Center(child: CircularProgressIndicator()),
Expanded(
child: StreamBuilder<List<DepotModel>>(
stream: _service.watchDepots(),
builder: (context, snap) {
if (snap.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
final depots = snap.data ?? [];
if (depots.isEmpty) {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.warehouse_outlined,
size: 64, color: Colors.grey[300]),
const SizedBox(height: 16),
Text(
'Aucun dépôt configuré',
style: Theme.of(context)
.textTheme
.titleMedium
?.copyWith(color: Colors.grey[500]),
),
const SizedBox(height: 8),
const Text('Ajoutez un dépôt pour commencer.'),
],
),
);
}
return ListView.separated(
itemCount: depots.length,
separatorBuilder: (_, __) => const Divider(height: 1),
itemBuilder: (context, i) {
final d = depots[i];
return ListTile(
leading: CircleAvatar(
backgroundColor: AppColors.rouge.withValues(alpha: 0.1),
child: Icon(Icons.warehouse_outlined, color: AppColors.rouge),
),
title: Text(d.name,
style: const TextStyle(fontWeight: FontWeight.w600)),
subtitle: Text(d.address),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: const Icon(Icons.edit_outlined),
tooltip: 'Modifier',
onPressed: () => _showDepotDialog(depot: d),
),
IconButton(
icon: const Icon(Icons.delete_outline, color: Colors.red),
tooltip: 'Supprimer',
onPressed: () => _delete(d),
),
],
),
);
},
);
},
),
),
],
),
);
}
}