import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:intl/intl.dart'; import '../application/events_notifier.dart'; import '../domain/event.dart'; import '../infrastructure/events_repository.dart'; import 'widgets/countdown_timer.dart'; import 'widgets/registration_button.dart'; class EventDetailScreen extends ConsumerWidget { const EventDetailScreen({super.key, required this.eventId}); final String eventId; @override Widget build(BuildContext context, WidgetRef ref) { final eventsAsync = ref.watch(eventsStreamProvider); return Scaffold( appBar: AppBar( leading: IconButton( icon: const Icon(Icons.arrow_back), onPressed: () { if (context.canPop()) { context.pop(); } else { context.go('/events'); } }, ), title: const Text('Event details'), ), body: eventsAsync.when( loading: () => const Center(child: CircularProgressIndicator()), error: (error, _) => _NotFound( message: 'Could not load event: $error', ), data: (_) { final event = ref.watch(eventByIdProvider(eventId)); if (event == null) { return const _NotFound(message: 'Event not found.'); } return _EventDetailBody(event: event); }, ), ); } } class _EventDetailBody extends StatelessWidget { const _EventDetailBody({required this.event}); final Event event; @override Widget build(BuildContext context) { final theme = Theme.of(context); final scheme = theme.colorScheme; final dateLabel = DateFormat('EEEE, MMMM d, y · h:mm a').format(event.date); final deadlineLabel = DateFormat('EEE, MMM d · h:mm a').format(event.registrationDeadline); return LayoutBuilder( builder: (context, constraints) { final isWide = constraints.maxWidth > 720; final horizontalPadding = isWide ? 32.0 : 16.0; return SingleChildScrollView( padding: EdgeInsets.symmetric( horizontal: horizontalPadding, vertical: 16, ), child: Center( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 760), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _Header( event: event, dateLabel: dateLabel, ), const SizedBox(height: 20), Align( alignment: Alignment.centerLeft, child: CountdownTimer( target: event.date, compact: false, ), ), const SizedBox(height: 24), Text( 'About this event', style: theme.textTheme.titleMedium?.copyWith( fontWeight: FontWeight.w700, ), ), const SizedBox(height: 8), Text( event.description, style: theme.textTheme.bodyLarge?.copyWith( height: 1.45, color: scheme.onSurface, ), ), const SizedBox(height: 24), _RegistrationSection( event: event, deadlineLabel: deadlineLabel, ), const SizedBox(height: 32), ], ), ), ), ); }, ); } } class _Header extends StatelessWidget { const _Header({required this.event, required this.dateLabel}); final Event event; final String dateLabel; @override Widget build(BuildContext context) { final theme = Theme.of(context); final scheme = theme.colorScheme; return Container( width: double.infinity, padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: scheme.primaryContainer, borderRadius: BorderRadius.circular(20), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ if (event.isCancelled) Padding( padding: const EdgeInsets.only(bottom: 8), child: Container( padding: const EdgeInsets.symmetric( horizontal: 10, vertical: 4, ), decoration: BoxDecoration( color: scheme.error, borderRadius: BorderRadius.circular(999), ), child: Text( 'Cancelled', style: theme.textTheme.labelMedium?.copyWith( color: scheme.onError, fontWeight: FontWeight.w700, ), ), ), ), Text( event.title, style: theme.textTheme.headlineSmall?.copyWith( color: scheme.onPrimaryContainer, fontWeight: FontWeight.w800, ), ), const SizedBox(height: 12), Row( children: [ Icon( Icons.calendar_today_outlined, size: 18, color: scheme.onPrimaryContainer, ), const SizedBox(width: 8), Expanded( child: Text( dateLabel, style: theme.textTheme.bodyLarge?.copyWith( color: scheme.onPrimaryContainer, ), ), ), ], ), const SizedBox(height: 6), Row( children: [ Icon( Icons.place_outlined, size: 18, color: scheme.onPrimaryContainer, ), const SizedBox(width: 8), Expanded( child: Text( event.location, style: theme.textTheme.bodyLarge?.copyWith( color: scheme.onPrimaryContainer, ), ), ), ], ), ], ), ); } } class _RegistrationSection extends StatelessWidget { const _RegistrationSection({ required this.event, required this.deadlineLabel, }); final Event event; final String deadlineLabel; @override Widget build(BuildContext context) { final theme = Theme.of(context); final scheme = theme.colorScheme; final ratio = event.maxTeams == 0 ? 0.0 : (event.teamsRegistered / event.maxTeams).clamp(0.0, 1.0).toDouble(); final deadlinePassed = DateTime.now().isAfter(event.registrationDeadline); return Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: scheme.surfaceContainerHighest, borderRadius: BorderRadius.circular(20), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Registration', style: theme.textTheme.titleMedium?.copyWith( fontWeight: FontWeight.w700, ), ), const SizedBox(height: 12), Row( children: [ Icon(Icons.groups_outlined, color: scheme.primary), const SizedBox(width: 8), Text( '${event.teamsRegistered} / ${event.maxTeams} teams', style: theme.textTheme.titleLarge?.copyWith( color: scheme.primary, fontWeight: FontWeight.w800, ), ), ], ), const SizedBox(height: 12), ClipRRect( borderRadius: BorderRadius.circular(999), child: LinearProgressIndicator( value: ratio, minHeight: 8, backgroundColor: scheme.surfaceContainer, valueColor: AlwaysStoppedAnimation(scheme.primary), ), ), const SizedBox(height: 12), Row( children: [ Icon( Icons.timer_off_outlined, size: 16, color: scheme.onSurfaceVariant, ), const SizedBox(width: 6), Expanded( child: Text( deadlinePassed ? 'Registration closed $deadlineLabel' : 'Registration closes $deadlineLabel', style: theme.textTheme.bodySmall?.copyWith( color: scheme.onSurfaceVariant, ), ), ), ], ), const SizedBox(height: 16), RegistrationButton( fullWidth: true, enabled: !deadlinePassed && !event.isCancelled, ), if (event.maxTeams > 0 && event.teamsRegistered >= event.maxTeams) ...[ const SizedBox(height: 8), Text( 'Preferred headcount reached — extras are still welcome to drop in.', style: theme.textTheme.bodySmall?.copyWith( color: scheme.onSurfaceVariant, ), ), ], ], ), ); } } class _NotFound extends StatelessWidget { const _NotFound({required this.message}); final String message; @override Widget build(BuildContext context) { final theme = Theme.of(context); return Center( child: Padding( padding: const EdgeInsets.all(32), child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon( Icons.search_off, size: 64, color: theme.colorScheme.onSurfaceVariant, ), const SizedBox(height: 16), Text(message, style: theme.textTheme.titleMedium), const SizedBox(height: 16), FilledButton.tonalIcon( onPressed: () => context.go('/events'), icon: const Icon(Icons.arrow_back), label: const Text('Back to events'), ), ], ), ), ); } }