From 64eaa380796a541a8a53ff44783a7d32c7916c0a Mon Sep 17 00:00:00 2001 From: Prince Date: Wed, 17 Jun 2026 14:42:46 +0200 Subject: [PATCH] added zwg support --- lib/models/user_model.dart | 43 +++ .../widgets/account_balance_widget.dart | 355 ++++++++---------- lib/screens/confirm/confirm_screen.dart | 9 + lib/screens/home/home_screen.dart | 47 ++- lib/screens/onboarding/auth_provider.dart | 18 + .../onboarding/login/login_controller.dart | 7 + lib/screens/pay/pay_controller.dart | 2 +- lib/screens/pay/pay_screen.dart | 14 +- lib/screens/receipt/receipt_controller.dart | 19 +- lib/screens/recipient/recipients_screen.dart | 24 +- lib/screens/transaction_controller.dart | 6 + lib/widgets/transaction_item_widget.dart | 27 +- 12 files changed, 324 insertions(+), 247 deletions(-) create mode 100644 lib/models/user_model.dart create mode 100644 lib/screens/onboarding/auth_provider.dart diff --git a/lib/models/user_model.dart b/lib/models/user_model.dart new file mode 100644 index 0000000..2f98139 --- /dev/null +++ b/lib/models/user_model.dart @@ -0,0 +1,43 @@ +class UserModel { + final String token; + final String type; + final String username; + final String email; + final String phone; + final String firstName; + final String uuid; + + UserModel({ + required this.token, + required this.type, + required this.username, + required this.email, + required this.phone, + required this.firstName, + required this.uuid, + }); + + factory UserModel.fromJson(Map json) { + return UserModel( + token: json['token'] as String, + type: json['type'] as String, + username: json['username'] as String, + email: json['email'] as String, + phone: json['phone'] as String, + firstName: json['firstName'] as String, + uuid: json['uuid'] as String, + ); + } + + Map toJson() { + return { + 'token': token, + 'type': type, + 'username': username, + 'email': email, + 'phone': phone, + 'firstName': firstName, + 'uuid': uuid, + }; + } +} \ No newline at end of file diff --git a/lib/screens/accounts/widgets/account_balance_widget.dart b/lib/screens/accounts/widgets/account_balance_widget.dart index ea95d64..a8eda20 100644 --- a/lib/screens/accounts/widgets/account_balance_widget.dart +++ b/lib/screens/accounts/widgets/account_balance_widget.dart @@ -5,14 +5,20 @@ import 'package:qpay/screens/accounts/models/account_model.dart'; import 'package:shared_preferences/shared_preferences.dart'; class AccountBalanceWidget extends StatefulWidget { - const AccountBalanceWidget({super.key}); + final String? selectedCurrency; + final ValueChanged? onCurrencySelected; + + const AccountBalanceWidget({ + super.key, + this.selectedCurrency, + this.onCurrencySelected, + }); @override State createState() => _AccountBalanceWidgetState(); } -class _AccountBalanceWidgetState extends State - with SingleTickerProviderStateMixin { +class _AccountBalanceWidgetState extends State { late AccountProvider _accountProvider; AccountModel? _usdAccount; @@ -23,22 +29,15 @@ class _AccountBalanceWidgetState extends State String? _zwgError; String? _phoneNumber; - late AnimationController _pulseController; - @override void initState() { super.initState(); - _pulseController = AnimationController( - vsync: this, - duration: const Duration(milliseconds: 800), - ); _accountProvider = AccountProvider(); _loadBalances(); } @override void dispose() { - _pulseController.dispose(); _accountProvider.dispose(); super.dispose(); } @@ -85,10 +84,6 @@ class _AccountBalanceWidgetState extends State } }); } - - if (mounted) { - _pulseController.forward(); - } } @override @@ -101,46 +96,39 @@ class _AccountBalanceWidgetState extends State } return Padding( - padding: const EdgeInsets.only(bottom: 16), + padding: const EdgeInsets.only(bottom: 12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - _buildSectionLabel( - theme, - isDark, - Icons.account_balance_wallet_rounded, - 'Account Balances', - ), - const SizedBox(height: 12), Row( children: [ Expanded( - child: _buildBalanceCard( + child: _buildCompactCard( theme: theme, isDark: isDark, currencyCode: 'USD', - currencyName: 'US Dollar', flagAsset: 'united-states.png', balance: _usdAccount?.balance, isLoading: _isLoadingUsd, error: _usdError, + isSelected: widget.selectedCurrency == 'USD', gradientColors: [ const Color(0xFF0D47A1), const Color(0xFF1976D2), ], ), ), - const SizedBox(width: 12), + const SizedBox(width: 10), Expanded( - child: _buildBalanceCard( + child: _buildCompactCard( theme: theme, isDark: isDark, currencyCode: 'ZWG', - currencyName: 'Zimbabwe Gold', flagAsset: 'zimbabwe.png', balance: _zwgAccount?.balance, isLoading: _isLoadingZwG, error: _zwgError, + isSelected: widget.selectedCurrency == 'ZWG', gradientColors: [ const Color(0xFF1B5E20), const Color(0xFF388E3C), @@ -149,42 +137,38 @@ class _AccountBalanceWidgetState extends State ), ], ), - const SizedBox(height: 8), + const SizedBox(height: 6), Center( child: TextButton.icon( onPressed: _isLoadingUsd || _isLoadingZwG ? null : () { - _pulseController.reset(); _loadBalances(); }, - icon: AnimatedBuilder( - animation: _pulseController, - builder: (context, child) { - return Transform.rotate( - angle: _pulseController.value * 6.2832, - child: Icon( - Icons.refresh_rounded, - size: 16, - color: _isLoadingUsd || _isLoadingZwG - ? (isDark ? Colors.white24 : Colors.grey.shade400) - : theme.colorScheme.primary, - ), - ); - }, + icon: Icon( + Icons.refresh_rounded, + size: 14, + color: _isLoadingUsd || _isLoadingZwG + ? (isDark ? Colors.white24 : Colors.grey.shade400) + : theme.colorScheme.primary, ), label: Text( _isLoadingUsd || _isLoadingZwG ? 'Refreshing...' - : 'Refresh Balances', + : 'Refresh', style: TextStyle( - fontSize: 12, + fontSize: 11, fontWeight: FontWeight.w600, color: _isLoadingUsd || _isLoadingZwG ? (isDark ? Colors.white24 : Colors.grey.shade400) : theme.colorScheme.primary, ), ), + style: TextButton.styleFrom( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + minimumSize: Size.zero, + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + ), ), ), ], @@ -192,197 +176,158 @@ class _AccountBalanceWidgetState extends State ); } - Widget _buildBalanceCard({ + Widget _buildCompactCard({ required ThemeData theme, required bool isDark, required String currencyCode, - required String currencyName, required String flagAsset, double? balance, required bool isLoading, String? error, + required bool isSelected, required List gradientColors, }) { - return Container( - padding: const EdgeInsets.all(16), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - gradient: LinearGradient( - colors: gradientColors, - begin: Alignment.topLeft, - end: Alignment.bottomRight, - ), - boxShadow: [ - BoxShadow( - color: gradientColors[0].withValues(alpha: 0.3), - blurRadius: 12, - offset: const Offset(0, 4), + return GestureDetector( + onTap: () { + widget.onCurrencySelected?.call(currencyCode); + }, + child: AnimatedContainer( + duration: const Duration(milliseconds: 200), + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16), + gradient: LinearGradient( + colors: gradientColors, + begin: Alignment.topLeft, + end: Alignment.bottomRight, ), - ], - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Top row: flag + currency code - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - mainAxisSize: MainAxisSize.min, - children: [ - Container( - width: 24, - height: 24, - decoration: BoxDecoration( - shape: BoxShape.circle, - border: Border.all( - color: Colors.white.withValues(alpha: 0.3), - width: 1.5, - ), + boxShadow: [ + BoxShadow( + color: gradientColors[0].withValues(alpha: isSelected ? 0.5 : 0.25), + blurRadius: isSelected ? 12 : 6, + offset: Offset(0, isSelected ? 4 : 2), + ), + ], + border: isSelected + ? Border.all( + color: Colors.white.withValues(alpha: 0.8), + width: 2, + ) + : Border.all( + color: Colors.white.withValues(alpha: 0.15), + width: 1, + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + // Top row: flag + currency code + Row( + children: [ + Container( + width: 22, + height: 22, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: Colors.white.withValues(alpha: 0.3), + width: 1.5, ), - child: ClipOval( - child: Image.asset( - 'assets/$flagAsset', - fit: BoxFit.cover, - errorBuilder: (_, __, ___) => Icon( - Icons.monetization_on_outlined, - size: 14, - color: Colors.white.withValues(alpha: 0.8), - ), + ), + child: ClipOval( + child: Image.asset( + 'assets/$flagAsset', + fit: BoxFit.cover, + errorBuilder: (_, __, ___) => Icon( + Icons.monetization_on_outlined, + size: 12, + color: Colors.white.withValues(alpha: 0.8), ), ), ), - const SizedBox(width: 8), - Text( + ), + const SizedBox(width: 6), + Expanded( + child: Text( currencyCode, style: const TextStyle( - fontSize: 13, + fontSize: 12, fontWeight: FontWeight.w700, color: Colors.white, letterSpacing: 0.5, ), ), - ], - ), - Container( - padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), - decoration: BoxDecoration( - color: Colors.white.withValues(alpha: 0.2), - borderRadius: BorderRadius.circular(8), ), - child: Text( - currencyName, - style: TextStyle( - fontSize: 8, - fontWeight: FontWeight.w500, - color: Colors.white.withValues(alpha: 0.9), + if (isSelected) + Container( + width: 18, + height: 18, + decoration: BoxDecoration( + color: Colors.white.withValues(alpha: 0.3), + shape: BoxShape.circle, + ), + child: const Icon( + Icons.check_rounded, + size: 12, + color: Colors.white, + ), ), + ], + ), + const SizedBox(height: 10), + // Balance amount + if (isLoading) + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 70, + height: 12, + decoration: BoxDecoration( + color: Colors.white.withValues(alpha: 0.3), + borderRadius: BorderRadius.circular(4), + ), + ), + ], + ) + else if (error != null) + Row( + children: [ + Icon( + Icons.error_outline_rounded, + color: Colors.white.withValues(alpha: 0.7), + size: 16, + ), + const SizedBox(width: 4), + Text( + 'Unavailable', + style: TextStyle( + fontSize: 10, + fontWeight: FontWeight.w500, + color: Colors.white.withValues(alpha: 0.7), + ), + ), + ], + ) + else ...[ + Text( + _formatBalance(balance ?? 0.0), + style: const TextStyle( + fontSize: 17, + fontWeight: FontWeight.w800, + color: Colors.white, + letterSpacing: 0.5, + height: 1.1, ), ), ], - ), - const SizedBox(height: 16), - // Balance amount - if (isLoading) - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - width: 80, - height: 14, - decoration: BoxDecoration( - color: Colors.white.withValues(alpha: 0.3), - borderRadius: BorderRadius.circular(4), - ), - ), - const SizedBox(height: 4), - Container( - width: 50, - height: 10, - decoration: BoxDecoration( - color: Colors.white.withValues(alpha: 0.2), - borderRadius: BorderRadius.circular(4), - ), - ), - ], - ) - else if (error != null) - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Icon( - Icons.error_outline_rounded, - color: Colors.white.withValues(alpha: 0.7), - size: 20, - ), - const SizedBox(height: 4), - Text( - 'Unavailable', - style: TextStyle( - fontSize: 11, - fontWeight: FontWeight.w500, - color: Colors.white.withValues(alpha: 0.7), - ), - ), - ], - ) - else ...[ - Text( - _formatBalance(balance ?? 0.0), - style: const TextStyle( - fontSize: 22, - fontWeight: FontWeight.w800, - color: Colors.white, - letterSpacing: 0.5, - height: 1.1, - ), - ), - const SizedBox(height: 4), - Text( - currencyCode == 'USD' ? 'Available Balance' : 'Available Balance', - style: TextStyle( - fontSize: 10, - fontWeight: FontWeight.w400, - color: Colors.white.withValues(alpha: 0.7), - ), - ), ], - ], + ), ), ); } - Widget _buildSectionLabel( - ThemeData theme, - bool isDark, - IconData icon, - String label, - ) { - return Row( - children: [ - Container( - width: 32, - height: 32, - decoration: BoxDecoration( - color: theme.colorScheme.primary.withValues(alpha: 0.1), - borderRadius: BorderRadius.circular(10), - ), - child: Icon(icon, size: 16, color: theme.colorScheme.primary), - ), - const SizedBox(width: 10), - Text( - label, - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w700, - color: isDark ? Colors.white : Colors.black87, - letterSpacing: 0.3, - ), - ), - ], - ); - } - String _formatBalance(double balance) { final formatter = NumberFormat('#,##0.00', 'en_US'); return '\$${formatter.format(balance)}'; diff --git a/lib/screens/confirm/confirm_screen.dart b/lib/screens/confirm/confirm_screen.dart index 90b598e..c2d1af1 100644 --- a/lib/screens/confirm/confirm_screen.dart +++ b/lib/screens/confirm/confirm_screen.dart @@ -370,6 +370,15 @@ class _ConfirmScreenState extends State children: [ _buildDetailRow( icon: Icons.monetization_on_outlined, + label: "Currency", + value: controller + .transactionController + .model + .confirmationData["debitCurrency"] + .toString(), + ), + _buildDetailRow( + icon: Icons.money, label: "Amount", value: controller .transactionController diff --git a/lib/screens/home/home_screen.dart b/lib/screens/home/home_screen.dart index cea0600..88aedc4 100644 --- a/lib/screens/home/home_screen.dart +++ b/lib/screens/home/home_screen.dart @@ -1,3 +1,4 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:provider/provider.dart'; @@ -8,7 +9,6 @@ import 'package:qpay/screens/home/home_controller.dart'; import 'package:qpay/screens/receipt/receipt_controller.dart'; import 'package:qpay/screens/transaction_controller.dart'; import 'package:qpay/screens/transactions/transaction_model.dart' as txn; -import 'package:qpay/widgets/app_snack_bar.dart'; import 'package:qpay/widgets/transaction_item_widget.dart'; import 'package:skeletonizer/skeletonizer.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -28,8 +28,8 @@ class _HomeScreenState extends State with TickerProviderStateMixin { late final router = GoRouter.of(context); bool _isLoggedIn = false; - bool _isZWGSelected = false; String initials = ''; + String _selectedCurrency = 'USD'; late AnimationController _providersAnimController; late Animation _providersSlideAnimation; @@ -107,7 +107,10 @@ class _HomeScreenState extends State with TickerProviderStateMixin { homeController = HomeController(context); await homeController.getAccounts(); await homeController.getCategories(); - await homeController.getProviders(_isZWGSelected ? "ZWG" : "USD"); + await homeController.getProviders(_selectedCurrency); + + transactionController.model.formData = transactionController.model.formData + .copyWith(debitCurrency: _selectedCurrency); prefs = await SharedPreferences.getInstance(); @@ -127,10 +130,22 @@ class _HomeScreenState extends State with TickerProviderStateMixin { await homeController.getTransactions(prefs.getString("userId")!); } + void _onCurrencySelected(String currency) { + if (_selectedCurrency == currency) return; + + setState(() { + _selectedCurrency = currency; + }); + + homeController.getProviders(currency); + + transactionController.updateCurrency(currency); + } + Future _onRefresh() async { await homeController.getAccounts(); await homeController.getCategories(); - await homeController.getProviders(_isZWGSelected ? "ZWG" : "USD"); + await homeController.getProviders(_selectedCurrency); prefs = await SharedPreferences.getInstance(); if (prefs.getString("userId") != null) { @@ -153,9 +168,21 @@ class _HomeScreenState extends State with TickerProviderStateMixin { /// Handles the repeat action for a transaction. Future _repeatTransaction(Map item) async { - final receiptController = ReceiptController(context); - final transaction = txn.TransactionModel.fromJson(item); - await receiptController.repeatTransaction(transaction); + try { + final receiptController = ReceiptController(context); + final transaction = txn.TransactionModel.fromJson(item); + await receiptController.repeatTransaction(transaction); + } catch (e, s) { + if (kDebugMode) { + print(e); + print(s); + } + if (mounted) { + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text('Error repeating transaction'))); + } + } if (mounted) { context.push('/make-payment'); } @@ -370,7 +397,11 @@ class _HomeScreenState extends State with TickerProviderStateMixin { _buildLoginPrompt(theme, isDark), const SizedBox(height: 20), ], - if (_isLoggedIn) const AccountBalanceWidget(), + if (_isLoggedIn) + AccountBalanceWidget( + selectedCurrency: _selectedCurrency, + onCurrencySelected: _onCurrencySelected, + ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ diff --git a/lib/screens/onboarding/auth_provider.dart b/lib/screens/onboarding/auth_provider.dart new file mode 100644 index 0000000..5884833 --- /dev/null +++ b/lib/screens/onboarding/auth_provider.dart @@ -0,0 +1,18 @@ +import 'dart:convert'; + +import 'package:shared_preferences/shared_preferences.dart'; + +import '../../models/user_model.dart'; + +class AuthProvider { + // get the user model from shared preferences + static Future getUserModel() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + String? userModelString = prefs.getString("userModel"); + if (userModelString != null) { + Map userModelJson = jsonDecode(userModelString); + return UserModel.fromJson(userModelJson); + } + return null; + } +} diff --git a/lib/screens/onboarding/login/login_controller.dart b/lib/screens/onboarding/login/login_controller.dart index 529e70c..3936895 100644 --- a/lib/screens/onboarding/login/login_controller.dart +++ b/lib/screens/onboarding/login/login_controller.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:qpay/auth_state.dart'; @@ -5,6 +7,8 @@ import 'package:qpay/http/http.dart'; import 'package:qpay/models/api_response.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import '../../../models/user_model.dart'; + class LoginScreenModel { String username = ""; String password = ""; @@ -72,6 +76,9 @@ class LoginController extends ChangeNotifier { : ''), ); + UserModel userModel = UserModel.fromJson(response); + prefs.setString("userModel", jsonEncode(userModel)); + // Notify the router that the user is now signed in so any // auth-gated redirects (e.g. the groups flow) are re-evaluated // and the user is sent to their originally requested page. diff --git a/lib/screens/pay/pay_controller.dart b/lib/screens/pay/pay_controller.dart index 116c336..9bb7966 100644 --- a/lib/screens/pay/pay_controller.dart +++ b/lib/screens/pay/pay_controller.dart @@ -89,7 +89,7 @@ class PayController extends ChangeNotifier { type: "CONFIRM", billClientId: model.selectedProvider?.clientId ?? '', debitRef: 'Velocity', - debitCurrency: 'USD', + debitCurrency: transactionController.model.currency, creditAccount: transactionController.model.formData.creditAccount, creditPhone: transactionController.model.formData.creditPhone, billName: model.selectedProvider?.name ?? '', diff --git a/lib/screens/pay/pay_screen.dart b/lib/screens/pay/pay_screen.dart index 8704a6e..08e9ed7 100644 --- a/lib/screens/pay/pay_screen.dart +++ b/lib/screens/pay/pay_screen.dart @@ -259,6 +259,12 @@ class _PayScreenState extends State with TickerProviderStateMixin { .formData .creditAccount, ), + _buildDetailRow( + icon: Icons.monetization_on, + label: "Currency", + value: + transactionController.model.currency, + ), ], ), SizedBox(height: 20), @@ -643,7 +649,7 @@ class _PayScreenState extends State with TickerProviderStateMixin { const SizedBox(height: 5), DropdownButtonHideUnderline( child: DropdownButtonFormField( - initialValue: controller.model.selectedProduct?.uid, + initialValue: transactionController.model.selectedProduct?.name, isExpanded: true, icon: const Icon(Icons.arrow_drop_down), decoration: InputDecoration( @@ -661,10 +667,10 @@ class _PayScreenState extends State with TickerProviderStateMixin { DropdownMenuItem(child: Text("Select Product")), ...controller.model.products.map( (e) => DropdownMenuItem( - value: e.uid, + value: e.name, child: Text( '${e.displayName} - ' - '${transactionController.model.formData.debitCurrency}${e.defaultAmount}', + '${transactionController.model.currency}${e.defaultAmount}', ), ), ), @@ -718,7 +724,7 @@ class _PayScreenState extends State with TickerProviderStateMixin { ), const SizedBox(width: 8), Text( - "TRANSACTION DETAILS", + "PROVIDER DETAILS", style: TextStyle( fontSize: 13, fontWeight: FontWeight.w700, diff --git a/lib/screens/receipt/receipt_controller.dart b/lib/screens/receipt/receipt_controller.dart index f422e68..74f8e5e 100644 --- a/lib/screens/receipt/receipt_controller.dart +++ b/lib/screens/receipt/receipt_controller.dart @@ -82,8 +82,8 @@ class ReceiptController extends ChangeNotifier { Map provider = await http.get( '/public/providers/client/$providerId', ); - transactionController.model.selectedProvider = hc.BillProvider.fromJson( - provider, + transactionController.updateSelectedProvider( + hc.BillProvider.fromJson(provider), ); // update tran controller formdata with credit account, email, name, phone & providerLabel @@ -96,19 +96,20 @@ class ReceiptController extends ChangeNotifier { ); // fetch product and update tran controller formdata - String providerUid = - transactionController.model.selectedProvider?.uid ?? ''; - String productUid = transaction.productUid ?? ''; + String providerUid = transactionController.model.selectedProvider?.id ?? ''; + String productName = transaction.billProductName ?? ''; - if (productUid.isNotEmpty) { + if (productName.isNotEmpty) { Map product = await http.get( - '/public/providers/$providerUid/products/$productUid', + '/public/providers/$providerUid/products/$productName', ); - transactionController.model.selectedProduct = pc.BillProduct.fromJson( - product, + transactionController.updateSelectedProduct( + pc.BillProduct.fromJson(product), ); } + transactionController.updateCurrency(transaction.debitCurrency); + // update credit amount, notification phone number, payment processor transactionController.model.formData = transactionController.model.formData .copyWith( diff --git a/lib/screens/recipient/recipients_screen.dart b/lib/screens/recipient/recipients_screen.dart index f093ebe..b0d1e78 100644 --- a/lib/screens/recipient/recipients_screen.dart +++ b/lib/screens/recipient/recipients_screen.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:convert'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -6,11 +7,14 @@ import 'package:flutter_contacts/flutter_contacts.dart'; import 'package:go_router/go_router.dart'; import 'package:provider/provider.dart'; import 'package:qpay/models/responsive_policy.dart'; +import 'package:qpay/models/user_model.dart'; import 'package:qpay/screens/transaction_controller.dart'; import 'package:skeletonizer/skeletonizer.dart'; import 'package:qpay/screens/recipient/recipients_controller.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import '../onboarding/auth_provider.dart'; + class RecipientsScreen extends StatefulWidget { const RecipientsScreen({super.key}); @@ -66,6 +70,16 @@ class _RecipientsScreenState extends State controller.getRecipients(_queryParams); } + Future getAuthenticatedUser() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + String? userModelString = prefs.getString("userModel"); + if (userModelString != null) { + Map userModelJson = jsonDecode(userModelString); + return UserModel.fromJson(userModelJson); + } + return null; + } + @override void dispose() { _animationController.dispose(); @@ -577,7 +591,7 @@ class _RecipientsScreenState extends State ), const SizedBox(width: 8), Text( - "TRANSACTION DETAILS", + "PROVIDER DETAILS", style: TextStyle( fontSize: 13, fontWeight: FontWeight.w700, @@ -592,12 +606,18 @@ class _RecipientsScreenState extends State const SizedBox(height: 16), if (children != null) ...children - else + else ...[ _buildDetailRow( icon: Icons.business_outlined, label: label, value: value ?? '', ), + _buildDetailRow( + icon: Icons.monetization_on, + label: "Currency", + value: transactionController.model.currency, + ), + ], ], ), ), diff --git a/lib/screens/transaction_controller.dart b/lib/screens/transaction_controller.dart index b155332..dd79d93 100644 --- a/lib/screens/transaction_controller.dart +++ b/lib/screens/transaction_controller.dart @@ -14,6 +14,7 @@ class TransactionModel { BillProduct? selectedProduct; BillProvider? selectedProvider; Map? receiptData; + String currency = 'USD'; FormData formData = FormData( type: '', billClientId: '', @@ -118,6 +119,11 @@ class TransactionController extends ChangeNotifier { notifyListeners(); } + updateCurrency(String currency) { + model.currency = currency; + notifyListeners(); + } + clearFormData() { model.formData = FormData( id: null, diff --git a/lib/widgets/transaction_item_widget.dart b/lib/widgets/transaction_item_widget.dart index fb4c4bf..843f2a0 100644 --- a/lib/widgets/transaction_item_widget.dart +++ b/lib/widgets/transaction_item_widget.dart @@ -46,13 +46,13 @@ class _TransactionItemWidgetState extends State { final statusIcon = isSuccess ? Icons.check_circle_rounded : isPending - ? Icons.pending_rounded - : Icons.cancel_rounded; + ? Icons.pending_rounded + : Icons.cancel_rounded; final statusColor = isSuccess ? const Color(0xFF10B981) : isPending - ? const Color(0xFFF59E0B) - : const Color(0xFFEF4444); + ? const Color(0xFFF59E0B) + : const Color(0xFFEF4444); return Skeletonizer( enabled: widget.enabled, @@ -66,8 +66,7 @@ class _TransactionItemWidgetState extends State { context.push("/receipt/${widget.transaction['id']}"); }, child: Container( - padding: - const EdgeInsets.symmetric(vertical: 12, horizontal: 14), + padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 14), decoration: BoxDecoration( color: isDark ? const Color(0xFF1A1A2E) : Colors.white, borderRadius: BorderRadius.circular(16), @@ -138,11 +137,7 @@ class _TransactionItemWidgetState extends State { ), ), const SizedBox(width: 8), - Icon( - statusIcon, - size: 16, - color: statusColor, - ), + Icon(statusIcon, size: 16, color: statusColor), ], ), const SizedBox(height: 4), @@ -172,9 +167,7 @@ class _TransactionItemWidgetState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ Text( - (widget.transaction['amount'] as num?) - ?.toStringAsFixed(2) ?? - '', + '${(widget.transaction['amount'] as num?)?.toStringAsFixed(2) ?? ''} ${widget.transaction['debitCurrency']}', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w700, @@ -182,9 +175,7 @@ class _TransactionItemWidgetState extends State { ), ), Text( - (widget.transaction['totalAmount'] as num?) - ?.toStringAsFixed(2) ?? - '', + '${(widget.transaction['totalAmount'] as num?)?.toStringAsFixed(2) ?? ''} ${widget.transaction['debitCurrency']}', style: TextStyle( fontSize: 12, fontWeight: FontWeight.w500, @@ -246,4 +237,4 @@ class _TransactionItemWidgetState extends State { ), ); } -} \ No newline at end of file +}