import 'package:flutter/material.dart'; import 'package:p_tits_pas/models/dossier_list_item.dart'; /// Choix pour le dernier enfant d’un dossier famille (#160). enum DernierEnfantSuppressionChoice { enfantSeul, dossierAussi, } /// Ligne d’impact (une personne / une fiche). class SuppressionPersonLine { final String label; final IconData icon; final String? role; const SuppressionPersonLine({ required this.label, this.icon = Icons.person_outline, this.role, }); factory SuppressionPersonLine.parent(String label) => SuppressionPersonLine( label: label, icon: Icons.supervisor_account_outlined, role: 'Parent', ); factory SuppressionPersonLine.enfant(String label) => SuppressionPersonLine( label: label, icon: Icons.child_care_outlined, role: 'Enfant', ); factory SuppressionPersonLine.am(String label) => SuppressionPersonLine( label: label, icon: Icons.face, role: 'AM', ); factory SuppressionPersonLine.gestionnaire(String label) => SuppressionPersonLine( label: label, icon: Icons.assignment_ind_outlined, role: 'Gestionnaire', ); factory SuppressionPersonLine.administrateur(String label) => SuppressionPersonLine( label: label, icon: Icons.manage_accounts_outlined, role: 'Admin', ); factory SuppressionPersonLine.relais(String label) => SuppressionPersonLine( label: label, icon: Icons.apartment_outlined, role: 'Relais', ); } /// Widget unique pour toutes les boîtes de confirmation de suppression (#160). class SuppressionConfirmDialog extends StatelessWidget { final String title; final String? subtitle; final String? message; final List people; final List footnotes; final List actions; const SuppressionConfirmDialog({ super.key, required this.title, this.subtitle, this.message, this.people = const [], this.footnotes = const [], required this.actions, }); /// Variante oui/non standard (Annuler / Supprimer). static SuppressionConfirmDialog yesNo({ required String title, String? subtitle, String? message, List people = const [], List footnotes = const [], String confirmLabel = 'Supprimer', required VoidCallback onCancel, required VoidCallback onConfirm, }) { return SuppressionConfirmDialog( title: title, subtitle: subtitle, message: message, people: people, footnotes: footnotes, actions: [ TextButton(onPressed: onCancel, child: const Text('Annuler')), FilledButton( onPressed: onConfirm, style: _dangerButtonStyle, child: Text(confirmLabel), ), ], ); } static ButtonStyle get _dangerButtonStyle => FilledButton.styleFrom( backgroundColor: Colors.red.shade700, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 10), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), ), ); @override Widget build(BuildContext context) { final theme = Theme.of(context); return AlertDialog( backgroundColor: const Color(0xFFF7F2FB), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), titlePadding: const EdgeInsets.fromLTRB(24, 20, 24, 0), contentPadding: const EdgeInsets.fromLTRB(24, 12, 24, 8), actionsPadding: const EdgeInsets.fromLTRB(16, 4, 16, 14), title: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( width: 40, height: 40, decoration: BoxDecoration( color: Colors.red.shade50, borderRadius: BorderRadius.circular(12), ), child: Icon( Icons.delete_outline, color: Colors.red.shade700, size: 22, ), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: theme.textTheme.titleLarge?.copyWith( fontWeight: FontWeight.w700, color: const Color(0xFF2F2F2F), fontSize: 20, ), ), if ((subtitle ?? '').trim().isNotEmpty) ...[ const SizedBox(height: 4), Text( subtitle!.trim(), style: theme.textTheme.bodyMedium?.copyWith( color: const Color(0xFF6D4EA1), fontWeight: FontWeight.w600, ), ), ], ], ), ), ], ), content: SizedBox( width: 420, child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ if ((message ?? '').trim().isNotEmpty) ...[ Text( message!.trim(), style: theme.textTheme.bodyMedium?.copyWith( color: Colors.black87, height: 1.35, ), ), if (people.isNotEmpty || footnotes.isNotEmpty) const SizedBox(height: 12), ], if (people.isNotEmpty) ...[ Container( decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.9), borderRadius: BorderRadius.circular(12), border: Border.all(color: const Color(0xFFE5D8F2)), ), padding: const EdgeInsets.symmetric(vertical: 4), child: Column( children: [ for (var i = 0; i < people.length; i++) ...[ if (i > 0) Divider( height: 1, indent: 44, endIndent: 12, color: Colors.grey.shade200, ), _SuppressionPersonRow(line: people[i]), ], ], ), ), if (footnotes.isNotEmpty) const SizedBox(height: 12), ], for (final note in footnotes) Padding( padding: const EdgeInsets.only(bottom: 6), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.only(top: 2), child: Icon( Icons.info_outline, size: 16, color: Colors.orange.shade800, ), ), const SizedBox(width: 8), Expanded( child: Text( note, style: theme.textTheme.bodySmall?.copyWith( color: Colors.black54, height: 1.35, ), ), ), ], ), ), ], ), ), actions: actions, ); } } class _SuppressionPersonRow extends StatelessWidget { final SuppressionPersonLine line; const _SuppressionPersonRow({required this.line}); @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), child: Row( children: [ Icon(line.icon, size: 18, color: const Color(0xFF6D4EA1)), const SizedBox(width: 10), Expanded( child: Text( line.label, style: const TextStyle( fontWeight: FontWeight.w600, fontSize: 14, color: Color(0xFF2F2F2F), ), maxLines: 1, overflow: TextOverflow.ellipsis, ), ), if ((line.role ?? '').trim().isNotEmpty) ...[ const SizedBox(width: 8), Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), decoration: BoxDecoration( color: const Color(0xFFEDE5FA), borderRadius: BorderRadius.circular(10), ), child: Text( line.role!.trim(), style: const TextStyle( fontSize: 11, fontWeight: FontWeight.w600, color: Color(0xFF6D4EA1), ), ), ), ], ], ), ); } } /// Affiche [SuppressionConfirmDialog] et renvoie `true` si confirmé. Future showSuppressionConfirmDialog( BuildContext context, { required String title, String? subtitle, String? message, List people = const [], List footnotes = const [], String confirmLabel = 'Supprimer', }) async { final result = await showDialog( context: context, builder: (ctx) => SuppressionConfirmDialog.yesNo( title: title, subtitle: subtitle, message: message, people: people, footnotes: footnotes, confirmLabel: confirmLabel, onCancel: () => Navigator.of(ctx).pop(false), onConfirm: () => Navigator.of(ctx).pop(true), ), ); return result == true; } /// Confirmation delete dossier famille / AM avec liste nominative. Future showDossierSuppressionConfirmDialog( BuildContext context, { required String numeroDossier, required bool isFamille, required List people, String? fallbackSummary, }) { final num = numeroDossier.trim(); if (isFamille) { return showSuppressionConfirmDialog( context, title: 'Supprimer le dossier', subtitle: 'Dossier famille $num', people: people, footnotes: people.isEmpty && (fallbackSummary ?? '').isNotEmpty ? [fallbackSummary!] : const [ 'Tous les comptes et fiches listés seront définitivement ' 'supprimés.', 'Les placements AM des enfants seront clos.', ], ); } return showSuppressionConfirmDialog( context, title: 'Supprimer le dossier', subtitle: 'Dossier AM $num', people: people, footnotes: const [ 'Le compte et le dossier AM seront supprimés.', 'Les enfants accueillis ne seront pas supprimés ' '(placements clos).', ], ); } /// Dialog dernier enfant : deux actions métier. Future showDernierEnfantSuppressionDialog( BuildContext context, { required String enfantName, required String familleLabel, required String numeroDossier, String? amLabel, }) { final am = (amLabel ?? '').trim(); return showDialog( context: context, builder: (ctx) => SuppressionConfirmDialog( title: 'Dernier enfant du dossier', subtitle: 'Dossier $numeroDossier' '${familleLabel.isEmpty ? '' : ' · $familleLabel'}', people: [SuppressionPersonLine.enfant(enfantName)], footnotes: [ '« Enfant seulement » : le dossier reste sans enfant.', '« Dossier aussi » : parents et dossier sont également ' 'supprimés.', if (am.isNotEmpty) 'Le placement chez $am sera clos.', ], actions: [ TextButton( onPressed: () => Navigator.of(ctx).pop(), child: const Text('Annuler'), ), OutlinedButton( onPressed: () => Navigator.of(ctx).pop( DernierEnfantSuppressionChoice.enfantSeul, ), child: const Text('Enfant seulement'), ), FilledButton( onPressed: () => Navigator.of(ctx).pop( DernierEnfantSuppressionChoice.dossierAussi, ), style: SuppressionConfirmDialog._dangerButtonStyle, child: const Text('Dossier aussi'), ), ], ), ); } /// Notes d’impact pour suppression d’un enfant (AM optionnelle). List enfantSuppressionFootnotes({ required String? numeroDossier, required String familleLabel, String? amLabel, }) { final notes = []; final num = (numeroDossier ?? '').trim(); final famille = familleLabel.trim(); final am = (amLabel ?? '').trim(); if (num.isEmpty) { notes.add('Supprimer définitivement cette fiche enfant.'); } else { notes.add( 'L’enfant sera retiré du dossier de ' '${famille.isEmpty ? 'la famille' : famille}.', ); } if (am.isNotEmpty) { notes.add('Le placement chez $am sera clos.'); } return notes; } /// Bouton poubelle compact pour les cartes liste. Widget suppressionIconButton({ required VoidCallback? onPressed, String tooltip = 'Supprimer', }) { return IconButton( icon: Icon(Icons.delete_outline, color: Colors.red.shade700), tooltip: tooltip, onPressed: onPressed, ); } /// Construit les lignes parents / enfants depuis un dossier unifié. List suppressionPeopleFromDossier({ required bool isFamille, required List<({String nom, String prenom, String email})> parents, required List<({String nom, String prenom})> enfants, String? amName, }) { final lines = []; if (isFamille) { for (final p in parents) { final label = formatDossierPersonLabel( nom: p.nom, prenom: p.prenom, email: p.email, ); if (label.isEmpty) continue; lines.add(SuppressionPersonLine.parent(label)); } for (final e in enfants) { final label = formatDossierPersonLabel(nom: e.nom, prenom: e.prenom); if (label.isEmpty) continue; lines.add(SuppressionPersonLine.enfant(label)); } } else if ((amName ?? '').trim().isNotEmpty) { lines.add(SuppressionPersonLine.am(amName!.trim())); } return lines; }