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,
|
required this.onRefuse,
|
||||||
});
|
});
|
||||||
|
|
||||||
String get _periode {
|
String _fmtLong(String? iso) {
|
||||||
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) {
|
|
||||||
if (iso == null || iso.isEmpty) return '…';
|
if (iso == null || iso.isEmpty) return '…';
|
||||||
final dt = DateTime.tryParse(iso);
|
final dt = DateTime.tryParse(iso);
|
||||||
if (dt == null) return 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 jj = dt.day.toString().padLeft(2, '0');
|
||||||
final mm = dt.month.toString().padLeft(2, '0');
|
return '$jj ${mois[dt.month - 1]} ${dt.year}';
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Arrêt maladie : reste opaque tant que la période n’est pas finie.
|
/// Arrêt maladie : reste opaque tant que la période n’est pas finie.
|
||||||
@@ -374,53 +370,61 @@ class _BulleTile extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final indice = _indice;
|
final memeJour = card.dateDebut != null &&
|
||||||
|
card.dateFin != null &&
|
||||||
|
card.dateDebut == card.dateFin;
|
||||||
|
|
||||||
return Opacity(
|
return Opacity(
|
||||||
opacity: _attenuee ? 0.55 : 1,
|
opacity: _attenuee ? 0.55 : 1,
|
||||||
child: Carte(
|
child: Carte(
|
||||||
assetPath: card.backgroundAsset,
|
assetPath: card.backgroundAsset,
|
||||||
constraints: const BoxConstraints(minHeight: 96),
|
constraints: const BoxConstraints(minHeight: 120),
|
||||||
padding: const EdgeInsets.fromLTRB(20, 14, 20, 14),
|
padding: const EdgeInsets.fromLTRB(22, 16, 22, 16),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Text(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
card.titreAffiche,
|
card.titreAffiche,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: GoogleFonts.merienda(
|
style: GoogleFonts.merienda(
|
||||||
fontSize: 16,
|
fontSize: 18,
|
||||||
fontWeight: FontWeight.w700,
|
fontWeight: FontWeight.w700,
|
||||||
color: QuotidienTheme.ink,
|
color: QuotidienTheme.ink,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
if (card.dateDebut != null || card.dateFin != null) ...[
|
||||||
if (indice != null)
|
const SizedBox(height: 10),
|
||||||
|
if (memeJour)
|
||||||
Text(
|
Text(
|
||||||
indice,
|
'Le ${_fmtLong(card.dateDebut)}',
|
||||||
style: GoogleFonts.merriweather(
|
style: GoogleFonts.merriweather(
|
||||||
fontSize: 11,
|
fontSize: 14,
|
||||||
color: QuotidienTheme.muted,
|
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) ...[
|
if (card.motif != null && card.motif!.trim().isNotEmpty) ...[
|
||||||
const SizedBox(height: 2),
|
const SizedBox(height: 6),
|
||||||
Text(
|
Text(
|
||||||
card.motif!,
|
card.motif!,
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
@@ -431,12 +435,13 @@ class _BulleTile extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
if (card.statut == CarteStatut.refusee &&
|
if (card.statut == CarteStatut.refusee) ...[
|
||||||
card.lastRefuseComment != null &&
|
const SizedBox(height: 6),
|
||||||
card.lastRefuseComment!.trim().isNotEmpty) ...[
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
Text(
|
Text(
|
||||||
'Motif : ${card.lastRefuseComment}',
|
card.lastRefuseComment != null &&
|
||||||
|
card.lastRefuseComment!.trim().isNotEmpty
|
||||||
|
? 'Refusé — ${card.lastRefuseComment}'
|
||||||
|
: 'Refusé',
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: GoogleFonts.merriweather(
|
style: GoogleFonts.merriweather(
|
||||||
@@ -445,39 +450,50 @@ class _BulleTile extends StatelessWidget {
|
|||||||
color: QuotidienTheme.ink,
|
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) ...[
|
if (card.peutRepondre) ...[
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 14),
|
||||||
if (busy)
|
if (busy)
|
||||||
const Align(
|
const Center(
|
||||||
alignment: Alignment.centerRight,
|
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: 18,
|
width: 22,
|
||||||
height: 18,
|
height: 22,
|
||||||
child: CircularProgressIndicator(strokeWidth: 2),
|
child: CircularProgressIndicator(strokeWidth: 2),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
else
|
else if (card.responseMode == CarteResponseMode.acceptRefuse)
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
|
||||||
children: [
|
children: [
|
||||||
if (card.responseMode ==
|
Expanded(
|
||||||
CarteResponseMode.acceptRefuse) ...[
|
child: _ActionPill(
|
||||||
_ActionPill(
|
|
||||||
asset: QuotidienTheme.pillTurquoiseAsset,
|
asset: QuotidienTheme.pillTurquoiseAsset,
|
||||||
iconAsset: 'assets/images/coche.png',
|
iconAsset: 'assets/images/coche.png',
|
||||||
tooltip: 'Accepter',
|
tooltip: 'Accepter',
|
||||||
onTap: onAccept,
|
onTap: onAccept,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 10),
|
),
|
||||||
_ActionPill(
|
const SizedBox(width: 12),
|
||||||
|
Expanded(
|
||||||
|
child: _ActionPill(
|
||||||
asset: QuotidienTheme.pillCorailAsset,
|
asset: QuotidienTheme.pillCorailAsset,
|
||||||
iconAsset: 'assets/images/red_cross2.png',
|
iconAsset: 'assets/images/red_cross2.png',
|
||||||
tooltip: 'Refuser',
|
tooltip: 'Refuser',
|
||||||
onTap: onRefuse,
|
onTap: onRefuse,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
if (card.responseMode == CarteResponseMode.ack)
|
)
|
||||||
|
else if (card.responseMode == CarteResponseMode.ack)
|
||||||
_ActionPill(
|
_ActionPill(
|
||||||
asset: QuotidienTheme.pillTurquoiseAsset,
|
asset: QuotidienTheme.pillTurquoiseAsset,
|
||||||
iconAsset: 'assets/images/coche.png',
|
iconAsset: 'assets/images/coche.png',
|
||||||
@@ -485,8 +501,6 @@ class _BulleTile extends StatelessWidget {
|
|||||||
onTap: onAck,
|
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 {
|
class _ActionPill extends StatelessWidget {
|
||||||
final String asset;
|
final String asset;
|
||||||
final String iconAsset;
|
final String iconAsset;
|
||||||
@@ -514,10 +528,9 @@ class _ActionPill extends StatelessWidget {
|
|||||||
message: tooltip,
|
message: tooltip,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
borderRadius: BorderRadius.circular(20),
|
borderRadius: BorderRadius.circular(24),
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: 52,
|
height: 40,
|
||||||
height: 32,
|
|
||||||
child: Stack(
|
child: Stack(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
children: [
|
children: [
|
||||||
@@ -526,8 +539,8 @@ class _ActionPill extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
Image.asset(
|
Image.asset(
|
||||||
iconAsset,
|
iconAsset,
|
||||||
height: 18,
|
height: 22,
|
||||||
width: 18,
|
width: 22,
|
||||||
fit: BoxFit.contain,
|
fit: BoxFit.contain,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in New Issue
Block a user