feat(front): câbler PATCH refus utilisateur depuis wizards validation (#110)

- UserService.refuseUser → /users/:id/refuser
- AM + famille : envoi motifs, onSuccess + refresh liste
- Famille : refus séquentiel pour chaque parent en_attente
- ValidationRefusForm : état isSubmitting (UX)

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-05-13 17:38:29 +02:00
co-authored by Cursor
parent 43dabd1885
commit 7ba8d51668
4 changed files with 115 additions and 24 deletions
@@ -697,16 +697,12 @@ class _ValidationFamilyWizardState extends State<ValidationFamilyWizard> {
children: [
Expanded(
child: ValidationRefusForm(
isSubmitting: _submitting,
onCancel: widget.onClose,
onPrevious: () => setState(() => _showRefusForm = false),
onSubmit: (comment) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text(
'Refus (à brancher sur lAPI refus ticket #110)')),
);
widget.onClose();
if (comment == null || comment.trim().isEmpty) return;
_refuserEnvoyer(comment.trim());
},
),
),
@@ -714,4 +710,42 @@ class _ValidationFamilyWizardState extends State<ValidationFamilyWizard> {
),
);
}
/// Refuse chaque parent encore `en_attente` (même commentaire, e-mail reprise par compte). Ticket #110.
Future<void> _refuserEnvoyer(String comment) async {
if (_submitting) return;
final pending = widget.dossier.parents
.where((p) => p.statut == 'en_attente' && p.id.trim().isNotEmpty)
.toList();
if (pending.isEmpty) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: const Text('Aucun compte en attente à refuser pour ce dossier.'),
backgroundColor: Colors.red.shade700,
),
);
return;
}
setState(() => _submitting = true);
try {
for (final p in pending) {
await UserService.refuseUser(p.id, comment: comment);
}
if (!mounted) return;
widget.onSuccess();
} catch (e) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(e is Exception
? e.toString().replaceFirst('Exception: ', '')
: 'Erreur'),
backgroundColor: Colors.red.shade700,
),
);
} finally {
if (mounted) setState(() => _submitting = false);
}
}
}