Squash depuis develop : POST /enfants staff + parent_user_id, photo multipart, modale création + sélection famille, fixes UX/birth_date. Co-authored-by: Cursor <cursoragent@cursor.com>
128 lines
4.0 KiB
Dart
128 lines
4.0 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:p_tits_pas/models/parent_model.dart';
|
|
import 'package:p_tits_pas/services/user_service.dart';
|
|
import 'package:p_tits_pas/widgets/admin/common/admin_select_list_modal.dart';
|
|
import 'package:p_tits_pas/widgets/admin/common/admin_user_card.dart';
|
|
|
|
/// Foyer / famille sélectionnable pour rattacher un nouvel enfant (#132).
|
|
class AdminFamilleFoyer {
|
|
/// Parent pivot pour `POST /enfants` (`parent_user_id`).
|
|
final String pivotParentUserId;
|
|
final String? numeroDossier;
|
|
final String displayTitle;
|
|
final List<String> parentNames;
|
|
|
|
const AdminFamilleFoyer({
|
|
required this.pivotParentUserId,
|
|
required this.displayTitle,
|
|
required this.parentNames,
|
|
this.numeroDossier,
|
|
});
|
|
|
|
String get subtitle {
|
|
final parts = <String>[];
|
|
final dossier = (numeroDossier ?? '').trim();
|
|
if (dossier.isNotEmpty) parts.add('Dossier $dossier');
|
|
if (parentNames.isNotEmpty) {
|
|
parts.add('Responsables : ${parentNames.join(', ')}');
|
|
}
|
|
return parts.join(' ');
|
|
}
|
|
}
|
|
|
|
/// Construit la liste des foyers uniques à partir de `GET /parents`.
|
|
List<AdminFamilleFoyer> buildFamilleFoyers(List<ParentModel> parents) {
|
|
final seenDossiers = <String>{};
|
|
final seenUserIds = <String>{};
|
|
final foyers = <AdminFamilleFoyer>[];
|
|
|
|
for (final p in parents) {
|
|
final dossier = (p.user.numeroDossier ?? '').trim();
|
|
if (dossier.isNotEmpty) {
|
|
if (seenDossiers.contains(dossier)) continue;
|
|
seenDossiers.add(dossier);
|
|
} else if (seenUserIds.contains(p.user.id)) {
|
|
continue;
|
|
}
|
|
|
|
seenUserIds.add(p.user.id);
|
|
final co = p.coParent;
|
|
if (co != null) seenUserIds.add(co.id);
|
|
|
|
final names = <String>[
|
|
if (p.user.fullName.trim().isNotEmpty) p.user.fullName.trim(),
|
|
if (co != null && co.fullName.trim().isNotEmpty) co.fullName.trim(),
|
|
];
|
|
|
|
final title = dossier.isNotEmpty
|
|
? 'Dossier $dossier'
|
|
: (names.isNotEmpty ? names.first : 'Famille');
|
|
|
|
foyers.add(
|
|
AdminFamilleFoyer(
|
|
pivotParentUserId: p.user.id,
|
|
numeroDossier: dossier.isNotEmpty ? dossier : null,
|
|
displayTitle: title,
|
|
parentNames: names,
|
|
),
|
|
);
|
|
}
|
|
|
|
foyers.sort(
|
|
(a, b) => a.displayTitle.toLowerCase().compareTo(b.displayTitle.toLowerCase()),
|
|
);
|
|
return foyers;
|
|
}
|
|
|
|
/// Sélection d'une famille / dossier pour créer un enfant — ticket #132.
|
|
class AdminSelectFamilleModal {
|
|
AdminSelectFamilleModal._();
|
|
|
|
static Future<AdminFamilleFoyer?> show(
|
|
BuildContext context, {
|
|
String title = 'Choisir une famille',
|
|
}) {
|
|
return AdminSelectListModal.show<AdminFamilleFoyer>(
|
|
context,
|
|
title: title,
|
|
searchHint: 'Rechercher par dossier, nom…',
|
|
emptyMessage: 'Aucune famille disponible',
|
|
noResultsMessage: 'Aucun résultat pour cette recherche',
|
|
loadItems: () async {
|
|
final parents = await UserService.getParents();
|
|
return buildFamilleFoyers(parents);
|
|
},
|
|
matchesQuery: (f, q) {
|
|
final dossier = (f.numeroDossier ?? '').toLowerCase();
|
|
final title = f.displayTitle.toLowerCase();
|
|
final names = f.parentNames.join(' ').toLowerCase();
|
|
return dossier.contains(q) || title.contains(q) || names.contains(q);
|
|
},
|
|
itemBuilder: (context, f, onSelect) {
|
|
return AdminUserCard(
|
|
title: f.displayTitle,
|
|
fallbackIcon: Icons.family_restroom,
|
|
subtitleLines: [
|
|
if (f.parentNames.isNotEmpty) f.parentNames.join(', '),
|
|
if ((f.numeroDossier ?? '').isNotEmpty)
|
|
'Dossier ${f.numeroDossier}',
|
|
],
|
|
onCardTap: onSelect,
|
|
margin: const EdgeInsets.only(bottom: 4),
|
|
contentPadding: const EdgeInsets.symmetric(
|
|
horizontal: 10,
|
|
vertical: 5,
|
|
),
|
|
actions: [
|
|
IconButton(
|
|
icon: const Icon(Icons.check_circle_outline),
|
|
tooltip: 'Choisir',
|
|
onPressed: onSelect,
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|