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:
@@ -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(
|
||||
card.titreAffiche,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: GoogleFonts.merienda(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: QuotidienTheme.ink,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (indice != null)
|
||||
Text(
|
||||
indice,
|
||||
style: GoogleFonts.merriweather(
|
||||
fontSize: 11,
|
||||
color: QuotidienTheme.muted,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (_periode.isNotEmpty) ...[
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
_periode,
|
||||
style: GoogleFonts.merriweather(
|
||||
fontSize: 13,
|
||||
color: QuotidienTheme.ink,
|
||||
),
|
||||
Text(
|
||||
card.titreAffiche,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: GoogleFonts.merienda(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: QuotidienTheme.ink,
|
||||
),
|
||||
),
|
||||
if (card.dateDebut != null || card.dateFin != null) ...[
|
||||
const SizedBox(height: 10),
|
||||
if (memeJour)
|
||||
Text(
|
||||
'Le ${_fmtLong(card.dateDebut)}',
|
||||
style: GoogleFonts.merriweather(
|
||||
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 (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,46 +450,55 @@ 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)
|
||||
_ActionPill(
|
||||
asset: QuotidienTheme.pillTurquoiseAsset,
|
||||
iconAsset: 'assets/images/coche.png',
|
||||
tooltip: 'Bien reçu',
|
||||
onTap: onAck,
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
else if (card.responseMode == CarteResponseMode.ack)
|
||||
_ActionPill(
|
||||
asset: QuotidienTheme.pillTurquoiseAsset,
|
||||
iconAsset: 'assets/images/coche.png',
|
||||
tooltip: 'Bien reçu',
|
||||
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,
|
||||
),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user