import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../core/format.dart'; import '../../core/models.dart'; import '../../core/providers.dart'; import '../../core/theme.dart'; import '../../widgets/account_sheet.dart'; import 'calendar_picker.dart'; import '../../widgets/ui.dart'; class ResultsScreen extends ConsumerWidget { const ResultsScreen({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final date = ref.watch(selectedDateProvider); final res = ref.watch(resultsProvider(date)); Future refresh() => refreshData(context, ref, resultsProvider(date)); return Column( children: [ ScreenHeader( title: 'Results', trailing: Row( mainAxisSize: MainAxisSize.min, children: [ Builder( builder: (anchor) => RoundIcon( Icons.calendar_month_rounded, label: 'Calendar', onTap: () => showCalendarPicker(anchor), ), ), // Wider windows show the account in the sidebar. if (!isWide(context)) ...[ const SizedBox(width: S.s10), RoundIcon( Icons.person_outline_rounded, label: 'Account', onTap: () => showAccountSheet(context, ref), ), ], ], ), ), Expanded( child: RefreshableView( value: res, onRefresh: refresh, skeleton: _Loading(date: date), builder: (r) => _Body(results: r, date: date), ), ), ], ); } } class _DrawRow extends ConsumerWidget { const _DrawRow({required this.date, required this.info}); final DateTime date; final Widget info; @override Widget build(BuildContext context, WidgetRef ref) { final n = ref.read(selectedDateProvider.notifier); return Row( children: [ Expanded(child: info), RoundIcon( Icons.chevron_left_rounded, label: 'Previous draw', size: 40, onTap: () => n.set(addDays(date, -1)), ), const SizedBox(width: S.s8), RoundIcon( Icons.chevron_right_rounded, label: 'Next draw', size: 40, onTap: () => n.set(addDays(date, 1)), ), ], ); } } /// Skeleton of the Results page. class _Loading extends StatelessWidget { const _Loading({required this.date}); final DateTime date; @override Widget build(BuildContext context) { const onNavy = C.onNavy8; Widget line(double h, Bone bone) => SizedBox( height: h, child: Align(alignment: Alignment.centerLeft, child: bone), ); Widget stat() => SrsCard( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ line(16, const Bone(width: 72, height: 10)), const SizedBox(height: S.s8), line(30, const Bone(width: 96, height: 22)), ], ), ); return PageList( children: [ _DrawRow( date: date, info: Skeleton( announce: false, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ line(22, const Bone(width: 120, height: 14)), const SizedBox(height: S.s2), line(18, const Bone(width: 180, height: 10)), ], ), ), ), const SizedBox(height: S.s16), Skeleton( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ _HeroRow( hero: NavyCard( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ line( 15, const Bone(width: 90, height: 10, color: onNavy), ), const SizedBox(height: S.s10), line( 58, const Bone( width: 200, height: 38, radius: R.md, color: onNavy, ), ), const SizedBox(height: S.s10), line( 18, const Bone(width: 170, height: 10, color: onNavy), ), ], ), ), stats: [stat(), stat()], ), const Padding( padding: EdgeInsets.fromLTRB(S.s2, S.s22, S.s2, S.s10), child: SizedBox( height: 21, child: Align( alignment: Alignment.centerLeft, child: Bone(width: 64, height: 14), ), ), ), const TileGridBone(count: 4), ], ), ), ], ); } } class _Body extends StatelessWidget { const _Body({required this.results, required this.date}); final DrawResults results; final DateTime date; @override Widget build(BuildContext context) { final r = results; final d = r.draw; return PageList( children: [ _DrawRow( date: date, info: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(d.label, style: ts(16, w: w7)), const SizedBox(height: S.s2), Text(dateLong(d.date), style: ts(13, c: C.muted)), ], ), ), const SizedBox(height: S.s16), if (d.published) ..._published(context, r) else ..._notOut(context, r), ], ); } List _published(BuildContext context, DrawResults r) { final top = r.topShop; return [ _HeroRow( hero: NavyCard( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'TOTAL PRIZE', style: ts(11, w: w6, c: C.softOnNavy, ls: 1.2), ), const SizedBox(height: S.s10), Text( rupees(r.totalPrize), style: ts( 40, w: w7, c: r.totalPrize > 0 ? C.gold : C.softOnNavy, ls: -1, ), ), const SizedBox(height: S.s10), Text( r.totalPrize > 0 ? '${plural(r.winningTickets, 'winning ticket')} · ${plural(r.winningShops, 'shop')}' : 'No winning tickets in this draw', style: ts(13, w: w5, c: C.softOnNavy), ), ], ), ), stats: [ StatCard(label: 'TOTAL TICKETS', value: inr(r.totalTickets)), StatCard( label: 'TOP SHOP', value: top?.shop.code ?? '—', valueColor: top == null ? C.muted : C.ink, ), ], ), SectionTitle( 'Shops', trailing: r.shops.isEmpty ? null : plural(r.shops.length, 'shop'), ), if (r.shops.isEmpty) const SrsCard( child: EmptyState( icon: Icons.receipt_long_outlined, title: 'No orders for this draw', body: 'Orders placed for your shops on this date will appear here.', ), ) else CardGrid(children: [for (final s in r.shops) _ShopRow(s: s)]), ]; } List _notOut(BuildContext context, DrawResults r) { return [ _HeroRow( hero: NavyCard( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( padding: const EdgeInsets.fromLTRB(S.s4, S.s4, S.s12, S.s4), decoration: BoxDecoration( color: C.goldSoft, borderRadius: BorderRadius.circular(R.pill), border: Border.all(color: C.gold.withAlpha(90)), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Container( width: 20, height: 20, decoration: const BoxDecoration( color: C.gold, shape: BoxShape.circle, ), child: const Icon( Icons.hourglass_top_rounded, size: 12, color: C.navy, ), ), const SizedBox(width: S.s8), Text( 'AWAITING RESULTS', style: ts(11, w: w7, c: C.gold, ls: 1.1), ), ], ), ), const SizedBox(height: S.s12), Text( 'Results not yet published', style: ts(26, w: w7, c: Colors.white, ls: -0.4), ), const SizedBox(height: S.s8), Text( 'Official results are usually published after 4 PM.', style: ts(13, c: C.softOnNavy, h: 1.5), ), ], ), ), stats: [ StatCard(label: 'ORDERS', value: inr(r.orders)), StatCard(label: 'TICKETS', value: inr(r.totalTickets)), ], ), SectionTitle( 'Shops', trailing: r.shops.isEmpty ? null : plural(r.shops.length, 'shop'), ), if (r.shops.isEmpty) const SrsCard( child: EmptyState( icon: Icons.inbox_outlined, title: 'No orders', body: 'No orders have been placed for this draw.', ), ) else CardGrid( children: [for (final s in r.shops) _ShopRow(s: s, pending: true)], ), ]; } } /// The navy hero with its two stat cards: stacked under it on phones, /// beside it on wide windows. class _HeroRow extends StatelessWidget { const _HeroRow({required this.hero, required this.stats}); final Widget hero; final List stats; @override Widget build(BuildContext context) => LayoutBuilder( builder: (context, c) { if (c.maxWidth < 760) { return Column( children: [ hero, const SizedBox(height: S.s12), Row( children: [ Expanded(child: stats[0]), const SizedBox(width: S.s12), Expanded(child: stats[1]), ], ), ], ); } return IntrinsicHeight( child: Row( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Expanded(flex: 2, child: hero), const SizedBox(width: S.s12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Expanded(child: stats[0]), const SizedBox(height: S.s12), Expanded(child: stats[1]), ], ), ), ], ), ); }, ); } class _ShopRow extends StatelessWidget { const _ShopRow({required this.s, this.pending = false}); final ShopResult s; final bool pending; @override Widget build(BuildContext context) { final win = s.prize > 0; return SrsCard( onTap: () => context.push('/shops/${s.shop.id}'), padding: const EdgeInsets.all(S.s14), child: Row( children: [ CodeTile(s.shop.code, dark: win), const SizedBox(width: S.s14), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( s.shop.name, maxLines: 1, overflow: TextOverflow.ellipsis, style: ts(15, w: w6), ), const SizedBox(height: S.s4), Text( pending ? plural(s.tickets, 'ticket') : win ? plural(s.winTickets, 'winning ticket') : 'No winning tickets', style: ts(12, c: C.muted), ), ], ), ), if (!pending) Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( rupees(s.prize), style: ts(16, w: w7, c: win ? C.green : C.muted), ), if (win) ...[ const SizedBox(height: S.s2), Text( plural(s.winTickets, 'ticket'), style: ts(11, c: C.muted), ), ], ], ) else const Icon(Icons.chevron_right_rounded, color: C.placeholder), ], ), ); } }