import 'package:p_tits_pas/models/assistante_maternelle_model.dart'; import 'package:p_tits_pas/models/parent_model.dart'; import 'package:p_tits_pas/utils/name_format_utils.dart'; /// Ligne de liste unifiée dossiers (famille ou AM) — ticket #153. enum DossierListType { famille, assistanteMaternelle } class DossierListItem { final DossierListType type; final String numeroDossier; final String libelle; final List emails; final String? statut; /// Photo profil (AM) — affichée à la place de l’icône si présente. final String? photoUrl; const DossierListItem({ required this.type, required this.numeroDossier, required this.libelle, this.emails = const [], this.statut, this.photoUrl, }); bool get isFamille => type == DossierListType.famille; bool get isAm => type == DossierListType.assistanteMaternelle; String get typeLabel => isFamille ? 'Famille' : 'AM'; /// Sous-titre carte : `NOM Prénom` ou `NOM Prénom - NOM Prénom`. String get namesLine => libelle; String get emailsLine => emails.where((e) => e.trim().isNotEmpty).join(' · '); /// Titre carte : numéro de dossier seul. String get titleLine => numeroDossier; bool matchesQuery(String query) { final q = query.trim().toLowerCase(); if (q.isEmpty) return true; if (numeroDossier.toLowerCase().contains(q)) return true; if (libelle.toLowerCase().contains(q)) return true; for (final e in emails) { if (e.toLowerCase().contains(q)) return true; } return false; } /// Une ligne par `numero_dossier` (foyer dédupliqué). static List fromParents(List parents) { final byDossier = >{}; for (final p in parents) { final num = (p.user.numeroDossier ?? '').trim(); if (num.isEmpty) continue; byDossier.putIfAbsent(num, () => []).add(p); } final items = []; for (final entry in byDossier.entries) { final seenIds = {}; final names = []; final emails = []; final statuts = []; void consider( String? id, String? nom, String? prenom, String? email, String? statut, ) { final uid = (id ?? '').trim(); if (uid.isEmpty || !seenIds.add(uid)) return; final label = formatDossierPersonLabel( nom: nom, prenom: prenom, email: email, ); if (label.isNotEmpty) names.add(label); final e = (email ?? '').trim(); if (e.isNotEmpty) emails.add(e); final s = (statut ?? '').trim(); if (s.isNotEmpty) statuts.add(s); } for (final p in entry.value) { consider( p.user.id, p.user.nom, p.user.prenom, p.user.email, p.user.statut, ); final co = p.coParent; if (co != null) { consider(co.id, co.nom, co.prenom, co.email, co.statut); } } items.add( DossierListItem( type: DossierListType.famille, numeroDossier: entry.key, libelle: names.isNotEmpty ? names.join(' - ') : 'Famille', emails: emails, statut: _preferStatut(statuts), ), ); } return items; } static List fromAssistantes( List ams, ) { final byDossier = {}; for (final am in ams) { final num = (am.user.numeroDossier ?? '').trim(); if (num.isEmpty) continue; byDossier.putIfAbsent(num, () => am); } return byDossier.entries.map((e) { final u = e.value.user; final name = formatDossierPersonLabel( nom: u.nom, prenom: u.prenom, email: u.email, ); final photo = (u.photoUrl ?? '').trim(); return DossierListItem( type: DossierListType.assistanteMaternelle, numeroDossier: e.key, libelle: name.isNotEmpty ? name : 'AM', emails: u.email.trim().isEmpty ? const [] : [u.email.trim()], statut: u.statut?.trim(), photoUrl: photo.isEmpty ? null : photo, ); }).toList(); } /// Priorité affichage : en_attente > suspendu > refuse > actif > autre. static String? _preferStatut(List raw) { if (raw.isEmpty) return null; const order = ['en_attente', 'suspendu', 'refuse', 'actif']; for (final wanted in order) { for (final s in raw) { if (s.toLowerCase() == wanted) return s; } } return raw.first; } } /// Affichage carte dossier : `NOM Prénom` (repli email). String formatDossierPersonLabel({ String? nom, String? prenom, String? email, }) { final n = (nom ?? '').trim().toUpperCase(); final p = formatPersonNameCase(prenom ?? ''); if (n.isNotEmpty && p.isNotEmpty) return '$n $p'; if (n.isNotEmpty) return n; if (p.isNotEmpty) return p; return (email ?? '').trim(); } /// Reformate un libellé famille API (`A & B` / `Famille …`) en `NOM Prénom - …`. String formatDossierFamilyNamesLine(String libelle) { var raw = libelle.trim(); if (raw.isEmpty) return ''; raw = raw.replaceFirst(RegExp(r'^famille\s+', caseSensitive: false), ''); raw = raw .replaceAll(RegExp(r'\s+&\s+'), ' - ') .replaceAll(RegExp(r'\s+et\s+', caseSensitive: false), ' - '); final parts = raw .split(RegExp(r'\s+-\s+')) .map((part) => _formatLoosePersonSegment(part.trim())) .where((s) => s.isNotEmpty) .toList(); return parts.join(' - '); } /// Segment libre type « martin sophie » ou « DURAND Amélie » → `NOM Prénom`. String _formatLoosePersonSegment(String segment) { final words = segment.split(RegExp(r'\s+')).where((w) => w.isNotEmpty).toList(); if (words.isEmpty) return ''; if (words.length == 1) return words.first.toUpperCase(); // Convention affichage : premier mot = NOM, reste = prénom(s). final nom = words.first.toUpperCase(); final prenom = formatPersonNameCase(words.sublist(1).join(' ')); return '$nom $prenom'; }