feat(gestion): suppression en cascade exercice/programme/séance-modèle (ticket #37)

Ajoute la suppression avec gestion des dépendances en cascade sur les
écrans bibliothèque d'exercices, programme et séance-modèle, avec les
use cases et le bootstrap applicatif associés. flutter analyze propre,
58/58 tests verts, build APK debug validé.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-18 10:33:06 +02:00
parent 7434790da2
commit 1a0d01f038
10 changed files with 881 additions and 26 deletions

View File

@ -157,6 +157,7 @@ final class _ExerciseLibraryScreenState extends State<ExerciseLibraryScreen> {
return ExerciseListTile(
exercise: exercise,
onTap: () => _openForm(exercise: exercise),
onDelete: () => _confirmDelete(exercise),
);
}, childCount: filtered.length * 2 - 1),
),
@ -203,13 +204,64 @@ final class _ExerciseLibraryScreenState extends State<ExerciseLibraryScreen> {
_reload();
}
}
Future<void> _confirmDelete(Exercise exercise) async {
final used = await widget.exerciseUseCases.isUsedInPrograms(
exercise.metadata.id,
);
if (!mounted) return;
final confirmed = await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: Text('Supprimer ${exercise.name} ?'),
content: Text(
used
? 'Cet exercice est utilisé dans un ou plusieurs programmes. '
'Le supprimer le retirera de ces programmes. Continuer ?'
: 'Cette action est irréversible.',
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(false),
child: const Text('Annuler'),
),
FilledButton.icon(
onPressed: () => Navigator.of(context).pop(true),
icon: const Icon(Icons.delete_outline),
label: const Text('Supprimer'),
),
],
),
);
if (confirmed != true) return;
try {
await widget.exerciseUseCases.delete(exercise.metadata.id);
if (!mounted) return;
_reload();
} on Exception catch (error) {
_showSnackBar(error.toString());
}
}
void _showSnackBar(String message) {
if (!mounted) return;
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text(message)));
}
}
final class ExerciseListTile extends StatelessWidget {
const ExerciseListTile({required this.exercise, this.onTap, super.key});
const ExerciseListTile({
required this.exercise,
this.onTap,
this.onDelete,
super.key,
});
final Exercise exercise;
final VoidCallback? onTap;
final VoidCallback? onDelete;
@override
Widget build(BuildContext context) {
@ -258,7 +310,19 @@ final class ExerciseListTile extends StatelessWidget {
),
],
),
trailing: const Icon(Icons.chevron_right),
trailing: onDelete == null
? const Icon(Icons.chevron_right)
: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
tooltip: 'Supprimer lexercice',
icon: const Icon(Icons.delete_outline),
onPressed: onDelete,
),
const Icon(Icons.chevron_right),
],
),
);
}
}