feat(#206,#207): cartes rose soin_enfant (backend + UI)

Closes #206

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-09 16:49:37 +02:00
co-authored by Cursor
parent 9a74c8eefe
commit f7ae13a909
20 changed files with 1464 additions and 77 deletions
+73 -1
View File
@@ -13,6 +13,7 @@ enum CarteStatut {
switch (raw) {
case 'refusee':
return CarteStatut.refusee;
case 'fermee':
case 'traitee':
return CarteStatut.traitee;
case 'ouverte':
@@ -110,6 +111,57 @@ class CarteBulle {
String? get dateFin => payload['date_fin']?.toString();
String? get motif => payload['motif']?.toString();
/// Heure de relevé (payload soins), format API `HH:mm`.
String? get heureReleve => payload['heure_releve']?.toString();
/// Heure affichable `18h19` (depuis `heure_releve`).
String? get heureReleveAffichee {
final h = heureReleve?.trim();
if (h == null || h.isEmpty) return null;
final parts = h.split(':');
if (parts.length < 2) return h;
return '${parts[0]}h${parts[1]}';
}
/// Ligne détail soin (sans date/heure) — sous la ligne agenda.
String? get detailSoin {
if (!isSoinEnfant) return null;
final st = payload['sous_type']?.toString();
if (st == 'fievre') {
final t = payload['temperature_c'];
final med = payload['medicament_donne']?.toString().trim();
String? temp;
if (t is num) {
temp = t.toStringAsFixed(1).replaceAll('.', ',');
} else if (t != null) {
temp = t.toString().replaceAll('.', ',');
}
if (temp == null || temp.isEmpty) return motif;
var s = '$temp °C';
if (med != null && med.isNotEmpty) {
s += ' (Médicament : $med)';
}
return s;
}
if (st == 'bobo') {
final nature = payload['bobo_nature']?.toString() ?? 'bobo';
final desc = payload['description']?.toString().trim();
if (desc != null && desc.isNotEmpty) {
return 'Bobo ($nature) : $desc';
}
return 'Bobo ($nature)';
}
if (st == 'medicament') {
final nom = payload['medicament_nom']?.toString().trim();
final ctx = payload['contexte']?.toString().trim();
final label =
(nom != null && nom.isNotEmpty) ? 'Médicament ($nom)' : 'Médicament';
if (ctx != null && ctx.isNotEmpty) return '$label : $ctx';
return label;
}
return motif;
}
/// Motif de refus : réponse API, sinon motif payload (co-parent / reload).
String? get motifRefus {
final fromResponse = lastRefuseComment?.trim();
@@ -139,6 +191,21 @@ class CarteBulle {
bool get isArretMaladie => typeCode == 'arret_maladie_am';
bool get isCongeAm => typeCode == 'conge_am';
bool get isSoinEnfant => typeCode == 'soin_enfant';
bool get isAbsenceEnfant =>
typeCode == 'absence_enfant' || typeCode == 'absence_enfant_modif';
/// Info seule (`response_mode: none`) : créée déjà `traitee`, mais reste
/// pertinente dans la file tant que la période n’est pas finie.
bool get restePleineDansFile {
if (statut == CarteStatut.ouverte) return true;
if (statut != CarteStatut.traitee) return false;
if (!periodeEncoreActive) return false;
// Arrêt maladie ack : opaque jusqu’à date_fin.
if (isArretMaladie) return true;
// Absence (et autres infos) : pas d’atténuation tant que période active.
if (responseMode == CarteResponseMode.none) return true;
return false;
}
/// Icône dessin (haut droite) selon le type de bulle.
String? get iconAsset {
@@ -150,7 +217,12 @@ class CarteBulle {
case 'absence_enfant':
case 'absence_enfant_modif':
return 'assets/images/sac_lapin.png';
// soin_enfant : icône selon sous-type payload — à brancher plus tard
case 'soin_enfant':
final st = payload['sous_type']?.toString();
if (st == 'fievre') return 'assets/images/thermo_verre.png';
if (st == 'bobo') return 'assets/images/pansement.png';
if (st == 'medicament') return 'assets/images/flacon.png';
return null;
default:
return null;
}