fix(#173): layout bulle selon maquette (Du/Au + pilules larges)

Titre, dates en français sur deux lignes, actions coche/croix
en pilules pleine largeur côte à côte.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-01 17:10:41 +02:00
co-authored by Cursor
parent 535af3b146
commit 7705a14bb5
+84 -71
View File
@@ -340,30 +340,26 @@ class _BulleTile extends StatelessWidget {
required this.onRefuse,
});
String get _periode {
final d = card.dateDebut;
final f = card.dateFin;
if (d == null && f == null) return '';
if (d != null && f != null && d == f) return _fmt(d);
return '${_fmt(d)} → ${_fmt(f)}';
}
String _fmt(String? iso) {
String _fmtLong(String? iso) {
if (iso == null || iso.isEmpty) return '…';
final dt = DateTime.tryParse(iso);
if (dt == null) return iso;
const mois = [
'janvier',
'février',
'mars',
'avril',
'mai',
'juin',
'juillet',
'août',
'septembre',
'octobre',
'novembre',
'décembre',
];
final jj = dt.day.toString().padLeft(2, '0');
final mm = dt.month.toString().padLeft(2, '0');
return '$jj/$mm/${dt.year}';
}
/// Pas de « À traiter / Traité » : seulement un indice utile (refus / attente créateur).
String? get _indice {
if (card.statut == CarteStatut.refusee) return 'Refusé';
if (card.statut == CarteStatut.ouverte && card.isCreator) {
return 'En attente de réponse';
}
return null;
return '$jj ${mois[dt.month - 1]} ${dt.year}';
}
/// Arrêt maladie : reste opaque tant que la période n’est pas finie.
@@ -374,53 +370,61 @@ class _BulleTile extends StatelessWidget {
@override
Widget build(BuildContext context) {
final indice = _indice;
final memeJour = card.dateDebut != null &&
card.dateFin != null &&
card.dateDebut == card.dateFin;
return Opacity(
opacity: _attenuee ? 0.55 : 1,
child: Carte(
assetPath: card.backgroundAsset,
constraints: const BoxConstraints(minHeight: 96),
padding: const EdgeInsets.fromLTRB(20, 14, 20, 14),
constraints: const BoxConstraints(minHeight: 120),
padding: const EdgeInsets.fromLTRB(22, 16, 22, 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Text(
Text(
card.titreAffiche,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: GoogleFonts.merienda(
fontSize: 16,
fontSize: 18,
fontWeight: FontWeight.w700,
color: QuotidienTheme.ink,
),
),
),
if (indice != null)
if (card.dateDebut != null || card.dateFin != null) ...[
const SizedBox(height: 10),
if (memeJour)
Text(
indice,
'Le ${_fmtLong(card.dateDebut)}',
style: GoogleFonts.merriweather(
fontSize: 11,
color: QuotidienTheme.muted,
fontSize: 14,
color: QuotidienTheme.ink,
height: 1.35,
),
)
else ...[
Text(
'Du ${_fmtLong(card.dateDebut)}',
style: GoogleFonts.merriweather(
fontSize: 14,
color: QuotidienTheme.ink,
height: 1.35,
),
),
Text(
'Au ${_fmtLong(card.dateFin)}',
style: GoogleFonts.merriweather(
fontSize: 14,
color: QuotidienTheme.ink,
height: 1.35,
),
),
],
),
if (_periode.isNotEmpty) ...[
const SizedBox(height: 4),
Text(
_periode,
style: GoogleFonts.merriweather(
fontSize: 13,
color: QuotidienTheme.ink,
),
),
],
if (card.motif != null && card.motif!.trim().isNotEmpty) ...[
const SizedBox(height: 2),
const SizedBox(height: 6),
Text(
card.motif!,
maxLines: 2,
@@ -431,12 +435,13 @@ class _BulleTile extends StatelessWidget {
),
),
],
if (card.statut == CarteStatut.refusee &&
card.lastRefuseComment != null &&
card.lastRefuseComment!.trim().isNotEmpty) ...[
const SizedBox(height: 4),
if (card.statut == CarteStatut.refusee) ...[
const SizedBox(height: 6),
Text(
'Motif : ${card.lastRefuseComment}',
card.lastRefuseComment != null &&
card.lastRefuseComment!.trim().isNotEmpty
? 'Refusé — ${card.lastRefuseComment}'
: 'Refusé',
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: GoogleFonts.merriweather(
@@ -445,39 +450,50 @@ class _BulleTile extends StatelessWidget {
color: QuotidienTheme.ink,
),
),
] else if (card.statut == CarteStatut.ouverte &&
card.isCreator) ...[
const SizedBox(height: 6),
Text(
'En attente de réponse',
style: GoogleFonts.merriweather(
fontSize: 12,
color: QuotidienTheme.muted,
),
),
],
if (card.peutRepondre) ...[
const SizedBox(height: 10),
const SizedBox(height: 14),
if (busy)
const Align(
alignment: Alignment.centerRight,
const Center(
child: SizedBox(
width: 18,
height: 18,
width: 22,
height: 22,
child: CircularProgressIndicator(strokeWidth: 2),
),
)
else
else if (card.responseMode == CarteResponseMode.acceptRefuse)
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
if (card.responseMode ==
CarteResponseMode.acceptRefuse) ...[
_ActionPill(
Expanded(
child: _ActionPill(
asset: QuotidienTheme.pillTurquoiseAsset,
iconAsset: 'assets/images/coche.png',
tooltip: 'Accepter',
onTap: onAccept,
),
const SizedBox(width: 10),
_ActionPill(
),
const SizedBox(width: 12),
Expanded(
child: _ActionPill(
asset: QuotidienTheme.pillCorailAsset,
iconAsset: 'assets/images/red_cross2.png',
tooltip: 'Refuser',
onTap: onRefuse,
),
),
],
if (card.responseMode == CarteResponseMode.ack)
)
else if (card.responseMode == CarteResponseMode.ack)
_ActionPill(
asset: QuotidienTheme.pillTurquoiseAsset,
iconAsset: 'assets/images/coche.png',
@@ -485,8 +501,6 @@ class _BulleTile extends StatelessWidget {
onTap: onAck,
),
],
),
],
],
),
),
@@ -494,7 +508,7 @@ class _BulleTile extends StatelessWidget {
}
}
/// Bouton pilule dessiné (fond pastel + coche / croix).
/// Bouton pilule dessiné pleine largeur (fond pastel + coche / croix).
class _ActionPill extends StatelessWidget {
final String asset;
final String iconAsset;
@@ -514,10 +528,9 @@ class _ActionPill extends StatelessWidget {
message: tooltip,
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(20),
borderRadius: BorderRadius.circular(24),
child: SizedBox(
width: 52,
height: 32,
height: 40,
child: Stack(
alignment: Alignment.center,
children: [
@@ -526,8 +539,8 @@ class _ActionPill extends StatelessWidget {
),
Image.asset(
iconAsset,
height: 18,
width: 18,
height: 22,
width: 22,
fit: BoxFit.contain,
),
],