feat(#175,#176): modale générique Ajouter une carte + CTA pilule

Creation absence (parent) et conge/arret (AM) via une seule modale
Material, catalogue UI extensible, calendrier de periode compact.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-05 22:36:29 +02:00
co-authored by Cursor
parent 5d36b0f4c1
commit a36e9ef176
8 changed files with 677 additions and 32 deletions
+15
View File
@@ -139,6 +139,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 {
+110
View File
@@ -0,0 +1,110 @@
/// Configuration UI d'un type de carte — catalogue front (#175).
/// Les types émissibles viennent de l'API ; ce fichier affine libellés / hints.
enum CartePeriodeMode {
/// Plage date_debut → date_fin.
range,
/// Une seule date (début = fin).
singleDay,
}
class CarteSousTypeUi {
final String code;
final String label;
const CarteSousTypeUi({required this.code, required this.label});
}
class CarteTypeUi {
final String typeCode;
final String label;
final String motifHint;
final bool motifRequired;
final CartePeriodeMode periodeMode;
final List<CarteSousTypeUi> sousTypes;
const CarteTypeUi({
required this.typeCode,
required this.label,
this.motifHint = 'Motif (optionnel)',
this.motifRequired = false,
this.periodeMode = CartePeriodeMode.range,
this.sousTypes = const [],
});
static const _catalog = <String, CarteTypeUi>{
'absence_enfant': CarteTypeUi(
typeCode: 'absence_enfant',
label: 'Notification d’absence',
motifHint: 'Motif de l’absence (optionnel)',
),
// absence_enfant_modif : hors scope cartes — via agenda uniquement.
'conge_am': CarteTypeUi(
typeCode: 'conge_am',
label: 'Demande de congés',
motifHint: 'Précisions (optionnel)',
),
'arret_maladie_am': CarteTypeUi(
typeCode: 'arret_maladie_am',
label: 'Arrêt maladie',
motifHint: 'Précisions (optionnel)',
),
// Point d’extension #soins — visible dès que le back l’émet.
'soin_enfant': CarteTypeUi(
typeCode: 'soin_enfant',
label: 'Soin / bobo',
motifHint: 'Décrivez le soin ou le bobo',
motifRequired: true,
periodeMode: CartePeriodeMode.singleDay,
sousTypes: [
CarteSousTypeUi(code: 'temperature', label: 'Température'),
CarteSousTypeUi(code: 'bobo', label: 'Bobo'),
CarteSousTypeUi(code: 'medicament', label: 'Médicament'),
CarteSousTypeUi(code: 'autre', label: 'Autre'),
],
),
};
/// Fusionne un type API avec le catalogue ; fallback générique sinon.
static CarteTypeUi resolve({
required String typeCode,
String? apiTitre,
}) {
final known = _catalog[typeCode];
if (known != null) return known;
final titre = (apiTitre ?? '').trim();
return CarteTypeUi(
typeCode: typeCode,
label: titre.isNotEmpty ? titre : typeCode,
motifHint: 'Motif (optionnel)',
);
}
}
/// Type SYSTEM émissible renvoyé par GET /cards/types.
class CarteTypeEmissible {
final String code;
final String titre;
final String? responseMode;
final CarteTypeUi ui;
const CarteTypeEmissible({
required this.code,
required this.titre,
this.responseMode,
required this.ui,
});
factory CarteTypeEmissible.fromJson(Map<String, dynamic> json) {
final code = (json['code'] ?? '').toString();
final titre = (json['titre'] ?? code).toString();
return CarteTypeEmissible(
code: code,
titre: titre,
responseMode: json['response_mode']?.toString(),
ui: CarteTypeUi.resolve(typeCode: code, apiTitre: titre),
);
}
String get label => ui.label;
}