import 'dart:math'; import 'package:audioplayers/audioplayers.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; import 'package:provider/provider.dart'; import '../../l10n/app_localizations.dart'; import '../../models/payment_method.dart'; import '../../utils/money.dart'; import '../../viewmodels/bar_screen_view_model.dart'; import '../widgets/money_rain.dart'; import '../widgets/slide_confirm.dart'; Future confirmCloseTab(BuildContext context) async { final viewModel = context.read(); final l10n = AppLocalizations.of(context); final tab = viewModel.selectedTab; if (tab == null) return; PaymentMethod paymentMethod = PaymentMethod.cash; final confirmed = await showDialog( context: context, builder: (dialogContext) { return StatefulBuilder( builder: (context, setDialogState) { return AlertDialog( title: Text(l10n.closeTabForCustomer(tab.customerName)), content: SizedBox( width: 360, child: Column( mainAxisSize: MainAxisSize.min, children: [ Text(l10n.currentTotal(tab.totalInCents.asEuro)), const SizedBox(height: 20), Text( l10n.paymentMethod, style: Theme.of(context).textTheme.bodySmall, ), const SizedBox(height: 8), _PaymentPicker( selected: paymentMethod, onChanged: (value) { setDialogState(() => paymentMethod = value); }, ), const SizedBox(height: 24), SlideConfirm( onConfirmed: () { Navigator.of(dialogContext).pop(true); }, ), ], ), ), actionsPadding: const EdgeInsets.fromLTRB(16, 0, 16, 16), actions: [ TextButton( onPressed: () => Navigator.of(dialogContext).pop(false), child: Text(l10n.cancel), ), ], ); }, ); }, ); if (confirmed != true || !context.mounted) return; if (paymentMethod == PaymentMethod.payconiq) { final paid = await _showPayconiqQr(context, tab.totalInCents.asEuro); if (paid != true || !context.mounted) return; } try { await viewModel.closeSelectedTab(paymentMethod: paymentMethod); _playChaChing(); // Make it rain, but only once in a hundred tabs. if (context.mounted && Random().nextInt(25) == 0) showMoneyRain(context); } catch (_) { if (!context.mounted) return; ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text(l10n.couldNotCloseTab))); } } final _chaChingPlayer = AudioPlayer(); /// Plays the till sound once a tab is paid; a sound that fails to play never /// gets in the way of closing the tab. void _playChaChing() { _chaChingPlayer .play(AssetSource('Cha-Ching.mp3')) .catchError((Object e) => debugPrint('Could not play cha-ching: $e')); } const _payconiqQrAsset = 'assets/payconiq/qr.png'; /// Shows the Payconiq QR code for the customer to scan. Resolves to true /// once the bartender taps Done; dismissing it leaves the tab open. Future _showPayconiqQr(BuildContext context, String amount) { final l10n = AppLocalizations.of(context); return showDialog( context: context, builder: (dialogContext) { return AlertDialog( title: Text(l10n.payconiq), content: SizedBox( width: 360, child: Column( mainAxisSize: MainAxisSize.min, children: [ Text(l10n.scanToPay(amount), textAlign: TextAlign.center), const SizedBox(height: 20), ClipRRect( borderRadius: BorderRadius.circular(12), child: Image.asset( _payconiqQrAsset, width: 280, height: 280, fit: BoxFit.contain, errorBuilder: (context, _, _) => const SizedBox( width: 280, height: 280, child: Icon(Icons.qr_code_2, size: 160), ), ), ), const SizedBox(height: 24), SizedBox( width: double.infinity, child: FilledButton( onPressed: () => Navigator.of(dialogContext).pop(true), child: Text(l10n.done), ), ), ], ), ), ); }, ); } class _PaymentPicker extends StatelessWidget { final PaymentMethod selected; final ValueChanged onChanged; const _PaymentPicker({required this.selected, required this.onChanged}); static const _options = [PaymentMethod.cash, PaymentMethod.payconiq]; @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context); return Row( mainAxisAlignment: MainAxisAlignment.center, children: _options.map((option) { final value = option; final label = switch (value) { PaymentMethod.cash => l10n.cash, PaymentMethod.payconiq => l10n.payconiq, PaymentMethod.paymentPending => l10n.paymentPending, }; final isSelected = selected == value; return Padding( padding: const EdgeInsets.symmetric(horizontal: 6), child: ChoiceChip( showCheckmark: false, selected: isSelected, onSelected: (_) => onChanged(value), label: Row( mainAxisSize: MainAxisSize.min, children: [ if (value == PaymentMethod.cash) const Icon( Icons.attach_money, size: 16, color: Colors.lightGreen, ) else if (value == PaymentMethod.payconiq) SvgPicture.asset( 'assets/icons/payconic.svg', width: 16, height: 16, colorFilter: ColorFilter.mode( Colors.pinkAccent, BlendMode.srcIn, ), ) else const Icon( Icons.hourglass_empty, size: 16, color: Colors.orange, ), const SizedBox(width: 6), Text(label), ], ), ), ); }).toList(), ); } }