import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import '../../domain/bracket.dart'; /// Compact card representing a single [BracketMatch] inside the bracket tree. /// /// Fixed 200x80 footprint so the bracket tree can lay matches out with /// predictable geometry. If the match is scheduled, an additional date line /// is rendered directly beneath the card. class MatchCard extends StatelessWidget { const MatchCard({ super.key, required this.match, this.width = 200, this.height = 80, }); final BracketMatch match; final double width; final double height; @override Widget build(BuildContext context) { final theme = Theme.of(context); final scheme = theme.colorScheme; return Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Container( width: width, height: height, decoration: BoxDecoration( color: scheme.surface, borderRadius: BorderRadius.circular(10), border: Border.all(color: scheme.outlineVariant), ), clipBehavior: Clip.antiAlias, child: Column( children: [ Expanded( child: _TeamRow( team: match.teamA, score: match.scoreA, isWinner: match.isTeamAWinner, ), ), Divider(height: 1, thickness: 1, color: scheme.outlineVariant), Expanded( child: _TeamRow( team: match.teamB, score: match.scoreB, isWinner: match.isTeamBWinner, ), ), ], ), ), const SizedBox(height: 4), _StatusLine(match: match, width: width), ], ); } } class _TeamRow extends StatelessWidget { const _TeamRow({ required this.team, required this.score, required this.isWinner, }); final BracketTeam? team; final int? score; final bool isWinner; @override Widget build(BuildContext context) { final theme = Theme.of(context); final scheme = theme.colorScheme; final name = team?.name ?? 'TBD'; final nameStyle = theme.textTheme.bodyMedium?.copyWith( fontWeight: isWinner ? FontWeight.w700 : FontWeight.w500, color: team == null ? scheme.onSurfaceVariant : (isWinner ? scheme.onPrimaryContainer : scheme.onSurface), fontStyle: team == null ? FontStyle.italic : FontStyle.normal, ); final scoreStyle = theme.textTheme.titleMedium?.copyWith( fontWeight: isWinner ? FontWeight.w800 : FontWeight.w600, color: isWinner ? scheme.onPrimaryContainer : scheme.onSurface, ); return Container( color: isWinner ? scheme.primaryContainer : null, padding: const EdgeInsets.symmetric(horizontal: 10), child: Row( children: [ Expanded( child: Text( name, maxLines: 1, overflow: TextOverflow.ellipsis, style: nameStyle, ), ), const SizedBox(width: 8), SizedBox( width: 24, child: Text( score?.toString() ?? '', textAlign: TextAlign.right, style: scoreStyle, ), ), ], ), ); } } class _StatusLine extends StatelessWidget { const _StatusLine({required this.match, required this.width}); final BracketMatch match; final double width; @override Widget build(BuildContext context) { final theme = Theme.of(context); final scheme = theme.colorScheme; final (Color dot, String label) = switch (match.status) { MatchStatus.completed => (Colors.green.shade600, 'Final'), MatchStatus.inProgress => (Colors.amber.shade700, 'Live'), MatchStatus.scheduled => (scheme.outline, _scheduledLabel(match)), }; return SizedBox( width: width, child: Row( children: [ Container( width: 8, height: 8, decoration: BoxDecoration(color: dot, shape: BoxShape.circle), ), const SizedBox(width: 6), Expanded( child: Text( label, maxLines: 1, overflow: TextOverflow.ellipsis, style: theme.textTheme.labelSmall?.copyWith( color: scheme.onSurfaceVariant, fontWeight: FontWeight.w600, ), ), ), ], ), ); } static String _scheduledLabel(BracketMatch match) { final scheduled = match.scheduledAt; if (scheduled == null) return 'Scheduled'; return DateFormat('MMM d ยท h:mm a').format(scheduled); } }