import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:p_tits_pas/models/couple_garde.dart'; import 'package:p_tits_pas/services/api/api_config.dart'; import 'package:p_tits_pas/widgets/common/auth_network_image.dart'; import 'package:p_tits_pas/widgets/quotidien/quotidien_theme.dart'; /// Rôle qui consulte le bandeau : côté parent (enfant | nounou) ou côté AM /// (enfant | parent(s)). Le composant est le même, seule la lecture change. /// Ticket #167 (parent) ; #170 réutilise en mode AM. enum CoupleBandeauMode { parent, assistanteMaternelle } /// Bandeau « couple de garde » en haut de la colonne gauche du TdB. /// /// - Plusieurs couples → contrôle unique avec chevron (dropdown de bascule). /// - Un seul couple → affichage informatif (pas de chevron, pas de menu). /// - Aucun couple → état vide discret. class CoupleSelectorBandeau extends StatelessWidget { final CoupleBandeauMode mode; final List couples; final String? selectedCoupleId; final ValueChanged? onCoupleSelected; final bool loading; final String? errorMessage; final VoidCallback? onRetry; const CoupleSelectorBandeau({ super.key, this.mode = CoupleBandeauMode.parent, required this.couples, this.selectedCoupleId, this.onCoupleSelected, this.loading = false, this.errorMessage, this.onRetry, }); CoupleGarde? get _selected { if (couples.isEmpty) return null; if (selectedCoupleId != null) { for (final c in couples) { if (c.id == selectedCoupleId) return c; } } for (final c in couples) { if (c.courant) return c; } return couples.first; } @override Widget build(BuildContext context) { return LayoutBuilder(builder: (context, constraints) { if (loading) return const _CoupleBandeauSkeleton(); if (errorMessage != null) { return _CoupleBandeauError(message: errorMessage!, onRetry: onRetry); } if (couples.isEmpty) return const _CoupleBandeauEmpty(); final selected = _selected!; final multi = couples.length > 1; final card = _CoupleCard( mode: mode, couple: selected, showChevron: multi, colorIndex: couples.indexOf(selected), ); if (!multi) return card; return Theme( data: Theme.of(context).copyWith( hoverColor: Colors.transparent, splashColor: Colors.transparent, highlightColor: Colors.transparent, focusColor: Colors.transparent, ), child: PopupMenuButton( tooltip: 'Changer de garde', // L'offset doit être suffisant pour descendre sous la carte (qui fait 90px). offset: const Offset(0, 95), color: Colors.transparent, // Le fond devient invisible elevation: 0, // Pas d'ombre carrée constraints: BoxConstraints.tightFor(width: constraints.maxWidth), padding: EdgeInsets.zero, onSelected: (id) { final chosen = couples.firstWhere((c) => c.id == id); onCoupleSelected?.call(chosen); }, itemBuilder: (context) => [ for (final c in couples) if (c.id != selected.id) PopupMenuItem( value: c.id, padding: EdgeInsets.zero, height: 100, // Hauteur de la carte (90) + un peu de marge (10) child: Padding( padding: const EdgeInsets.only(bottom: 10), child: _CoupleCard( mode: mode, couple: c, showChevron: false, selected: false, isMenuItem: true, colorIndex: couples.indexOf(c), ), ), ), ], child: card, ), ); }); } } /// Carte principale : enfant à gauche, séparateur, AM/parents à droite. class _CoupleCard extends StatelessWidget { final CoupleBandeauMode mode; final CoupleGarde couple; final bool showChevron; final bool selected; final bool isMenuItem; final int colorIndex; const _CoupleCard({ required this.mode, required this.couple, required this.showChevron, this.selected = true, this.isMenuItem = false, required this.colorIndex, }); @override Widget build(BuildContext context) { return Container( height: 90, decoration: BoxDecoration( image: DecorationImage( image: AssetImage(QuotidienTheme.bandeauColors[colorIndex % QuotidienTheme.bandeauColors.length]), fit: BoxFit.fill, ), ), padding: const EdgeInsets.symmetric(horizontal: 24), child: Row( children: [ Expanded( child: Padding( padding: const EdgeInsets.only(right: 12), child: _MembreTile( photoUrl: couple.enfant.photoUrl, name: (couple.enfant.prenom != null && couple.enfant.prenom!.isNotEmpty) ? couple.enfant.prenom! : couple.enfant.displayName(fallback: 'Enfant'), fallbackIcon: Icons.child_care, ), ), ), Container( width: 8, height: 44, decoration: const BoxDecoration( image: DecorationImage( image: AssetImage(QuotidienTheme.pencilLineVerticalAsset), fit: BoxFit.contain, ), ), ), Expanded( child: Padding( padding: const EdgeInsets.only(left: 12), child: _MembreTile( photoUrl: couple.am.photoUrl, name: (couple.am.prenom != null && couple.am.prenom!.isNotEmpty) ? couple.am.prenom! : couple.am.displayName( fallback: mode == CoupleBandeauMode.parent ? 'Nounou' : 'Parent', ), fallbackIcon: mode == CoupleBandeauMode.parent ? Icons.volunteer_activism : Icons.person_outline, ), ), ), SizedBox( width: 28, child: showChevron ? const Icon( Icons.keyboard_arrow_down, color: QuotidienTheme.ink, ) : null, ), ], ), ); } } /// Photo ronde + nom (une moitié du couple). class _MembreTile extends StatelessWidget { final String? photoUrl; final String name; final IconData fallbackIcon; const _MembreTile({ required this.photoUrl, required this.name, required this.fallbackIcon, }); @override Widget build(BuildContext context) { return Row( children: [ _Avatar(photoUrl: photoUrl, fallbackIcon: fallbackIcon, size: 64), const SizedBox(width: 12), Expanded( child: Text( name, maxLines: 1, overflow: TextOverflow.ellipsis, style: GoogleFonts.merienda( fontSize: 22, fontWeight: FontWeight.bold, color: QuotidienTheme.ink, ), ), ), ], ); } } class _Avatar extends StatelessWidget { final String? photoUrl; final IconData fallbackIcon; final double size; const _Avatar({ required this.photoUrl, required this.fallbackIcon, required this.size, }); @override Widget build(BuildContext context) { final url = ApiConfig.absoluteMediaUrl(photoUrl); final placeholder = Container( width: size, height: size, color: QuotidienTheme.lavender.withValues(alpha: 0.35), child: Icon(fallbackIcon, size: size * 0.5, color: QuotidienTheme.ink), ); return ClipOval( child: SizedBox( width: size, height: size, child: url.isEmpty ? placeholder : AuthNetworkImage( url: url, width: size, height: size, fit: BoxFit.cover, errorBuilder: (_, __, ___) => placeholder, ), ), ); } } class _CoupleBandeauSkeleton extends StatelessWidget { const _CoupleBandeauSkeleton(); @override Widget build(BuildContext context) { return Container( height: 90, decoration: BoxDecoration( image: DecorationImage( image: const AssetImage(QuotidienTheme.bandeauLime), // Un asset existant fit: BoxFit.fill, colorFilter: ColorFilter.mode( Colors.white.withValues(alpha: 0.5), BlendMode.lighten, ), ), ), alignment: Alignment.center, child: const SizedBox( width: 26, height: 26, child: CircularProgressIndicator(strokeWidth: 2), ), ); } } class _CoupleBandeauEmpty extends StatelessWidget { const _CoupleBandeauEmpty(); @override Widget build(BuildContext context) { return Container( height: 90, decoration: const BoxDecoration( image: DecorationImage( image: AssetImage(QuotidienTheme.bandeauLime), // Un asset existant par défaut fit: BoxFit.fill, ), ), padding: const EdgeInsets.symmetric(horizontal: 24), child: Row( children: [ const Icon(Icons.info_outline, color: QuotidienTheme.muted), const SizedBox(width: 12), Expanded( child: Text( 'Aucune garde active pour le moment.', style: GoogleFonts.merriweather( fontSize: 14, color: QuotidienTheme.muted, ), ), ), ], ), ); } } class _CoupleBandeauError extends StatelessWidget { final String message; final VoidCallback? onRetry; const _CoupleBandeauError({required this.message, this.onRetry}); @override Widget build(BuildContext context) { return Container( height: 90, decoration: BoxDecoration( image: DecorationImage( image: const AssetImage(QuotidienTheme.bandeauLime), // Un asset existant fit: BoxFit.fill, colorFilter: ColorFilter.mode( QuotidienTheme.coral.withValues(alpha: 0.3), BlendMode.srcATop, ), ), ), padding: const EdgeInsets.symmetric(horizontal: 24), child: Row( children: [ const Icon(Icons.error_outline, color: QuotidienTheme.ink), const SizedBox(width: 12), Expanded( child: Text( message, style: GoogleFonts.merriweather( fontSize: 13, color: QuotidienTheme.ink, ), ), ), if (onRetry != null) TextButton( onPressed: onRetry, child: const Text('Réessayer'), ), ], ), ); } }