From 4a1e35d700e3edee6373b80246705fb5fe79e187 Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Wed, 1 Oct 2025 20:59:45 +0200 Subject: [PATCH] BUG: Mzansi Wallet Card Scanner --- .../components/mih_add_card_window.dart | 364 ++++++++++++++++++ .../components/mih_barcode_scanner.dart | 15 +- .../package_tools/mih_cards.dart | 259 +------------ 3 files changed, 376 insertions(+), 262 deletions(-) create mode 100644 Frontend/lib/mih_packages/mzansi_wallet/components/mih_add_card_window.dart diff --git a/Frontend/lib/mih_packages/mzansi_wallet/components/mih_add_card_window.dart b/Frontend/lib/mih_packages/mzansi_wallet/components/mih_add_card_window.dart new file mode 100644 index 00000000..b194d0ce --- /dev/null +++ b/Frontend/lib/mih_packages/mzansi_wallet/components/mih_add_card_window.dart @@ -0,0 +1,364 @@ +// ignore_for_file: must_be_immutable + +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:ken_logger/ken_logger.dart'; +import 'package:mzansi_innovation_hub/main.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_objects/app_user.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_objects/arguments.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_objects/loyalty_card.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_button.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_dropdwn_field.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_form.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_alert.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_window.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_text_form_field.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_pop_up_messages/mih_error_message.dart'; +import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart'; +import 'package:mzansi_innovation_hub/mih_packages/mzansi_wallet/components/mih_card_display.dart'; +import 'package:mzansi_innovation_hub/mih_services/mih_alert_services.dart'; +import 'package:mzansi_innovation_hub/mih_services/mih_mzansi_wallet_services.dart'; +import 'package:mzansi_innovation_hub/mih_services/mih_validation_services.dart'; + +class MihAddCardWindow extends StatefulWidget { + final AppUser signedInUser; + Future> cardList; + + MihAddCardWindow({ + super.key, + required this.signedInUser, + required this.cardList, + }); + + @override + State createState() => _MihAddCardWindowState(); +} + +class _MihAddCardWindowState extends State { + final _formKey = GlobalKey(); + final TextEditingController _cardNumberController = TextEditingController(); + final TextEditingController _shopController = TextEditingController(); + final TextEditingController _nicknameController = TextEditingController(); + final ValueNotifier _shopName = ValueNotifier(""); + + void internetConnectionPopUp() { + showDialog( + context: context, + builder: (context) { + return const MIHErrorMessage( + errorType: "Internet Connection", + ); + }, + ); + } + + void successPopUp(String title, String message, int packageIndex) { + showDialog( + context: context, + builder: (context) { + return MihPackageAlert( + alertIcon: Icon( + Icons.check_circle_outline_rounded, + size: 150, + color: MihColors.getGreenColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + ), + alertTitle: title, + alertBody: Column( + children: [ + Text( + message, + style: TextStyle( + color: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + fontSize: 15, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 25), + Center( + child: MihButton( + onPressed: () { + context.pop(); + context.goNamed( + 'mzansiWallet', + extra: WalletArguments(widget.signedInUser, 0), + ); + }, + buttonColor: MihColors.getGreenColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + elevation: 10, + width: 300, + child: Text( + "Dismiss", + style: TextStyle( + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + ) + ], + ), + alertColour: MihColors.getGreenColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + ); + }, + ); + } + + @override + void dispose() { + _cardNumberController.dispose(); + _shopController.dispose(); + _nicknameController.dispose(); + _shopName.dispose(); + super.dispose(); + } + + @override + void initState() { + super.initState(); + _shopController.addListener(_shopSelected); + } + + void _shopSelected() { + if (_shopController.text.isNotEmpty) { + _shopName.value = _shopController.text; + } else { + _shopName.value = ""; + } + } + + // ... rest of your existing methods + + @override + Widget build(BuildContext context) { + final double width = MediaQuery.sizeOf(context).width; + return MihPackageWindow( + fullscreen: false, + windowTitle: "Add New Loyalty Card", + onWindowTapClose: () { + _shopController.clear(); + _cardNumberController.clear(); + _nicknameController.clear(); + _shopName.value = ""; + Navigator.pop(context); + }, + windowBody: Padding( + padding: MzansiInnovationHub.of(context)!.theme.screenType == "desktop" + ? EdgeInsets.symmetric(horizontal: width * 0.05) + : EdgeInsets.symmetric(horizontal: width * 0), + child: Column( + children: [ + MihForm( + formKey: _formKey, + formFields: [ + MihDropdownField( + controller: _shopController, + hintText: "Shop Name", + editable: true, + enableSearch: true, + validator: (value) { + return MihValidationServices().isEmpty(value); + }, + requiredText: true, + dropdownOptions: const [ + "+More", + "Apple Tree", + "+More", + "Apple Tree", + "Auchan", + "Best Before", + "Big Save", + "Boxer", + "BP", + "Builders Warehouse", + "Checkers", + "Choppies", + "Clicks", + "Continente", + "Cotton:On", + "Carrefour", + "Dis-Chem", + "Edgars", + "Eskom", + "Exclusive Books", + "Fresh Stop", + "Fresmart", + "Infinity", + "Jet", + "Justrite", + "Kero", + "Leroy Merlin", + "Makro", + "Naivas", + "OK Foods", + "Panarottis", + "Pick n Pay", + "PnA", + "PQ Clothing", + "Rage", + "Sefalana", + "Sasol", + "Shell", + "Shoprite", + "Signature Cosmetics & Fragrances", + "Spar", + "Spur", + "TFG Group", + "Total Energies", + "Toys R Us", + "Woermann Brock", + "Woolworths" + ], + ), + ValueListenableBuilder( + valueListenable: _shopName, + builder: (BuildContext context, String value, Widget? child) { + return Visibility( + visible: value != "", + child: Column( + children: [ + const SizedBox(height: 10), + MihCardDisplay( + shopName: _shopName.value, + nickname: "", + height: 200), + ], + ), + ); + }, + ), + const SizedBox(height: 10), + MihTextFormField( + fillColor: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + inputColor: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + controller: _nicknameController, + multiLineInput: false, + requiredText: false, + hintText: "Card Title", + ), + const SizedBox(height: 10), + Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisSize: MainAxisSize.max, + children: [ + Flexible( + child: MihTextFormField( + fillColor: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + inputColor: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + controller: _cardNumberController, + multiLineInput: false, + requiredText: true, + hintText: "Card Number", + numberMode: true, + validator: (value) { + return MihValidationServices().isEmpty(value); + }, + ), + ), + const SizedBox(width: 20), + MihButton( + onPressed: () { + context.pushNamed( + "barcodeScanner", + extra: _cardNumberController, // Use local controller + ); + }, + buttonColor: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + width: 100, + child: Text( + "Scan", + style: TextStyle( + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + ], + ), + const SizedBox(height: 15), + Center( + child: MihButton( + onPressed: () async { + if (_formKey.currentState!.validate()) { + if (_shopController.text == "") { + showDialog( + context: context, + builder: (context) { + return const MIHErrorMessage( + errorType: "Input Error"); + }, + ); + } else { + int statusCode = + await MIHMzansiWalletApis.addLoyaltyCardAPICall( + widget.signedInUser, + widget.signedInUser.app_id, + _shopController.text, + _cardNumberController.text, + "", + 0, + _nicknameController.text, + context, + ); + if (statusCode == 201) { + setState(() { + widget.cardList = + MIHMzansiWalletApis.getLoyaltyCards( + widget.signedInUser.app_id); + }); + context.pop(); + KenLogger.error("Card Added Successfully"); + successPopUp( + "Successfully Added Card", + "The loyalty card has been added to your favourites.", + 0, + ); + } else { + internetConnectionPopUp(); + } + } + } else { + MihAlertServices().formNotFilledCompletely(context); + } + }, + buttonColor: MihColors.getGreenColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + width: 300, + child: Text( + "Add", + style: TextStyle( + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ], + ), + ], + ), + ), + ); + } +} diff --git a/Frontend/lib/mih_packages/mzansi_wallet/components/mih_barcode_scanner.dart b/Frontend/lib/mih_packages/mzansi_wallet/components/mih_barcode_scanner.dart index 23eb53c5..ae1c2491 100644 --- a/Frontend/lib/mih_packages/mzansi_wallet/components/mih_barcode_scanner.dart +++ b/Frontend/lib/mih_packages/mzansi_wallet/components/mih_barcode_scanner.dart @@ -26,7 +26,7 @@ class _MihBarcodeScannerState extends State bool _isScannerStarting = false; bool barcodeScanned = false; - void foundCode(BarcodeCapture bcode) { + Future foundCode(BarcodeCapture bcode) async { if (mounted && barcodeScanned == false && bcode.barcodes.isNotEmpty && @@ -36,8 +36,10 @@ class _MihBarcodeScannerState extends State widget.cardNumberController.text = bcode.barcodes.first.rawValue!; }); print(bcode.barcodes.first.rawValue); - _scannerController.stop(); - context.pop(); + await _scannerController.stop(); + if (mounted) { + context.pop(); + } } } @@ -69,13 +71,12 @@ class _MihBarcodeScannerState extends State } @override - Future dispose() async { - // TODO: implement dispose + void dispose() { WidgetsBinding.instance.removeObserver(this); unawaited(_subscription?.cancel()); _subscription = null; + _scannerController.dispose(); super.dispose(); - await _scannerController.dispose(); } @override @@ -83,7 +84,7 @@ class _MihBarcodeScannerState extends State super.initState(); WidgetsBinding.instance.addObserver(this); _subscription = _scannerController.barcodes.listen(foundCode); - unawaited(_scannerController.start()); + // unawaited(_scannerController.start()); } @override diff --git a/Frontend/lib/mih_packages/mzansi_wallet/package_tools/mih_cards.dart b/Frontend/lib/mih_packages/mzansi_wallet/package_tools/mih_cards.dart index f82ebbbc..e806717c 100644 --- a/Frontend/lib/mih_packages/mzansi_wallet/package_tools/mih_cards.dart +++ b/Frontend/lib/mih_packages/mzansi_wallet/package_tools/mih_cards.dart @@ -4,24 +4,18 @@ import 'package:mzansi_innovation_hub/main.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_banner_ad.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_alert.dart'; import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart'; -import 'package:mzansi_innovation_hub/mih_services/mih_alert_services.dart'; +import 'package:mzansi_innovation_hub/mih_packages/mzansi_wallet/components/mih_add_card_window.dart'; import 'package:mzansi_innovation_hub/mih_services/mih_mzansi_wallet_services.dart'; -import 'package:mzansi_innovation_hub/mih_services/mih_validation_services.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_single_child_scroll.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_button.dart'; -import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_dropdwn_field.dart'; -import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_form.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_tool_body.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_floating_menu.dart'; -import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_window.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_search_bar.dart'; -import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_text_form_field.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_pop_up_messages/mih_error_message.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_pop_up_messages/mih_loading_circle.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_objects/app_user.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_objects/loyalty_card.dart'; import 'package:mzansi_innovation_hub/mih_packages/mzansi_wallet/builder/build_loyalty_card_list.dart'; -import 'package:mzansi_innovation_hub/mih_packages/mzansi_wallet/components/mih_card_display.dart'; import 'package:flutter/material.dart'; import 'package:mobile_scanner/mobile_scanner.dart'; @@ -37,19 +31,12 @@ class MihCards extends StatefulWidget { } class _MihCardsState extends State { - final TextEditingController shopController = TextEditingController(); - final TextEditingController _nicknameController = TextEditingController(); - final TextEditingController cardNumberController = TextEditingController(); final TextEditingController cardSearchController = TextEditingController(); final FocusNode searchFocusNode = FocusNode(); - final _formKey = GlobalKey(); late Future> cardList; MihBannerAd _bannerAd = MihBannerAd(); List listOfCards = []; - //bool showSelectedCardType = false; - final ValueNotifier shopName = ValueNotifier(""); final ValueNotifier> searchShopName = ValueNotifier([]); - final MobileScannerController scannerController = MobileScannerController( detectionSpeed: DetectionSpeed.unrestricted, ); @@ -74,21 +61,6 @@ class _MihCardsState extends State { } } - void openscanner() async { - context.pushNamed( - "barcodeScanner", - extra: cardNumberController, - ); - } - - void shopSelected() { - if (shopController.text.isNotEmpty) { - shopName.value = shopController.text; - } else { - shopName.value = ""; - } - } - void successPopUp(String title, String message, int packageIndex) { showDialog( context: context, @@ -116,9 +88,6 @@ class _MihCardsState extends State { Center( child: MihButton( onPressed: () { - shopController.clear(); - _nicknameController.clear(); - cardNumberController.clear(); context.pop(); }, buttonColor: MihColors.getGreenColor( @@ -161,245 +130,25 @@ class _MihCardsState extends State { showDialog( context: context, barrierDismissible: false, - builder: (context) => MihPackageWindow( - fullscreen: false, - windowTitle: "Add New Loyalty Card", - onWindowTapClose: () { - shopController.clear(); - cardNumberController.clear(); - _nicknameController.clear(); - shopName.value = ""; - Navigator.pop(context); - }, - windowBody: Padding( - padding: - MzansiInnovationHub.of(context)!.theme.screenType == "desktop" - ? EdgeInsets.symmetric(horizontal: width * 0.05) - : EdgeInsets.symmetric(horizontal: width * 0), - child: Column( - children: [ - MihForm( - formKey: _formKey, - formFields: [ - MihDropdownField( - controller: shopController, - hintText: "Shop Name", - editable: true, - enableSearch: true, - validator: (value) { - return MihValidationServices().isEmpty(value); - }, - requiredText: true, - dropdownOptions: const [ - "+More", - "Apple Tree", - "Auchan", - "Best Before", - "Big Save", - "Boxer", - "BP", - "Builders Warehouse", - "Checkers", - "Choppies", - "Clicks", - "Continente", - "Cotton:On", - "Carrefour", - "Dis-Chem", - "Edgars", - "Eskom", - "Exclusive Books", - "Fresh Stop", - "Fresmart", - "Infinity", - "Jet", - "Justrite", - "Kero", - "Leroy Merlin", - "Makro", - "Naivas", - "OK Foods", - "Panarottis", - "Pick n Pay", - "PnA", - "PQ Clothing", - "Rage", - "Sefalana", - "Sasol", - "Shell", - "Shoprite", - "Signature Cosmetics & Fragrances", - "Spar", - "Spur", - "TFG Group", - "Total Energies", - "Toys R Us", - "Woermann Brock", - "Woolworths" - ], - ), - ValueListenableBuilder( - valueListenable: shopName, - builder: - (BuildContext context, String value, Widget? child) { - return Visibility( - visible: value != "", - child: Column( - children: [ - const SizedBox(height: 10), - MihCardDisplay( - shopName: shopName.value, - nickname: "", - height: 200), - ], - ), - ); - }, - ), - const SizedBox(height: 10), - MihTextFormField( - fillColor: MihColors.getSecondaryColor( - MzansiInnovationHub.of(context)!.theme.mode == "Dark"), - inputColor: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)!.theme.mode == "Dark"), - controller: _nicknameController, - multiLineInput: false, - requiredText: false, - hintText: "Card Title", - ), - const SizedBox(height: 10), - Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.end, - mainAxisSize: MainAxisSize.max, - children: [ - Flexible( - child: MihTextFormField( - fillColor: MihColors.getSecondaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - inputColor: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - controller: cardNumberController, - multiLineInput: false, - requiredText: true, - hintText: "Card Number", - numberMode: true, - validator: (value) { - return MihValidationServices().isEmpty(value); - }, - ), - ), - const SizedBox(width: 20), - MihButton( - onPressed: () { - openscanner(); - }, - buttonColor: MihColors.getSecondaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - width: 100, - child: Text( - "Scan", - style: TextStyle( - color: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - fontSize: 20, - fontWeight: FontWeight.bold, - ), - ), - ), - ], - ), - const SizedBox(height: 15), - Center( - child: MihButton( - onPressed: () async { - if (_formKey.currentState!.validate()) { - if (shopController.text == "") { - showDialog( - context: context, - builder: (context) { - return const MIHErrorMessage( - errorType: "Input Error"); - }, - ); - } else { - int statusCode = - await MIHMzansiWalletApis.addLoyaltyCardAPICall( - widget.signedInUser, - widget.signedInUser.app_id, - shopController.text, - cardNumberController.text, - "", - 0, - _nicknameController.text, - context, - ); - if (statusCode == 201) { - setState(() { - cardList = MIHMzansiWalletApis.getLoyaltyCards( - widget.signedInUser.app_id); - }); - context.pop(); - successPopUp( - "Successfully Added Card", - "The loyalty card has been added to your favourites.", - 0, - ); - } else { - internetConnectionPopUp(); - } - } - } else { - MihAlertServices().formNotFilledCompletely(context); - } - }, - buttonColor: MihColors.getGreenColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - width: 300, - child: Text( - "Add", - style: TextStyle( - color: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - fontSize: 20, - fontWeight: FontWeight.bold, - ), - ), - ), - ), - ], - ), - ], - ), - ), + builder: (context) => MihAddCardWindow( + signedInUser: widget.signedInUser, + cardList: cardList, ), ); } @override void dispose() { - cardNumberController.dispose(); - shopController.removeListener(shopSelected); - shopController.dispose(); cardSearchController.removeListener(searchShop); cardSearchController.dispose(); searchShopName.dispose(); - _nicknameController.dispose(); searchFocusNode.dispose(); - shopName.dispose(); super.dispose(); } @override void initState() { cardList = MIHMzansiWalletApis.getLoyaltyCards(widget.signedInUser.app_id); - shopController.addListener(shopSelected); cardSearchController.addListener(searchShop); super.initState(); }