feat(front): selecteur couple enfant-nounou (#167)
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
/// Modèles du couple de garde (enfant ↔ AM) — ticket #167.
|
||||
/// Contrat backend #168 : GET /parents/me/couples-garde.
|
||||
|
||||
/// Identité minimale d'une personne du couple (enfant, AM ou parent).
|
||||
class CoupleMembre {
|
||||
final String id;
|
||||
final String? prenom;
|
||||
final String? nom;
|
||||
final String? photoUrl;
|
||||
|
||||
const CoupleMembre({
|
||||
required this.id,
|
||||
this.prenom,
|
||||
this.nom,
|
||||
this.photoUrl,
|
||||
});
|
||||
|
||||
/// Nom d'affichage : prénom seul si dispo, sinon « Prénom Nom », sinon repli.
|
||||
String displayName({String fallback = ''}) {
|
||||
final p = (prenom ?? '').trim();
|
||||
final n = (nom ?? '').trim();
|
||||
if (p.isNotEmpty && n.isNotEmpty) return '$p $n';
|
||||
if (p.isNotEmpty) return p;
|
||||
if (n.isNotEmpty) return n;
|
||||
return fallback;
|
||||
}
|
||||
|
||||
factory CoupleMembre.fromJson(Map<String, dynamic> json) {
|
||||
return CoupleMembre(
|
||||
id: (json['id'] ?? '').toString(),
|
||||
prenom: json['prenom']?.toString(),
|
||||
nom: json['nom']?.toString(),
|
||||
photoUrl: json['photo_url']?.toString(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Un couple de garde = placement actif enfant ↔ AM.
|
||||
class CoupleGarde {
|
||||
final String id;
|
||||
final CoupleMembre enfant;
|
||||
final CoupleMembre am;
|
||||
final bool courant;
|
||||
|
||||
const CoupleGarde({
|
||||
required this.id,
|
||||
required this.enfant,
|
||||
required this.am,
|
||||
this.courant = false,
|
||||
});
|
||||
|
||||
factory CoupleGarde.fromJson(Map<String, dynamic> json) {
|
||||
return CoupleGarde(
|
||||
id: (json['id'] ?? '').toString(),
|
||||
enfant: CoupleMembre.fromJson(
|
||||
Map<String, dynamic>.from(json['enfant'] ?? const {}),
|
||||
),
|
||||
am: CoupleMembre.fromJson(
|
||||
Map<String, dynamic>.from(json['am'] ?? const {}),
|
||||
),
|
||||
courant: json['courant'] == true,
|
||||
);
|
||||
}
|
||||
|
||||
CoupleGarde copyWith({bool? courant}) {
|
||||
return CoupleGarde(
|
||||
id: id,
|
||||
enfant: enfant,
|
||||
am: am,
|
||||
courant: courant ?? this.courant,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Réponse de l'API couples de garde (liste + id du couple courant).
|
||||
class CouplesGardeResponse {
|
||||
final List<CoupleGarde> couples;
|
||||
final String? coupleCourantId;
|
||||
|
||||
const CouplesGardeResponse({
|
||||
required this.couples,
|
||||
this.coupleCourantId,
|
||||
});
|
||||
|
||||
bool get isEmpty => couples.isEmpty;
|
||||
bool get isUnique => couples.length == 1;
|
||||
|
||||
/// Couple courant : celui marqué `courant`, sinon celui de [coupleCourantId],
|
||||
/// sinon le premier (repli implicite côté client, prévu par le back).
|
||||
CoupleGarde? get coupleCourant {
|
||||
if (couples.isEmpty) return null;
|
||||
for (final c in couples) {
|
||||
if (c.courant) return c;
|
||||
}
|
||||
if (coupleCourantId != null) {
|
||||
for (final c in couples) {
|
||||
if (c.id == coupleCourantId) return c;
|
||||
}
|
||||
}
|
||||
return couples.first;
|
||||
}
|
||||
|
||||
factory CouplesGardeResponse.fromJson(Map<String, dynamic> json) {
|
||||
final list = (json['couples'] as List?) ?? const [];
|
||||
return CouplesGardeResponse(
|
||||
couples: list
|
||||
.whereType<Map>()
|
||||
.map((e) => CoupleGarde.fromJson(Map<String, dynamic>.from(e)))
|
||||
.toList(),
|
||||
coupleCourantId: json['couple_courant_id']?.toString(),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user