Files
petitspas/frontend/lib/models/carte_bulle.dart
T
jmartinandCursor 6683139429 feat(#173): ajouter la teinte aqua aux cartes (V + H)
Enums, mappings formulaires / bulles / enfants, assets inclus.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-01 17:10:41 +02:00

200 lines
5.8 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:p_tits_pas/models/card_assets.dart';
/// Statut d'une instance de carte (file d'attention) — contrat #194 / #173.
enum CarteStatut {
ouverte('ouverte'),
refusee('refusee'),
traitee('traitee');
final String apiValue;
const CarteStatut(this.apiValue);
static CarteStatut fromApi(String? raw) {
switch (raw) {
case 'refusee':
return CarteStatut.refusee;
case 'traitee':
return CarteStatut.traitee;
case 'ouverte':
default:
return CarteStatut.ouverte;
}
}
}
/// Mode de réponse attendu selon le type SYSTEM.
enum CarteResponseMode {
none('none'),
ack('ack'),
acceptRefuse('accept_refuse');
final String apiValue;
const CarteResponseMode(this.apiValue);
static CarteResponseMode fromApi(String? raw) {
switch (raw) {
case 'ack':
return CarteResponseMode.ack;
case 'accept_refuse':
return CarteResponseMode.acceptRefuse;
case 'none':
default:
return CarteResponseMode.none;
}
}
}
/// Bulle / carte de la file d'attention.
class CarteBulle {
final String id;
final String typeCode;
final String titre;
final String? couleur;
final String idPlacement;
final String? idAbsence;
final String operation;
final CarteStatut statut;
final Map<String, dynamic> payload;
final DateTime? purgeAt;
final String? creePar;
final bool isCreator;
final CarteResponseMode responseMode;
final String? lastRefuseComment;
final DateTime? creeLe;
final DateTime? modifieLe;
const CarteBulle({
required this.id,
required this.typeCode,
required this.titre,
this.couleur,
required this.idPlacement,
this.idAbsence,
this.operation = 'create',
required this.statut,
this.payload = const {},
this.purgeAt,
this.creePar,
this.isCreator = false,
this.responseMode = CarteResponseMode.none,
this.lastRefuseComment,
this.creeLe,
this.modifieLe,
});
String? get dateDebut => payload['date_debut']?.toString();
String? get dateFin => payload['date_fin']?.toString();
String? get motif => payload['motif']?.toString();
/// Libellé UI (sémantique produit) — pas le titre brut API.
String get titreAffiche {
switch (typeCode) {
case 'conge_am':
return 'Demande de congés';
case 'arret_maladie_am':
return 'Arrêt maladie';
case 'absence_enfant':
case 'absence_enfant_modif':
return 'Notification d’absence';
default:
return titre;
}
}
bool get isArretMaladie => typeCode == 'arret_maladie_am';
bool get isCongeAm => typeCode == 'conge_am';
/// Icône dessin (haut droite) selon le type de bulle.
String? get iconAsset {
switch (typeCode) {
case 'conge_am':
return 'assets/images/cartes/icon_carte_conges2.png';
case 'arret_maladie_am':
return 'assets/images/cartes/icon_carte_maladie.png';
case 'absence_enfant':
case 'absence_enfant_modif':
return 'assets/images/cartes/icon_carte_absence.png';
default:
return null;
}
}
/// Fin de période (jour inclus). Null si dates absentes / invalides.
DateTime? get dateFinJour {
final raw = dateFin;
if (raw == null || raw.isEmpty) return null;
final dt = DateTime.tryParse(raw);
if (dt == null) return null;
return DateTime(dt.year, dt.month, dt.day);
}
/// Vrai tant que la date de fin n’est pas dépassée (jour courant inclus).
bool get periodeEncoreActive {
final fin = dateFinJour;
if (fin == null) return true;
final today = DateTime.now();
final todayDay = DateTime(today.year, today.month, today.day);
return !todayDay.isAfter(fin);
}
/// Asset horizontal pastel (palette `assets/cards/` uniquement).
String get backgroundAsset {
final key = (couleur ?? '').trim().toLowerCase();
switch (key) {
case 'red':
return CardColorHorizontal.red.path;
case 'pink':
return CardColorHorizontal.pink.path;
case 'peach':
return CardColorHorizontal.peach.path;
case 'lime':
return CardColorHorizontal.lime.path;
case 'lavender':
return CardColorHorizontal.lavender.path;
case 'green':
return CardColorHorizontal.green.path;
case 'blue':
return CardColorHorizontal.blue.path;
case 'aqua':
return CardColorHorizontal.aqua.path;
default:
return CardColorHorizontal.peach.path;
}
}
/// Le destinataire peut répondre (pas le créateur, carte ouverte).
bool get peutRepondre =>
!isCreator &&
statut == CarteStatut.ouverte &&
responseMode != CarteResponseMode.none;
factory CarteBulle.fromJson(Map<String, dynamic> json) {
final rawPayload = json['payload'];
return CarteBulle(
id: (json['id'] ?? '').toString(),
typeCode: (json['type_code'] ?? '').toString(),
titre: (json['titre'] ?? json['type_code'] ?? 'Carte').toString(),
couleur: json['couleur']?.toString(),
idPlacement: (json['id_placement'] ?? '').toString(),
idAbsence: json['id_absence']?.toString(),
operation: (json['operation'] ?? 'create').toString(),
statut: CarteStatut.fromApi(json['statut']?.toString()),
payload: rawPayload is Map
? Map<String, dynamic>.from(rawPayload)
: const {},
purgeAt: _parseDate(json['purge_at']),
creePar: json['cree_par']?.toString(),
isCreator: json['is_creator'] == true,
responseMode: CarteResponseMode.fromApi(json['response_mode']?.toString()),
lastRefuseComment: json['last_refuse_comment']?.toString(),
creeLe: _parseDate(json['cree_le']),
modifieLe: _parseDate(json['modifie_le']),
);
}
static DateTime? _parseDate(dynamic v) {
if (v == null) return null;
return DateTime.tryParse(v.toString());
}
}