From 6e2f91d2c04fcc3c74a2698ad74edd9b5bb79926 Mon Sep 17 00:00:00 2001 From: Vus Khoza Date: Mon, 1 Sep 2025 20:17:08 +0200 Subject: [PATCH] ui improvements --- .vscode/launch.json | 8 + assets/.env | 13 +- assets/apple.png | Bin 0 -> 11215 bytes assets/google.png | Bin 0 -> 13834 bytes assets/landing.png | Bin 0 -> 8390 bytes assets/password.png | Bin 0 -> 6285 bytes assets/united-states.png | Bin 0 -> 24631 bytes assets/verify.png | Bin 0 -> 9141 bytes assets/zimbabwe.png | Bin 0 -> 21422 bytes lib/main.dart | 30 ++ lib/screens/confirm/confirm_controller.dart | 6 +- lib/screens/confirm/confirm_screen.dart | 170 ++++--- lib/screens/gateway/gateway_controller.dart | 4 +- lib/screens/history/history_controller.dart | 2 +- lib/screens/home/home_controller.dart | 51 +- lib/screens/home/home_screen.dart | 447 ++++++++++++++++-- .../onboarding/landing/landing_screen.dart | 90 ++++ .../onboarding/login/login_controller.dart | 80 ++++ .../onboarding/login/login_screen.dart | 231 +++++++++ .../onboarding/password/password_screen.dart | 255 ++++++++++ .../onboarding/register/register_screen.dart | 194 ++++++++ .../onboarding/verify/verify_screen.dart | 188 ++++++++ lib/screens/pay/pay_controller.dart | 6 +- lib/screens/receipt/receipt_controller.dart | 6 +- lib/screens/receipt/receipt_screen.dart | 222 +++++---- .../recipient/recipients_controller.dart | 2 +- lib/screens/transaction_controller.dart | 2 + .../transaction_controller.freezed.dart | 27 +- lib/screens/transaction_controller.g.dart | 2 + lib/theme/app_theme.dart | 10 +- 30 files changed, 1822 insertions(+), 224 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 assets/apple.png create mode 100644 assets/google.png create mode 100644 assets/landing.png create mode 100644 assets/password.png create mode 100644 assets/united-states.png create mode 100644 assets/verify.png create mode 100644 assets/zimbabwe.png create mode 100644 lib/screens/onboarding/landing/landing_screen.dart create mode 100644 lib/screens/onboarding/login/login_controller.dart create mode 100644 lib/screens/onboarding/login/login_screen.dart create mode 100644 lib/screens/onboarding/password/password_screen.dart create mode 100644 lib/screens/onboarding/register/register_screen.dart create mode 100644 lib/screens/onboarding/verify/verify_screen.dart diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..b8d2d84 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,8 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + ] +} \ No newline at end of file diff --git a/assets/.env b/assets/.env index 8e8e1bf..f248619 100644 --- a/assets/.env +++ b/assets/.env @@ -1,6 +1,7 @@ -# BASE_URL=https://peakapi.qantra.co.zw/api -BASE_URL=http://192.168.100.8:6950/api -# BASE_URL=http://192.168.1.164:6950/api -# BASE_URL=http://192.168.120.160:6950/api -# BASE_URL=http://10.10.2.92:6950/api -# BASE_URL=http://172.20.5.105:6950/api +; BASE_URL=https://peakapi.qantra.co.zw/api + BASE_URL=http://192.168.100.8:6950/api +; BASE_URL=http://192.168.1.164:6950/api +; BASE_URL=http://192.168.120.160:6950/api +; BASE_URL=http://10.10.2.92:6950/api +; BASE_URL=http://172.20.5.105:6950/api +; BASE_URL=http://10.10.3.92:6950/api \ No newline at end of file diff --git a/assets/apple.png b/assets/apple.png new file mode 100644 index 0000000000000000000000000000000000000000..2da6b5cd55c186b703a69fc2959a1cb6a49fe1b5 GIT binary patch literal 11215 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 d&zRR-D7444 literal 0 HcmV?d00001 diff --git a/assets/google.png b/assets/google.png new file mode 100644 index 0000000000000000000000000000000000000000..1a0f3f02cb68e0a554d899b1de898b55c342b08a GIT binary patch literal 13834 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 QdrZJkl2ezhkTDDTA2DLpUH||9 literal 0 HcmV?d00001 diff --git a/assets/password.png b/assets/password.png new file mode 100644 index 0000000000000000000000000000000000000000..8debbf6f6f74b27007778c74a07a4cd2900923ae GIT binary patch literal 6285 zcmaKx=Q|sY*T#(?5h679O6g z=GyW%DjhaIb{?+Io*#Wb+gsiHI(~a|y;k!0Y`JZ*ukvTh^KT=KzvoN;UH;D)2{i1dH6K0+SE5N;6tO?QLa?p}f=O4=A`m}f_Ldi;6 zc059>+Aa8Y%QMZVWLt~J>duCXdMF}>ykE=ESfJGQghY&myZm+pE}KE9Z(B@fOCa0i zguF4LqvpW2eu8m~*ARKq5J%1^+X?-9a5e4_{RX)BE6yGm-7EalED`7+dc29#-ZNag z;Ey(n0pmE&(2p^(V7SZPvNI72!C(PFDS;;YZx9JYUb8q`7Ug+S0QU@s0+H0QlANux zU;-DuUWQg6+A#>$ZZ*oNMu!D|k5EK2KSBZ(Bg2=S0r)S(M2X_UqHo%G85s~48zSaE zlr#7TIYu30G*%OkLkLnptsw!*)*QP10mZ3!5Pr)PQKXu%lXg*LYEQ5VXDf4qxH~3< z?g1H~{aJmrF(ipNGabrXGIH=b7|N*Yjqgzj�p@r}w-^GZEZ(Z?ej|VWc7#A>kct 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 literal 0 HcmV?d00001 diff --git a/assets/united-states.png b/assets/united-states.png new file mode 100644 index 0000000000000000000000000000000000000000..82b720f541d365cd81a9138d99949cf4222b4fcf GIT binary patch literal 24631 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 IYutbJf9x=p*#H0l literal 0 HcmV?d00001 diff --git a/assets/verify.png b/assets/verify.png new file mode 100644 index 0000000000000000000000000000000000000000..0ec9b931532519d392fc5138df6ced132f2ccb7b GIT binary patch literal 9141 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 literal 0 HcmV?d00001 diff --git a/lib/main.dart b/lib/main.dart index eb2b470..74f0c57 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,6 +3,11 @@ import 'package:provider/provider.dart'; import 'package:go_router/go_router.dart'; import 'package:qpay/screens/confirm/confirm_screen.dart'; import 'package:qpay/screens/gateway/gateway_screen.dart'; +import 'package:qpay/screens/onboarding/landing/landing_screen.dart'; +import 'package:qpay/screens/onboarding/login/login_screen.dart'; +import 'package:qpay/screens/onboarding/password/password_screen.dart'; +import 'package:qpay/screens/onboarding/register/register_screen.dart'; +import 'package:qpay/screens/onboarding/verify/verify_screen.dart'; import 'package:qpay/screens/pay/pay_screen.dart'; import 'package:qpay/screens/receipt/receipt_screen.dart'; import 'package:qpay/screens/recipient/recipients_screen.dart'; @@ -65,6 +70,11 @@ class _MyAppState extends State { routes: [ ShellRoute( builder: (context, state, child) { + // If the current location is an onboarding route, don't show the navbar + final location = GoRouterState.of(context).uri.toString(); + if (location.startsWith('/onboarding/')) { + return child; + } return ScaffoldWithNavBar(child: child); }, routes: [ @@ -102,6 +112,26 @@ class _MyAppState extends State { path: '/profile', builder: (context, state) => const ProfileScreen(), ), + GoRoute( + path: '/onboarding/landing', + builder: (context, state) => const LandingScreen(), + ), + GoRoute( + path: '/onboarding/register', + builder: (context, state) => const RegisterScreen(), + ), + GoRoute( + path: '/onboarding/verify', + builder: (context, state) => const VerifyScreen(), + ), + GoRoute( + path: '/onboarding/password', + builder: (context, state) => const PasswordScreen(), + ), + GoRoute( + path: '/onboarding/login', + builder: (context, state) => const LoginScreen(), + ), ], ), ], diff --git a/lib/screens/confirm/confirm_controller.dart b/lib/screens/confirm/confirm_controller.dart index 8f4b844..2ca9390 100644 --- a/lib/screens/confirm/confirm_controller.dart +++ b/lib/screens/confirm/confirm_controller.dart @@ -62,11 +62,12 @@ class ConfirmController extends ChangeNotifier { .formData = transactionController.model.formData.copyWith( type: "REQUEST", trace: transactionController.model.confirmationData['trace'], + id: transactionController.model.confirmationData['id'], authType: transactionController.model.selectedPaymentProcessor.authType, ); dynamic response = await http.post( - '/transaction', + '/public/transaction', transactionController.model.formData, ); logger.i(response.toString()); @@ -107,12 +108,11 @@ class ConfirmController extends ChangeNotifier { Future pollTransaction(String uid) async { while (!model.isCancelled) { - // Check again after delay in case it was cancelled during the delay if (model.isCancelled) break; try { - dynamic response = await http.get('/transaction/poll/$uid'); + dynamic response = await http.get('/public/transaction/poll/$uid'); logger.i(response.toString()); if (response['status'] == 'SUCCESS') { diff --git a/lib/screens/confirm/confirm_screen.dart b/lib/screens/confirm/confirm_screen.dart index 270115b..d38469f 100644 --- a/lib/screens/confirm/confirm_screen.dart +++ b/lib/screens/confirm/confirm_screen.dart @@ -200,72 +200,99 @@ class _ConfirmScreenState extends State ], ), const SizedBox(height: 10), - Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - Text( - "Our Charge", - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, + if (controller + .transactionController + .model + .confirmationData["charge"] != + 0) + Column( + children: [ + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + "Our Charge", + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + Text( + controller + .transactionController + .model + .confirmationData["charge"] + .toString(), + style: TextStyle(fontSize: 16), + ), + ], ), - ), - Text( - controller - .transactionController - .model - .confirmationData["charge"] - .toString(), - style: TextStyle(fontSize: 16), - ), - ], - ), - const SizedBox(height: 10), - Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - Text( - "Gateway Charge", - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, + const SizedBox(height: 10), + ], + ), + if (controller + .transactionController + .model + .confirmationData["gatewayCharge"] != + 0) + Column( + children: [ + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + "Gateway Charge", + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + Text( + controller + .transactionController + .model + .confirmationData["gatewayCharge"] + .toString(), + style: TextStyle(fontSize: 16), + ), + ], ), - ), - Text( - controller - .transactionController - .model - .confirmationData["gatewayCharge"] - .toString(), - style: TextStyle(fontSize: 16), - ), - ], - ), - const SizedBox(height: 10), - Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - Text( - "Tax", - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, + const SizedBox(height: 10), + ], + ), + if (controller + .transactionController + .model + .confirmationData["tax"] != + 0) + Column( + children: [ + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + "Tax", + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + Text( + controller + .transactionController + .model + .confirmationData["tax"] + .toString(), + style: TextStyle(fontSize: 16), + ), + ], ), - ), - Text( - controller - .transactionController - .model - .confirmationData["tax"] - .toString(), - style: TextStyle(fontSize: 16), - ), - ], - ), - const SizedBox(height: 10), + const SizedBox(height: 10), + ], + ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -287,11 +314,28 @@ class _ConfirmScreenState extends State ), ], ), + const SizedBox(height: 5), + Text( + "NB: Your payment provider may charge additional fees.", + style: TextStyle( + fontSize: 11, + color: Colors.red, + ), + ), ], ), ), const SizedBox(height: 20), _buildPaymentProcessorButton(), + const SizedBox(height: 20), + Center( + child: OutlinedButton( + child: Text('Cancel'), + onPressed: () { + context.go('/'); + }, + ), + ), ], ), ), diff --git a/lib/screens/gateway/gateway_controller.dart b/lib/screens/gateway/gateway_controller.dart index d6001b8..c4f43b0 100644 --- a/lib/screens/gateway/gateway_controller.dart +++ b/lib/screens/gateway/gateway_controller.dart @@ -58,7 +58,7 @@ class GatewayController extends ChangeNotifier { if (model.isCancelled) break; try { - dynamic response = await http.get('/transaction/poll/$uid'); + dynamic response = await http.get('/public/transaction/poll/$uid'); logger.i(response.toString()); if (response['status'] == 'SUCCESS') { @@ -85,7 +85,7 @@ class GatewayController extends ChangeNotifier { } try { - dynamic response = await http.get('/transaction/poll/$uid'); + dynamic response = await http.get('/public/transaction/poll/$uid'); logger.i(response.toString()); if (response['status'] == 'SUCCESS') { diff --git a/lib/screens/history/history_controller.dart b/lib/screens/history/history_controller.dart index c5602ae..708b619 100644 --- a/lib/screens/history/history_controller.dart +++ b/lib/screens/history/history_controller.dart @@ -37,7 +37,7 @@ class HistoryController extends ChangeNotifier { try { params['type'] = 'REQUEST'; Map response = await http.get( - '/transaction?${buildQueryParameters(params)}', + '/public/transaction?${buildQueryParameters(params)}', ); if (params['page'] == '0') { diff --git a/lib/screens/home/home_controller.dart b/lib/screens/home/home_controller.dart index 062d6c5..0931e1b 100644 --- a/lib/screens/home/home_controller.dart +++ b/lib/screens/home/home_controller.dart @@ -11,6 +11,7 @@ class HomeScreenModel { List categories = []; List providers = []; List filterableProviders = []; + List accounts = []; BillProvider? selectedProvider; dynamic selectedCategory; dynamic account = {}; @@ -20,6 +21,26 @@ class HomeScreenModel { String? errorMessage; } +class Account { + late String name; + late String description; + late String image; + late String currency; + late String balance; + late String availableBalance; + late String accountNumber; + late String accountType; + late String accountHolderName; + + Account({ + required this.name, + required this.description, + required this.image, + required this.currency, + required this.balance, + }); +} + @freezed abstract class Category with _$Category { const factory Category({ @@ -76,12 +97,17 @@ class HomeController extends ChangeNotifier { }); } + Future getAccounts() async { + model.accounts = getFakeAccounts(); + notifyListeners(); + } + Future getProviders() async { try { model.isLoading = true; notifyListeners(); - List response = await http.get('/providers'); + List response = await http.get('/public/providers'); model.providers = response.map((e) => BillProvider.fromJson(e)).toList(); model.filterableProviders = model.providers; @@ -116,7 +142,7 @@ class HomeController extends ChangeNotifier { try { Map response = await http.get( - '/transaction?sort=createdAt,desc&size=3&page=0&userId=$userId&type=REQUEST', + '/public/transaction?sort=createdAt,desc&size=3&page=0&userId=$userId&type=REQUEST', ); PageableModel pageableModel = PageableModel.fromJson(response); @@ -146,7 +172,7 @@ class HomeController extends ChangeNotifier { // Simulate fetching categories try { - List response = await http.get('/categories'); + List response = await http.get('/public/categories'); model.categories = response.map((e) => Category.fromJson(e)).toList(); if (model.categories.isNotEmpty) { @@ -246,4 +272,23 @@ class HomeController extends ChangeNotifier { ), ]; } + + List getFakeAccounts() { + return [ + Account( + name: 'Vusumuzi Khoza', + description: 'USD Wallet', + image: 'united-states.png', + currency: 'USD', + balance: '4.02', + ), + Account( + name: 'Vusumuzi Khoza', + description: 'ZWG Wallet', + image: 'zimbabwe.png', + currency: 'ZWL', + balance: '36.00', + ), + ]; + } } diff --git a/lib/screens/home/home_screen.dart b/lib/screens/home/home_screen.dart index 7b94215..3dbb25b 100644 --- a/lib/screens/home/home_screen.dart +++ b/lib/screens/home/home_screen.dart @@ -80,6 +80,7 @@ class _HomeScreenState extends State Future setupDataAndTransitions() async { homeController = HomeController(context); + await homeController.getAccounts(); await homeController.getCategories(); await homeController.getProviders(); @@ -108,20 +109,7 @@ class _HomeScreenState extends State } getBoxDecoration(String? clientId) { - // BillProvider? provider = homeController.model.selectedProvider; - // - // if (provider != null && clientId != provider.clientId) { - // return BoxDecoration( - // border: Border.all(color: Colors.black87.withAlpha(20)), - // borderRadius: BorderRadius.circular(12), - // ); - // } - return BoxDecoration( - // border: Border.all( - // color: Theme.of(context).colorScheme.primary.withValues(alpha: 0.7), - // width: 3, - // ), border: Border.all(color: Colors.black87.withAlpha(20)), borderRadius: BorderRadius.circular(12), gradient: LinearGradient( @@ -161,12 +149,45 @@ class _HomeScreenState extends State surfaceTintColor: Colors.transparent, floating: true, title: Image(image: AssetImage('assets/peak.png'), width: 85), - actions: [ - // IconButton( - // icon: const Icon(Icons.notifications_outlined), - // onPressed: () {}, - // ), - ], + leading: IconButton( + icon: Container( + width: 32, + height: 32, + decoration: BoxDecoration( + border: Border.all(color: Colors.black87.withAlpha(20)), + shape: BoxShape.circle, + gradient: LinearGradient( + colors: [ + Theme.of( + context, + ).colorScheme.primary.withValues(alpha: 0.1), + Theme.of( + context, + ).colorScheme.tertiary.withValues(alpha: 0.05), + ], + stops: const [0.3, 0.7], + begin: Alignment.topRight, + end: Alignment.bottomLeft, + ), + ), + child: Center( + child: Text( + 'VK', + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + onPressed: () {}, + ), + // actions: [ + // IconButton( + // icon: const Icon(Icons.notifications_outlined), + // onPressed: () {}, + // ), + // ], ), SliverToBoxAdapter( child: Padding( @@ -177,29 +198,8 @@ class _HomeScreenState extends State return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (_isLoggedIn) - AlignTransition( - alignment: _alignAnimation, - child: Skeletonizer( - enabled: _loading, - child: Column( - children: [ - Text( - "Vusumuzi Khoza", - style: TextStyle(fontSize: 15), - ), - Text( - "USD 4.02", - style: TextStyle( - fontSize: 30, - fontWeight: FontWeight.bold, - ), - ), - const SizedBox(height: 20), - ], - ), - ), - ), + if (!_isLoggedIn) _buildLogin(homeController), + if (_isLoggedIn) _buildAccounts(homeController), _buildFilterChips(homeController), SizedBox(height: 10), Column( @@ -209,7 +209,7 @@ class _HomeScreenState extends State ), ], ), - const SizedBox(height: 20), + const SizedBox(height: 10), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, @@ -273,6 +273,174 @@ class _HomeScreenState extends State ); } + Widget _buildLogin(HomeController controller) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Text( + 'Register or Login for more', + style: TextStyle(fontSize: 16), + ), + InkWell( + onTap: () { + _showFeaturesPopup(context); + }, + borderRadius: BorderRadius.circular(5), + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 5, vertical: 2), + child: Text( + 'features', + style: TextStyle( + fontSize: 16, + decoration: TextDecoration.underline, + decorationColor: Theme.of(context).colorScheme.primary, + color: Theme.of(context).colorScheme.primary, + ), + ), + ), + ), + ], + ), + InkWell( + onTap: () { + context.push('/onboarding/landing'); + }, + borderRadius: BorderRadius.circular(12), + child: Container( + padding: EdgeInsets.symmetric(horizontal: 12, vertical: 6), + decoration: BoxDecoration( + color: Theme.of( + context, + ).colorScheme.primary.withValues(alpha: 0.15), + borderRadius: BorderRadius.circular(12), + ), + child: Text('Get Started', style: TextStyle(fontSize: 12)), + ), + ), + ], + ), + SizedBox(height: 10), + Divider( + color: Theme.of(context).colorScheme.primary.withValues(alpha: 0.2), + ), + ], + ); + } + + Widget _buildAccounts(HomeController controller) { + return Column( + children: [ + Row( + children: [ + ...controller.model.accounts.map( + (e) => Row( + children: [ + Skeletonizer( + enabled: controller.model.isLoading, + child: SlideTransition( + position: _alignListAnimations[0], + child: InkWell( + borderRadius: BorderRadius.circular(12), + onTap: () { + // context.push("/wallet"); + }, + child: Container( + width: 150, + padding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 12, + ), + decoration: BoxDecoration( + border: Border.all( + color: Colors.black87.withAlpha(20), + ), + borderRadius: BorderRadius.circular(12), + gradient: LinearGradient( + colors: [ + Theme.of( + context, + ).colorScheme.primary.withValues(alpha: 0.1), + Theme.of( + context, + ).colorScheme.tertiary.withValues(alpha: 0.05), + ], + stops: const [0.3, 0.7], + begin: Alignment.topRight, + end: Alignment.bottomLeft, + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Image.asset('assets/${e.image}', width: 20), + SizedBox(height: 5), + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + e.currency, + style: TextStyle( + fontSize: 22, + color: Colors.black, + fontWeight: FontWeight.w100, + ), + ), + SizedBox(width: 5), + Text( + e.balance, + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 22, + color: Colors.black, + ), + ), + ], + ), + SizedBox(height: 5), + InkWell( + onTap: () { + // context.push("/withdraw"); + }, + borderRadius: BorderRadius.circular(12), + child: Container( + padding: EdgeInsets.symmetric( + horizontal: 12, + vertical: 6, + ), + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.primary + .withValues(alpha: 0.15), + borderRadius: BorderRadius.circular(12), + ), + child: Text( + 'Add Money', + style: TextStyle(fontSize: 12), + ), + ), + ), + ], + ), + ), + ), + ), + ), + SizedBox(width: 10), + ], + ), + ), + ], + ), + SizedBox(height: 10), + ], + ); + } + Widget _buildTransactionItem(Map e, BuildContext context) { return SlideTransition( position: _alignListAnimations[0], @@ -463,4 +631,197 @@ class _HomeScreenState extends State ), ); } + + void _showFeaturesPopup(BuildContext context) { + showDialog( + context: context, + builder: (BuildContext context) { + return Dialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + elevation: 0, + backgroundColor: Colors.transparent, + child: Container( + padding: const EdgeInsets.all(20), + decoration: BoxDecoration( + shape: BoxShape.rectangle, + color: Theme.of(context).colorScheme.surface, + borderRadius: BorderRadius.circular(20), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.1), + blurRadius: 20, + offset: const Offset(0, 10), + ), + ], + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // Header with icon and title + Container( + padding: const EdgeInsets.all(20), + decoration: BoxDecoration( + color: Theme.of( + context, + ).colorScheme.primary.withValues(alpha: 0.1), + shape: BoxShape.circle, + ), + child: Image(image: AssetImage("assets/peak.png"), width: 45), + ), + const SizedBox(height: 16), + Text( + 'Unlock Additional Features', + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + color: Theme.of(context).colorScheme.onSurface, + ), + textAlign: TextAlign.center, + ), + const SizedBox(height: 8), + Text( + 'Register or login to access these great features', + style: TextStyle( + fontSize: 14, + color: Theme.of( + context, + ).colorScheme.onSurface.withOpacity(0.7), + ), + textAlign: TextAlign.center, + ), + const SizedBox(height: 24), + + // Features list + _buildFeatureItem( + Icons.account_balance_wallet, + 'Dual Currency Wallets', + 'USD and ZWG wallets for prepayments that can be utilized later', + ), + const SizedBox(height: 16), + _buildFeatureItem( + Icons.history, + 'Transaction History', + 'All transactions are saved for reference from any device', + ), + const SizedBox(height: 16), + _buildFeatureItem( + Icons.people, + 'Recipient Management', + 'Save and manage recipient details for future use', + ), + const SizedBox(height: 16), + _buildFeatureItem( + Icons.schedule, + 'Smart Payments', + 'Schedule payments and split bills with friends', + ), + + const SizedBox(height: 24), + + // Action buttons + Row( + children: [ + Expanded( + child: TextButton( + onPressed: () => Navigator.of(context).pop(), + style: TextButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 12), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + child: Text( + 'Maybe Later', + style: TextStyle( + color: Theme.of( + context, + ).colorScheme.onSurface.withOpacity(0.6), + fontSize: 16, + ), + ), + ), + ), + const SizedBox(width: 12), + Expanded( + child: ElevatedButton( + onPressed: () { + Navigator.of(context).pop(); + context.push('/onboarding/landing'); + }, + style: ElevatedButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 12), + backgroundColor: + Theme.of(context).colorScheme.primary, + foregroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + child: Text( + 'Get Started', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + ), + ), + ), + ), + ], + ), + ], + ), + ), + ); + }, + ); + } + + Widget _buildFeatureItem(IconData icon, String title, String description) { + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.primary.withValues(alpha: 0.1), + borderRadius: BorderRadius.circular(8), + ), + child: Icon( + icon, + size: 20, + color: Theme.of(context).colorScheme.primary, + ), + ), + const SizedBox(width: 10), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: Theme.of(context).colorScheme.onSurface, + ), + ), + const SizedBox(height: 8), + Text( + description, + style: TextStyle( + fontSize: 14, + color: Theme.of( + context, + ).colorScheme.onSurface.withOpacity(0.7), + height: 1.4, + ), + ), + ], + ), + ), + ], + ); + } } diff --git a/lib/screens/onboarding/landing/landing_screen.dart b/lib/screens/onboarding/landing/landing_screen.dart new file mode 100644 index 0000000..eaf6586 --- /dev/null +++ b/lib/screens/onboarding/landing/landing_screen.dart @@ -0,0 +1,90 @@ +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:qpay/screens/onboarding/login/login_controller.dart'; + +class LandingScreen extends StatefulWidget { + const LandingScreen({super.key}); + + @override + State createState() => _LandingScreenState(); +} + +class _LandingScreenState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + body: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(25), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox(height: 75), + Text( + 'Welcome to Peak', + style: TextStyle( + fontSize: 30, + fontWeight: FontWeight.bold, + color: Theme.of(context).colorScheme.primary, + ), + ), + SizedBox(height: 5), + Text( + 'The fastest way to pay for local goods and services', + style: TextStyle(fontSize: 18), + textAlign: TextAlign.center, + ), + Image.asset('assets/landing.png', width: 300), + Image.asset('assets/peak.png', width: 100), + SizedBox(height: 5), + Text( + 'Register or login to get started', + style: TextStyle(fontSize: 16), + ), + SizedBox(height: 10), + ], + ), + ), + ), + bottomNavigationBar: SizedBox( + height: 130, + child: Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 25), + child: Row( + children: [ + Expanded( + child: ElevatedButton( + onPressed: () { + context.go('/onboarding/register'); + }, + child: Text('Register', style: TextStyle(fontSize: 16)), + ), + ), + SizedBox(width: 16), + Expanded( + child: TextButton( + onPressed: () { + context.go('/onboarding/login'); + }, + child: Text('Login', style: TextStyle(fontSize: 16)), + ), + ), + ], + ), + ), + SizedBox(height: 5), + TextButton( + onPressed: () { + context.go('/'); + }, + child: Text('Continue as guest?', style: TextStyle(fontSize: 14)), + ), + ], + ), + ), + ); + } +} diff --git a/lib/screens/onboarding/login/login_controller.dart b/lib/screens/onboarding/login/login_controller.dart new file mode 100644 index 0000000..823fbaf --- /dev/null +++ b/lib/screens/onboarding/login/login_controller.dart @@ -0,0 +1,80 @@ +import 'package:dio/dio.dart'; +import 'package:flutter/material.dart'; +import 'package:qpay/http/http.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +class LoginScreenModel { + String username = ""; + String password = ""; + String status = "PENDING"; + + bool isLoading = false; + String? errorMessage; +} + +class LoginController extends ChangeNotifier { + final Http http = Http(); + final model = LoginScreenModel(); + late SharedPreferences prefs; + BuildContext context; + + LoginController(this.context); + + void _showErrorSnackBar(String? message) { + WidgetsBinding.instance.addPostFrameCallback((_) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(message.toString()), + behavior: SnackBarBehavior.floating, + backgroundColor: Colors.deepOrange, + ), + ); + }); + } + + Future login(String username, String password) async { + try { + model.isLoading = true; + notifyListeners(); + + prefs = await SharedPreferences.getInstance(); + + dynamic response = await http.post('/auth/login', { + 'username': username, + 'password': password, + }); + logger.i(response.toString()); + + if (response.containsKey('token')) { + model.status = 'SUCCESS'; + + prefs.setString('token', response['token']); + prefs.setString('username', username); + prefs.setString('email', response['email']); + prefs.setString('firstName', response['firstName']); + prefs.setString('lastName', response['lastName']); + prefs.setString('phone', response['phone']); + + return model; + } else { + model.errorMessage = + response['errorMessage'] ?? + "Problem with the transaction. Please try again or contact support"; + _showErrorSnackBar(model.errorMessage); + } + } on DioException catch (e, s) { + logger.e(s); + _showErrorSnackBar("Network error. Please try again or contact support"); + } catch (e, s) { + logger.e(s); + _showErrorSnackBar( + "Problem processing that transaction. Please try again or contact support", + ); + } + + model.isLoading = false; + notifyListeners(); + + return model; + } +} diff --git a/lib/screens/onboarding/login/login_screen.dart b/lib/screens/onboarding/login/login_screen.dart new file mode 100644 index 0000000..97c4a41 --- /dev/null +++ b/lib/screens/onboarding/login/login_screen.dart @@ -0,0 +1,231 @@ +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:qpay/screens/onboarding/login/login_controller.dart'; + +class LoginScreen extends StatefulWidget { + const LoginScreen({super.key}); + + @override + State createState() => _LoginScreenState(); +} + +class _LoginScreenState extends State { + final _formKey = GlobalKey(); + final _usernameController = TextEditingController(); + final _passwordController = TextEditingController(); + + late LoginController _loginController; + + @override + void initState() { + super.initState(); + _loginController = LoginController(context); + } + + @override + void dispose() { + _loginController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(25), + child: Form( + key: _formKey, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox(height: 75), + Text( + 'Login', + style: TextStyle( + fontSize: 30, + fontWeight: FontWeight.bold, + color: Theme.of(context).colorScheme.primary, + ), + ), + SizedBox(height: 5), + Text( + 'Welcome back to Peak', + style: TextStyle(fontSize: 18), + textAlign: TextAlign.center, + ), + SizedBox(height: 40), + // Username Field + TextFormField( + controller: _usernameController, + decoration: InputDecoration( + labelText: 'Phone Number', + prefixIcon: Icon(Icons.person_outline), + ), + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter a phone number'; + } + return null; + }, + ), + SizedBox(height: 20), + // Password Field + TextFormField( + controller: _passwordController, + decoration: InputDecoration( + labelText: 'Password', + prefixIcon: Icon(Icons.lock_outline), + suffixIcon: Icon(Icons.visibility_outlined), + ), + obscureText: true, + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter a password'; + } + return null; + }, + ), + SizedBox(height: 20), + // Forgot Password Link + Align( + alignment: Alignment.centerRight, + child: TextButton( + onPressed: () { + // TODO: Add forgot password logic + }, + child: Text( + 'Forgot Password?', + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + fontSize: 14, + ), + ), + ), + ), + SizedBox(height: 40), + // Divider with "or" text + Row( + children: [ + Expanded(child: Divider(color: Colors.grey.shade300)), + Padding( + padding: EdgeInsets.symmetric(horizontal: 16), + child: Text( + 'or continue with', + style: TextStyle( + color: Colors.grey.shade600, + fontSize: 14, + ), + ), + ), + Expanded(child: Divider(color: Colors.grey.shade300)), + ], + ), + SizedBox(height: 30), + // Social Login Buttons + Row( + children: [ + Expanded( + child: OutlinedButton.icon( + onPressed: () { + // TODO: Add Google login logic + }, + icon: Image.asset( + 'assets/google.png', + width: 20, + height: 20, + ), + label: Text('Google', style: TextStyle(fontSize: 16)), + style: OutlinedButton.styleFrom( + padding: EdgeInsets.symmetric(vertical: 16), + side: BorderSide(color: Colors.grey.shade300), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + ), + ), + SizedBox(width: 16), + Expanded( + child: OutlinedButton.icon( + onPressed: () { + // TODO: Add Apple login logic + }, + icon: Image.asset( + 'assets/apple.png', + width: 20, + height: 20, + ), + label: Text('Apple', style: TextStyle(fontSize: 16)), + style: OutlinedButton.styleFrom( + padding: EdgeInsets.symmetric(vertical: 16), + side: BorderSide(color: Colors.grey.shade300), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + ), + ), + ], + ), + SizedBox(height: 40), + ], + ), + ), + ), + ), + bottomNavigationBar: SizedBox( + height: 130, + child: Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 25), + child: Row( + children: [ + Expanded( + child: ElevatedButton( + onPressed: () async { + // TODO: Add login logic + if (_formKey.currentState!.validate()) { + LoginScreenModel model = await _loginController.login( + _usernameController.text, + _passwordController.text, + ); + + if (model.status == 'SUCCESS') { + context.go('/'); + } + } + }, + child: Text('Login', style: TextStyle(fontSize: 16)), + ), + ), + SizedBox(width: 16), + Expanded( + child: TextButton( + onPressed: () { + context.go('/onboarding/register'); + }, + child: Text( + 'Create Account', + style: TextStyle(fontSize: 16), + ), + ), + ), + ], + ), + ), + SizedBox(height: 5), + TextButton( + onPressed: () { + context.go('/'); + }, + child: Text('Continue as guest?', style: TextStyle(fontSize: 14)), + ), + ], + ), + ), + ); + } +} diff --git a/lib/screens/onboarding/password/password_screen.dart b/lib/screens/onboarding/password/password_screen.dart new file mode 100644 index 0000000..54943c0 --- /dev/null +++ b/lib/screens/onboarding/password/password_screen.dart @@ -0,0 +1,255 @@ +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + +class PasswordScreen extends StatefulWidget { + const PasswordScreen({super.key}); + + @override + State createState() => _PasswordScreenState(); +} + +class _PasswordScreenState extends State { + final TextEditingController _passwordController = TextEditingController(); + final TextEditingController _confirmPasswordController = + TextEditingController(); + final FocusNode _passwordFocusNode = FocusNode(); + final FocusNode _confirmPasswordFocusNode = FocusNode(); + bool _obscurePassword = true; + bool _obscureConfirmPassword = true; + + @override + void dispose() { + _passwordController.dispose(); + _confirmPasswordController.dispose(); + _passwordFocusNode.dispose(); + _confirmPasswordFocusNode.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(25), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox(height: 75), + Text( + 'Set New Password', + style: TextStyle( + fontSize: 30, + fontWeight: FontWeight.bold, + color: Theme.of(context).colorScheme.primary, + ), + ), + SizedBox(height: 5), + Text( + 'Create a strong password to secure your account', + style: TextStyle(fontSize: 18), + textAlign: TextAlign.center, + ), + Image.asset('assets/password.png', width: 300), + // Password Input + TextField( + controller: _passwordController, + focusNode: _passwordFocusNode, + obscureText: _obscurePassword, + decoration: InputDecoration( + labelText: 'New Password', + hintText: 'Enter your new password', + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide(color: Colors.grey.shade300), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide( + color: Theme.of(context).colorScheme.primary, + width: 2, + ), + ), + contentPadding: EdgeInsets.symmetric( + horizontal: 16, + vertical: 16, + ), + suffixIcon: IconButton( + icon: Icon( + _obscurePassword + ? Icons.visibility + : Icons.visibility_off, + color: Colors.grey.shade600, + ), + onPressed: () { + setState(() { + _obscurePassword = !_obscurePassword; + }); + }, + ), + ), + ), + SizedBox(height: 20), + // Confirm Password Input + TextField( + controller: _confirmPasswordController, + focusNode: _confirmPasswordFocusNode, + obscureText: _obscureConfirmPassword, + decoration: InputDecoration( + labelText: 'Confirm Password', + hintText: 'Confirm your new password', + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide(color: Colors.grey.shade300), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide( + color: Theme.of(context).colorScheme.primary, + width: 2, + ), + ), + contentPadding: EdgeInsets.symmetric( + horizontal: 16, + vertical: 16, + ), + suffixIcon: IconButton( + icon: Icon( + _obscureConfirmPassword + ? Icons.visibility + : Icons.visibility_off, + color: Colors.grey.shade600, + ), + onPressed: () { + setState(() { + _obscureConfirmPassword = !_obscureConfirmPassword; + }); + }, + ), + ), + ), + SizedBox(height: 20), + // Password Requirements + Container( + padding: EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.grey.shade50, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: Colors.grey.shade200), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Password Requirements:', + style: TextStyle( + fontWeight: FontWeight.w600, + fontSize: 14, + color: Colors.grey.shade700, + ), + ), + SizedBox(height: 8), + _buildRequirement('At least 8 characters', true), + _buildRequirement('Contains uppercase letter', true), + _buildRequirement('Contains lowercase letter', true), + _buildRequirement('Contains number', true), + _buildRequirement('Contains special character', false), + ], + ), + ), + SizedBox(height: 40), + ], + ), + ), + ), + bottomNavigationBar: SizedBox( + height: 130, + child: Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 25), + child: Row( + children: [ + Expanded( + child: ElevatedButton( + onPressed: () { + // TODO: Add password update logic + String password = _passwordController.text; + String confirmPassword = + _confirmPasswordController.text; + print('Password: $password'); + print('Confirm Password: $confirmPassword'); + + context.go('/onboarding/login'); + }, + style: ElevatedButton.styleFrom( + padding: EdgeInsets.symmetric(vertical: 16), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + child: Text( + 'Update Password', + style: TextStyle(fontSize: 16), + ), + ), + ), + SizedBox(width: 16), + Expanded( + child: OutlinedButton( + onPressed: () { + context.go('/onboarding/login'); + }, + style: OutlinedButton.styleFrom( + padding: EdgeInsets.symmetric(vertical: 16), + side: BorderSide(color: Colors.grey.shade300), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + child: Text( + 'Back to Login', + style: TextStyle(fontSize: 16), + ), + ), + ), + ], + ), + ), + SizedBox(height: 20), + TextButton( + onPressed: () { + context.go('/'); + }, + child: Text('Continue as guest?', style: TextStyle(fontSize: 14)), + ), + ], + ), + ), + ); + } + + Widget _buildRequirement(String text, bool isMet) { + return Padding( + padding: EdgeInsets.symmetric(vertical: 2), + child: Row( + children: [ + Icon( + isMet ? Icons.check_circle : Icons.circle_outlined, + size: 16, + color: isMet ? Colors.green : Colors.grey.shade600, + ), + SizedBox(width: 8), + Text( + text, + style: TextStyle( + fontSize: 12, + color: isMet ? Colors.grey.shade600 : Colors.grey.shade400, + ), + ), + ], + ), + ); + } +} diff --git a/lib/screens/onboarding/register/register_screen.dart b/lib/screens/onboarding/register/register_screen.dart new file mode 100644 index 0000000..f85bd67 --- /dev/null +++ b/lib/screens/onboarding/register/register_screen.dart @@ -0,0 +1,194 @@ +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + +class RegisterScreen extends StatefulWidget { + const RegisterScreen({super.key}); + + @override + State createState() => _RegisterScreenState(); +} + +class _RegisterScreenState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + body: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(25), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox(height: 75), + Text( + 'Registration', + style: TextStyle( + fontSize: 30, + fontWeight: FontWeight.bold, + color: Theme.of(context).colorScheme.primary, + ), + ), + SizedBox(height: 5), + Text( + 'Join Peak and start making fast payments', + style: TextStyle(fontSize: 18), + textAlign: TextAlign.center, + ), + SizedBox(height: 40), + // First Name Field + TextField( + decoration: InputDecoration( + labelText: 'First Name', + prefixIcon: Icon(Icons.person_outline), + ), + ), + SizedBox(height: 20), + // Last Name Field + TextField( + decoration: InputDecoration( + labelText: 'Last Name', + prefixIcon: Icon(Icons.person_outline), + ), + ), + SizedBox(height: 20), + // Phone Field + TextField( + decoration: InputDecoration( + labelText: 'Phone Number', + prefixIcon: Icon(Icons.phone_outlined), + ), + keyboardType: TextInputType.phone, + ), + SizedBox(height: 20), + // Email Field + TextField( + decoration: InputDecoration( + labelText: 'Email Address', + prefixIcon: Icon(Icons.email_outlined), + ), + keyboardType: TextInputType.emailAddress, + ), + SizedBox(height: 20), + // Password Field + TextField( + decoration: InputDecoration( + labelText: 'Password', + prefixIcon: Icon(Icons.lock_outline), + suffixIcon: Icon(Icons.visibility_outlined), + ), + obscureText: true, + ), + SizedBox(height: 40), + // Divider with "or" text + Row( + children: [ + Expanded(child: Divider(color: Colors.grey.shade300)), + Padding( + padding: EdgeInsets.symmetric(horizontal: 16), + child: Text( + 'or continue with', + style: TextStyle( + color: Colors.grey.shade600, + fontSize: 14, + ), + ), + ), + Expanded(child: Divider(color: Colors.grey.shade300)), + ], + ), + SizedBox(height: 30), + // Social Login Buttons + Row( + children: [ + Expanded( + child: OutlinedButton.icon( + onPressed: () { + // TODO: Add Google login logic + }, + icon: Image.asset( + 'assets/google.png', + width: 20, + height: 20, + ), + label: Text('Google', style: TextStyle(fontSize: 16)), + style: OutlinedButton.styleFrom( + padding: EdgeInsets.symmetric(vertical: 16), + side: BorderSide(color: Colors.grey.shade300), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + ), + ), + SizedBox(width: 16), + Expanded( + child: OutlinedButton.icon( + onPressed: () { + // TODO: Add Apple login logic + }, + icon: Image.asset( + 'assets/apple.png', + width: 20, + height: 20, + ), + label: Text('Apple', style: TextStyle(fontSize: 16)), + style: OutlinedButton.styleFrom( + padding: EdgeInsets.symmetric(vertical: 16), + side: BorderSide(color: Colors.grey.shade300), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + ), + ), + ], + ), + SizedBox(height: 40), + ], + ), + ), + ), + bottomNavigationBar: SizedBox( + height: 130, + child: Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 25), + child: Row( + children: [ + Expanded( + child: ElevatedButton( + onPressed: () { + context.go('/onboarding/verify'); + }, + child: Text('Submit', style: TextStyle(fontSize: 16)), + ), + ), + SizedBox(width: 16), + Expanded( + child: TextButton( + onPressed: () { + context.go('/onboarding/login'); + }, + child: Text( + 'Go to Login', + style: TextStyle(fontSize: 16), + ), + ), + ), + ], + ), + ), + SizedBox(height: 5), + TextButton( + onPressed: () { + context.go('/'); + }, + child: Text('Continue as guest?', style: TextStyle(fontSize: 14)), + ), + ], + ), + ), + ); + } +} diff --git a/lib/screens/onboarding/verify/verify_screen.dart b/lib/screens/onboarding/verify/verify_screen.dart new file mode 100644 index 0000000..98a73e2 --- /dev/null +++ b/lib/screens/onboarding/verify/verify_screen.dart @@ -0,0 +1,188 @@ +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + +class VerifyScreen extends StatefulWidget { + const VerifyScreen({super.key}); + + @override + State createState() => _VerifyScreenState(); +} + +class _VerifyScreenState extends State { + final List _codeControllers = List.generate( + 6, + (index) => TextEditingController(), + ); + final List _focusNodes = List.generate(6, (index) => FocusNode()); + + @override + void dispose() { + for (var controller in _codeControllers) { + controller.dispose(); + } + for (var node in _focusNodes) { + node.dispose(); + } + super.dispose(); + } + + void _onCodeChanged(String value, int index) { + if (value.length == 1 && index < 5) { + _focusNodes[index + 1].requestFocus(); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(25), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox(height: 75), + Text( + 'Verify Your Account', + style: TextStyle( + fontSize: 30, + fontWeight: FontWeight.bold, + color: Theme.of(context).colorScheme.primary, + ), + ), + SizedBox(height: 5), + Text( + 'Enter the 6-digit code sent to your phone/email', + style: TextStyle(fontSize: 18), + textAlign: TextAlign.center, + ), + Image.asset('assets/verify.png', width: 300), + // Verification Code Input + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: List.generate( + 6, + (index) => SizedBox( + width: 50, + child: TextField( + controller: _codeControllers[index], + focusNode: _focusNodes[index], + textAlign: TextAlign.center, + keyboardType: TextInputType.number, + maxLength: 1, + decoration: InputDecoration( + counterText: '', + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide(color: Colors.grey.shade300), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide( + color: Theme.of(context).colorScheme.primary, + width: 2, + ), + ), + contentPadding: EdgeInsets.symmetric(vertical: 16), + ), + onChanged: (value) => _onCodeChanged(value, index), + ), + ), + ), + ), + SizedBox(height: 30), + // Resend Code Section + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + "Didn't receive the code? ", + style: TextStyle(color: Colors.grey.shade600, fontSize: 16), + ), + TextButton( + onPressed: () { + // TODO: Add resend code logic + }, + child: Text( + 'Resend Code', + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + fontSize: 16, + fontWeight: FontWeight.w600, + ), + ), + ), + ], + ), + SizedBox(height: 20), + // Timer for resend (optional) + Text( + 'Resend available in 2:30', + style: TextStyle(color: Colors.grey.shade500, fontSize: 14), + ), + SizedBox(height: 40), + ], + ), + ), + ), + bottomNavigationBar: SizedBox( + height: 130, + child: Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 25), + child: Row( + children: [ + Expanded( + child: ElevatedButton( + onPressed: () { + context.go('/onboarding/password'); + }, + style: ElevatedButton.styleFrom( + padding: EdgeInsets.symmetric(vertical: 16), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + child: Text( + 'Verify Code', + style: TextStyle(fontSize: 16), + ), + ), + ), + SizedBox(width: 16), + Expanded( + child: OutlinedButton( + onPressed: () { + context.go('/onboarding/login'); + }, + style: OutlinedButton.styleFrom( + padding: EdgeInsets.symmetric(vertical: 16), + side: BorderSide(color: Colors.grey.shade300), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + child: Text( + 'Back to Login', + style: TextStyle(fontSize: 16), + ), + ), + ), + ], + ), + ), + SizedBox(height: 20), + TextButton( + onPressed: () { + context.go('/'); + }, + child: Text('Continue as guest?', style: TextStyle(fontSize: 14)), + ), + ], + ), + ), + ); + } +} diff --git a/lib/screens/pay/pay_controller.dart b/lib/screens/pay/pay_controller.dart index 3adae13..01fd76c 100644 --- a/lib/screens/pay/pay_controller.dart +++ b/lib/screens/pay/pay_controller.dart @@ -112,7 +112,7 @@ class PayController extends ChangeNotifier { ); dynamic response = await http.post( - '/transaction', + '/public/transaction', transactionController.model.formData, ); logger.i(response.toString()); @@ -148,7 +148,7 @@ class PayController extends ChangeNotifier { model.isLoading = true; List response = await http.get( - '/providers/$providerId/products', + '/public/providers/$providerId/products', ); model.products = response.map((e) => BillProduct.fromJson(e)).toList(); } catch (e) { @@ -168,7 +168,7 @@ class PayController extends ChangeNotifier { model.paymentProcessors = getFakePaymentProcessors(); notifyListeners(); - List response = await http.get('/payment-processors'); + List response = await http.get('/public/payment-processors'); model.paymentProcessors = response.map((e) => PaymentProcessor.fromJson(e)).toList(); diff --git a/lib/screens/receipt/receipt_controller.dart b/lib/screens/receipt/receipt_controller.dart index c7168e0..573b4a0 100644 --- a/lib/screens/receipt/receipt_controller.dart +++ b/lib/screens/receipt/receipt_controller.dart @@ -50,7 +50,7 @@ class ReceiptController extends ChangeNotifier { // fetch provider & update tran controller String providerId = model.receiptData?['billClientId'] ?? ''; Map provider = await http.get( - '/providers/client/$providerId', + '/public/providers/client/$providerId', ); transactionController.model.selectedProvider = hc.BillProvider.fromJson( provider, @@ -72,7 +72,7 @@ class ReceiptController extends ChangeNotifier { if (productUid.isNotEmpty) { Map product = await http.get( - '/providers/$providerUid/products/$productUid', + '/public/providers/$providerUid/products/$productUid', ); transactionController.model.selectedProduct = pc.BillProduct.fromJson( product, @@ -96,7 +96,7 @@ class ReceiptController extends ChangeNotifier { Future getReceiptData(String id) async { setLoading(true); try { - Map response = await http.get('/transaction/$id'); + Map response = await http.get('/public/transaction/$id'); model.receiptData = response; } catch (e) { _showErrorSnackBar( diff --git a/lib/screens/receipt/receipt_screen.dart b/lib/screens/receipt/receipt_screen.dart index 9c10d0e..f7aa8b6 100644 --- a/lib/screens/receipt/receipt_screen.dart +++ b/lib/screens/receipt/receipt_screen.dart @@ -212,7 +212,7 @@ class _ReceiptScreenState extends State Text( receiptController .model - .receiptData?["totalAmount"] + .receiptData?["amount"] .toStringAsFixed(2) ?? "", style: TextStyle(fontSize: 26), @@ -447,6 +447,37 @@ class _ReceiptScreenState extends State ), ), const SizedBox(height: 10), + Skeletonizer( + enabled: + receiptController + .model + .isLoading, + child: Row( + mainAxisAlignment: + MainAxisAlignment + .spaceBetween, + children: [ + Text( + "Debit Reference", + style: TextStyle( + fontSize: 16, + fontWeight: + FontWeight.bold, + ), + ), + Text( + transactionController + .model + .receiptData?["debitRef"] ?? + "", + style: TextStyle( + fontSize: 16, + ), + ), + ], + ), + ), + const SizedBox(height: 10), Skeletonizer( enabled: receiptController @@ -613,90 +644,123 @@ class _ReceiptScreenState extends State ], ), const SizedBox(height: 10), - Row( - mainAxisAlignment: - MainAxisAlignment - .spaceBetween, - children: [ - Text( - "Our Charge", - style: TextStyle( - fontSize: 16, - fontWeight: - FontWeight.bold, + if (transactionController + .model + .receiptData?["charge"] != + 0) + Column( + children: [ + Row( + mainAxisAlignment: + MainAxisAlignment + .spaceBetween, + children: [ + Text( + "Our Charge", + style: TextStyle( + fontSize: 16, + fontWeight: + FontWeight + .bold, + ), + ), + Text( + transactionController + .model + .receiptData?["charge"] + .toStringAsFixed( + 2, + ) ?? + "0.00", + style: TextStyle( + fontSize: 16, + ), + ), + ], ), - ), - Text( - transactionController - .model - .receiptData?["charge"] - .toStringAsFixed( - 2, - ) ?? - "0.00", - style: TextStyle( - fontSize: 16, + const SizedBox( + height: 10, ), - ), - ], - ), - const SizedBox(height: 10), - Row( - mainAxisAlignment: - MainAxisAlignment - .spaceBetween, - children: [ - Text( - "Gateway Charge", - style: TextStyle( - fontSize: 16, - fontWeight: - FontWeight.bold, + ], + ), + if (transactionController + .model + .receiptData?["gatewayCharge"] != + 0) + Column( + children: [ + Row( + mainAxisAlignment: + MainAxisAlignment + .spaceBetween, + children: [ + Text( + "Gateway Charge", + style: TextStyle( + fontSize: 16, + fontWeight: + FontWeight + .bold, + ), + ), + Text( + transactionController + .model + .receiptData?["gatewayCharge"] + .toStringAsFixed( + 2, + ) ?? + "0.00", + style: TextStyle( + fontSize: 16, + ), + ), + ], ), - ), - Text( - transactionController - .model - .receiptData?["gatewayCharge"] - .toStringAsFixed( - 2, - ) ?? - "0.00", - style: TextStyle( - fontSize: 16, + const SizedBox( + height: 10, ), - ), - ], - ), - const SizedBox(height: 10), - Row( - mainAxisAlignment: - MainAxisAlignment - .spaceBetween, - children: [ - Text( - "Tax", - style: TextStyle( - fontSize: 16, - fontWeight: - FontWeight.bold, + ], + ), + if (transactionController + .model + .receiptData?["tax"] != + 0) + Column( + children: [ + Row( + mainAxisAlignment: + MainAxisAlignment + .spaceBetween, + children: [ + Text( + "Tax", + style: TextStyle( + fontSize: 16, + fontWeight: + FontWeight + .bold, + ), + ), + Text( + transactionController + .model + .receiptData?["tax"] + .toStringAsFixed( + 2, + ) ?? + "0.00", + style: TextStyle( + fontSize: 16, + ), + ), + ], ), - ), - Text( - transactionController - .model - .receiptData?["tax"] - .toStringAsFixed( - 2, - ) ?? - "0.00", - style: TextStyle( - fontSize: 16, + const SizedBox( + height: 10, ), - ), - ], - ), - const SizedBox(height: 10), + ], + ), Row( mainAxisAlignment: MainAxisAlignment diff --git a/lib/screens/recipient/recipients_controller.dart b/lib/screens/recipient/recipients_controller.dart index f42bbcd..f01c512 100644 --- a/lib/screens/recipient/recipients_controller.dart +++ b/lib/screens/recipient/recipients_controller.dart @@ -67,7 +67,7 @@ class RecipientsController extends ChangeNotifier { try { List response = await http.get( - '/recipients/search?${buildQueryParameters(params ?? {})}', + '/public/recipients/search?${buildQueryParameters(params ?? {})}', ); model.recipients.clear(); model.recipients.addAll(response.map((e) => Recipient.fromJson(e))); diff --git a/lib/screens/transaction_controller.dart b/lib/screens/transaction_controller.dart index 973ca8a..0fee1e9 100644 --- a/lib/screens/transaction_controller.dart +++ b/lib/screens/transaction_controller.dart @@ -15,6 +15,7 @@ class TransactionModel { BillProvider? selectedProvider; Map? receiptData; FormData formData = FormData( + id: '', type: '', billClientId: '', debitRef: '', @@ -49,6 +50,7 @@ abstract class FormData with _$FormData { const factory FormData({ required String type, // CONFIRM, REQUEST, REVERSE required String billClientId, + required String id, required String debitRef, required String debitCurrency, required String amount, diff --git a/lib/screens/transaction_controller.freezed.dart b/lib/screens/transaction_controller.freezed.dart index 602c635..c204d16 100644 --- a/lib/screens/transaction_controller.freezed.dart +++ b/lib/screens/transaction_controller.freezed.dart @@ -17,7 +17,7 @@ T _$identity(T value) => value; mixin _$FormData { String get type;// CONFIRM, REQUEST, REVERSE - String get billClientId; String get debitRef; String get debitCurrency; String get amount; String get creditAccount; String? get creditPhone; String? get creditName; String? get creditEmail; String get billName; String? get errorMessage; String get status; String get paymentProcessorLabel; String get paymentProcessorName; String get paymentProcessorImage; String get providerImage; String get providerLabel; String get userId; String? get debitPhone; String? get debitAccount; String? get productUid; String? get trace; String? get authType; String? get charge; String? get gatewayCharge; String? get tax; String? get totalAmount; + String get billClientId; String get id; String get debitRef; String get debitCurrency; String get amount; String get creditAccount; String? get creditPhone; String? get creditName; String? get creditEmail; String get billName; String? get errorMessage; String get status; String get paymentProcessorLabel; String get paymentProcessorName; String get paymentProcessorImage; String get providerImage; String get providerLabel; String get userId; String? get debitPhone; String? get debitAccount; String? get productUid; String? get trace; String? get authType; String? get charge; String? get gatewayCharge; String? get tax; String? get totalAmount; /// Create a copy of FormData /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @@ -30,16 +30,16 @@ $FormDataCopyWith get copyWith => _$FormDataCopyWithImpl(thi @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is FormData&&(identical(other.type, type) || other.type == type)&&(identical(other.billClientId, billClientId) || other.billClientId == billClientId)&&(identical(other.debitRef, debitRef) || other.debitRef == debitRef)&&(identical(other.debitCurrency, debitCurrency) || other.debitCurrency == debitCurrency)&&(identical(other.amount, amount) || other.amount == amount)&&(identical(other.creditAccount, creditAccount) || other.creditAccount == creditAccount)&&(identical(other.creditPhone, creditPhone) || other.creditPhone == creditPhone)&&(identical(other.creditName, creditName) || other.creditName == creditName)&&(identical(other.creditEmail, creditEmail) || other.creditEmail == creditEmail)&&(identical(other.billName, billName) || other.billName == billName)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.status, status) || other.status == status)&&(identical(other.paymentProcessorLabel, paymentProcessorLabel) || other.paymentProcessorLabel == paymentProcessorLabel)&&(identical(other.paymentProcessorName, paymentProcessorName) || other.paymentProcessorName == paymentProcessorName)&&(identical(other.paymentProcessorImage, paymentProcessorImage) || other.paymentProcessorImage == paymentProcessorImage)&&(identical(other.providerImage, providerImage) || other.providerImage == providerImage)&&(identical(other.providerLabel, providerLabel) || other.providerLabel == providerLabel)&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.debitPhone, debitPhone) || other.debitPhone == debitPhone)&&(identical(other.debitAccount, debitAccount) || other.debitAccount == debitAccount)&&(identical(other.productUid, productUid) || other.productUid == productUid)&&(identical(other.trace, trace) || other.trace == trace)&&(identical(other.authType, authType) || other.authType == authType)&&(identical(other.charge, charge) || other.charge == charge)&&(identical(other.gatewayCharge, gatewayCharge) || other.gatewayCharge == gatewayCharge)&&(identical(other.tax, tax) || other.tax == tax)&&(identical(other.totalAmount, totalAmount) || other.totalAmount == totalAmount)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is FormData&&(identical(other.type, type) || other.type == type)&&(identical(other.billClientId, billClientId) || other.billClientId == billClientId)&&(identical(other.id, id) || other.id == id)&&(identical(other.debitRef, debitRef) || other.debitRef == debitRef)&&(identical(other.debitCurrency, debitCurrency) || other.debitCurrency == debitCurrency)&&(identical(other.amount, amount) || other.amount == amount)&&(identical(other.creditAccount, creditAccount) || other.creditAccount == creditAccount)&&(identical(other.creditPhone, creditPhone) || other.creditPhone == creditPhone)&&(identical(other.creditName, creditName) || other.creditName == creditName)&&(identical(other.creditEmail, creditEmail) || other.creditEmail == creditEmail)&&(identical(other.billName, billName) || other.billName == billName)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.status, status) || other.status == status)&&(identical(other.paymentProcessorLabel, paymentProcessorLabel) || other.paymentProcessorLabel == paymentProcessorLabel)&&(identical(other.paymentProcessorName, paymentProcessorName) || other.paymentProcessorName == paymentProcessorName)&&(identical(other.paymentProcessorImage, paymentProcessorImage) || other.paymentProcessorImage == paymentProcessorImage)&&(identical(other.providerImage, providerImage) || other.providerImage == providerImage)&&(identical(other.providerLabel, providerLabel) || other.providerLabel == providerLabel)&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.debitPhone, debitPhone) || other.debitPhone == debitPhone)&&(identical(other.debitAccount, debitAccount) || other.debitAccount == debitAccount)&&(identical(other.productUid, productUid) || other.productUid == productUid)&&(identical(other.trace, trace) || other.trace == trace)&&(identical(other.authType, authType) || other.authType == authType)&&(identical(other.charge, charge) || other.charge == charge)&&(identical(other.gatewayCharge, gatewayCharge) || other.gatewayCharge == gatewayCharge)&&(identical(other.tax, tax) || other.tax == tax)&&(identical(other.totalAmount, totalAmount) || other.totalAmount == totalAmount)); } @JsonKey(includeFromJson: false, includeToJson: false) @override -int get hashCode => Object.hashAll([runtimeType,type,billClientId,debitRef,debitCurrency,amount,creditAccount,creditPhone,creditName,creditEmail,billName,errorMessage,status,paymentProcessorLabel,paymentProcessorName,paymentProcessorImage,providerImage,providerLabel,userId,debitPhone,debitAccount,productUid,trace,authType,charge,gatewayCharge,tax,totalAmount]); +int get hashCode => Object.hashAll([runtimeType,type,billClientId,id,debitRef,debitCurrency,amount,creditAccount,creditPhone,creditName,creditEmail,billName,errorMessage,status,paymentProcessorLabel,paymentProcessorName,paymentProcessorImage,providerImage,providerLabel,userId,debitPhone,debitAccount,productUid,trace,authType,charge,gatewayCharge,tax,totalAmount]); @override String toString() { - return 'FormData(type: $type, billClientId: $billClientId, debitRef: $debitRef, debitCurrency: $debitCurrency, amount: $amount, creditAccount: $creditAccount, creditPhone: $creditPhone, creditName: $creditName, creditEmail: $creditEmail, billName: $billName, errorMessage: $errorMessage, status: $status, paymentProcessorLabel: $paymentProcessorLabel, paymentProcessorName: $paymentProcessorName, paymentProcessorImage: $paymentProcessorImage, providerImage: $providerImage, providerLabel: $providerLabel, userId: $userId, debitPhone: $debitPhone, debitAccount: $debitAccount, productUid: $productUid, trace: $trace, authType: $authType, charge: $charge, gatewayCharge: $gatewayCharge, tax: $tax, totalAmount: $totalAmount)'; + return 'FormData(type: $type, billClientId: $billClientId, id: $id, debitRef: $debitRef, debitCurrency: $debitCurrency, amount: $amount, creditAccount: $creditAccount, creditPhone: $creditPhone, creditName: $creditName, creditEmail: $creditEmail, billName: $billName, errorMessage: $errorMessage, status: $status, paymentProcessorLabel: $paymentProcessorLabel, paymentProcessorName: $paymentProcessorName, paymentProcessorImage: $paymentProcessorImage, providerImage: $providerImage, providerLabel: $providerLabel, userId: $userId, debitPhone: $debitPhone, debitAccount: $debitAccount, productUid: $productUid, trace: $trace, authType: $authType, charge: $charge, gatewayCharge: $gatewayCharge, tax: $tax, totalAmount: $totalAmount)'; } @@ -50,7 +50,7 @@ abstract mixin class $FormDataCopyWith<$Res> { factory $FormDataCopyWith(FormData value, $Res Function(FormData) _then) = _$FormDataCopyWithImpl; @useResult $Res call({ - String type, String billClientId, String debitRef, String debitCurrency, String amount, String creditAccount, String? creditPhone, String? creditName, String? creditEmail, String billName, String? errorMessage, String status, String paymentProcessorLabel, String paymentProcessorName, String paymentProcessorImage, String providerImage, String providerLabel, String userId, String? debitPhone, String? debitAccount, String? productUid, String? trace, String? authType, String? charge, String? gatewayCharge, String? tax, String? totalAmount + String type, String billClientId, String id, String debitRef, String debitCurrency, String amount, String creditAccount, String? creditPhone, String? creditName, String? creditEmail, String billName, String? errorMessage, String status, String paymentProcessorLabel, String paymentProcessorName, String paymentProcessorImage, String providerImage, String providerLabel, String userId, String? debitPhone, String? debitAccount, String? productUid, String? trace, String? authType, String? charge, String? gatewayCharge, String? tax, String? totalAmount }); @@ -67,10 +67,11 @@ class _$FormDataCopyWithImpl<$Res> /// Create a copy of FormData /// with the given fields replaced by the non-null parameter values. -@pragma('vm:prefer-inline') @override $Res call({Object? type = null,Object? billClientId = null,Object? debitRef = null,Object? debitCurrency = null,Object? amount = null,Object? creditAccount = null,Object? creditPhone = freezed,Object? creditName = freezed,Object? creditEmail = freezed,Object? billName = null,Object? errorMessage = freezed,Object? status = null,Object? paymentProcessorLabel = null,Object? paymentProcessorName = null,Object? paymentProcessorImage = null,Object? providerImage = null,Object? providerLabel = null,Object? userId = null,Object? debitPhone = freezed,Object? debitAccount = freezed,Object? productUid = freezed,Object? trace = freezed,Object? authType = freezed,Object? charge = freezed,Object? gatewayCharge = freezed,Object? tax = freezed,Object? totalAmount = freezed,}) { +@pragma('vm:prefer-inline') @override $Res call({Object? type = null,Object? billClientId = null,Object? id = null,Object? debitRef = null,Object? debitCurrency = null,Object? amount = null,Object? creditAccount = null,Object? creditPhone = freezed,Object? creditName = freezed,Object? creditEmail = freezed,Object? billName = null,Object? errorMessage = freezed,Object? status = null,Object? paymentProcessorLabel = null,Object? paymentProcessorName = null,Object? paymentProcessorImage = null,Object? providerImage = null,Object? providerLabel = null,Object? userId = null,Object? debitPhone = freezed,Object? debitAccount = freezed,Object? productUid = freezed,Object? trace = freezed,Object? authType = freezed,Object? charge = freezed,Object? gatewayCharge = freezed,Object? tax = freezed,Object? totalAmount = freezed,}) { return _then(_self.copyWith( type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable as String,billClientId: null == billClientId ? _self.billClientId : billClientId // ignore: cast_nullable_to_non_nullable +as String,id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable as String,debitRef: null == debitRef ? _self.debitRef : debitRef // ignore: cast_nullable_to_non_nullable as String,debitCurrency: null == debitCurrency ? _self.debitCurrency : debitCurrency // ignore: cast_nullable_to_non_nullable as String,amount: null == amount ? _self.amount : amount // ignore: cast_nullable_to_non_nullable @@ -107,12 +108,13 @@ as String?, @JsonSerializable() class _FormData implements FormData { - const _FormData({required this.type, required this.billClientId, required this.debitRef, required this.debitCurrency, required this.amount, required this.creditAccount, required this.creditPhone, required this.creditName, required this.creditEmail, required this.billName, required this.errorMessage, required this.status, required this.paymentProcessorLabel, required this.paymentProcessorName, required this.paymentProcessorImage, required this.providerImage, required this.providerLabel, required this.userId, this.debitPhone, this.debitAccount, this.productUid, this.trace, this.authType, this.charge, this.gatewayCharge, this.tax, this.totalAmount}); + const _FormData({required this.type, required this.billClientId, required this.id, required this.debitRef, required this.debitCurrency, required this.amount, required this.creditAccount, required this.creditPhone, required this.creditName, required this.creditEmail, required this.billName, required this.errorMessage, required this.status, required this.paymentProcessorLabel, required this.paymentProcessorName, required this.paymentProcessorImage, required this.providerImage, required this.providerLabel, required this.userId, this.debitPhone, this.debitAccount, this.productUid, this.trace, this.authType, this.charge, this.gatewayCharge, this.tax, this.totalAmount}); factory _FormData.fromJson(Map json) => _$FormDataFromJson(json); @override final String type; // CONFIRM, REQUEST, REVERSE @override final String billClientId; +@override final String id; @override final String debitRef; @override final String debitCurrency; @override final String amount; @@ -152,16 +154,16 @@ Map toJson() { @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is _FormData&&(identical(other.type, type) || other.type == type)&&(identical(other.billClientId, billClientId) || other.billClientId == billClientId)&&(identical(other.debitRef, debitRef) || other.debitRef == debitRef)&&(identical(other.debitCurrency, debitCurrency) || other.debitCurrency == debitCurrency)&&(identical(other.amount, amount) || other.amount == amount)&&(identical(other.creditAccount, creditAccount) || other.creditAccount == creditAccount)&&(identical(other.creditPhone, creditPhone) || other.creditPhone == creditPhone)&&(identical(other.creditName, creditName) || other.creditName == creditName)&&(identical(other.creditEmail, creditEmail) || other.creditEmail == creditEmail)&&(identical(other.billName, billName) || other.billName == billName)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.status, status) || other.status == status)&&(identical(other.paymentProcessorLabel, paymentProcessorLabel) || other.paymentProcessorLabel == paymentProcessorLabel)&&(identical(other.paymentProcessorName, paymentProcessorName) || other.paymentProcessorName == paymentProcessorName)&&(identical(other.paymentProcessorImage, paymentProcessorImage) || other.paymentProcessorImage == paymentProcessorImage)&&(identical(other.providerImage, providerImage) || other.providerImage == providerImage)&&(identical(other.providerLabel, providerLabel) || other.providerLabel == providerLabel)&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.debitPhone, debitPhone) || other.debitPhone == debitPhone)&&(identical(other.debitAccount, debitAccount) || other.debitAccount == debitAccount)&&(identical(other.productUid, productUid) || other.productUid == productUid)&&(identical(other.trace, trace) || other.trace == trace)&&(identical(other.authType, authType) || other.authType == authType)&&(identical(other.charge, charge) || other.charge == charge)&&(identical(other.gatewayCharge, gatewayCharge) || other.gatewayCharge == gatewayCharge)&&(identical(other.tax, tax) || other.tax == tax)&&(identical(other.totalAmount, totalAmount) || other.totalAmount == totalAmount)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is _FormData&&(identical(other.type, type) || other.type == type)&&(identical(other.billClientId, billClientId) || other.billClientId == billClientId)&&(identical(other.id, id) || other.id == id)&&(identical(other.debitRef, debitRef) || other.debitRef == debitRef)&&(identical(other.debitCurrency, debitCurrency) || other.debitCurrency == debitCurrency)&&(identical(other.amount, amount) || other.amount == amount)&&(identical(other.creditAccount, creditAccount) || other.creditAccount == creditAccount)&&(identical(other.creditPhone, creditPhone) || other.creditPhone == creditPhone)&&(identical(other.creditName, creditName) || other.creditName == creditName)&&(identical(other.creditEmail, creditEmail) || other.creditEmail == creditEmail)&&(identical(other.billName, billName) || other.billName == billName)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.status, status) || other.status == status)&&(identical(other.paymentProcessorLabel, paymentProcessorLabel) || other.paymentProcessorLabel == paymentProcessorLabel)&&(identical(other.paymentProcessorName, paymentProcessorName) || other.paymentProcessorName == paymentProcessorName)&&(identical(other.paymentProcessorImage, paymentProcessorImage) || other.paymentProcessorImage == paymentProcessorImage)&&(identical(other.providerImage, providerImage) || other.providerImage == providerImage)&&(identical(other.providerLabel, providerLabel) || other.providerLabel == providerLabel)&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.debitPhone, debitPhone) || other.debitPhone == debitPhone)&&(identical(other.debitAccount, debitAccount) || other.debitAccount == debitAccount)&&(identical(other.productUid, productUid) || other.productUid == productUid)&&(identical(other.trace, trace) || other.trace == trace)&&(identical(other.authType, authType) || other.authType == authType)&&(identical(other.charge, charge) || other.charge == charge)&&(identical(other.gatewayCharge, gatewayCharge) || other.gatewayCharge == gatewayCharge)&&(identical(other.tax, tax) || other.tax == tax)&&(identical(other.totalAmount, totalAmount) || other.totalAmount == totalAmount)); } @JsonKey(includeFromJson: false, includeToJson: false) @override -int get hashCode => Object.hashAll([runtimeType,type,billClientId,debitRef,debitCurrency,amount,creditAccount,creditPhone,creditName,creditEmail,billName,errorMessage,status,paymentProcessorLabel,paymentProcessorName,paymentProcessorImage,providerImage,providerLabel,userId,debitPhone,debitAccount,productUid,trace,authType,charge,gatewayCharge,tax,totalAmount]); +int get hashCode => Object.hashAll([runtimeType,type,billClientId,id,debitRef,debitCurrency,amount,creditAccount,creditPhone,creditName,creditEmail,billName,errorMessage,status,paymentProcessorLabel,paymentProcessorName,paymentProcessorImage,providerImage,providerLabel,userId,debitPhone,debitAccount,productUid,trace,authType,charge,gatewayCharge,tax,totalAmount]); @override String toString() { - return 'FormData(type: $type, billClientId: $billClientId, debitRef: $debitRef, debitCurrency: $debitCurrency, amount: $amount, creditAccount: $creditAccount, creditPhone: $creditPhone, creditName: $creditName, creditEmail: $creditEmail, billName: $billName, errorMessage: $errorMessage, status: $status, paymentProcessorLabel: $paymentProcessorLabel, paymentProcessorName: $paymentProcessorName, paymentProcessorImage: $paymentProcessorImage, providerImage: $providerImage, providerLabel: $providerLabel, userId: $userId, debitPhone: $debitPhone, debitAccount: $debitAccount, productUid: $productUid, trace: $trace, authType: $authType, charge: $charge, gatewayCharge: $gatewayCharge, tax: $tax, totalAmount: $totalAmount)'; + return 'FormData(type: $type, billClientId: $billClientId, id: $id, debitRef: $debitRef, debitCurrency: $debitCurrency, amount: $amount, creditAccount: $creditAccount, creditPhone: $creditPhone, creditName: $creditName, creditEmail: $creditEmail, billName: $billName, errorMessage: $errorMessage, status: $status, paymentProcessorLabel: $paymentProcessorLabel, paymentProcessorName: $paymentProcessorName, paymentProcessorImage: $paymentProcessorImage, providerImage: $providerImage, providerLabel: $providerLabel, userId: $userId, debitPhone: $debitPhone, debitAccount: $debitAccount, productUid: $productUid, trace: $trace, authType: $authType, charge: $charge, gatewayCharge: $gatewayCharge, tax: $tax, totalAmount: $totalAmount)'; } @@ -172,7 +174,7 @@ abstract mixin class _$FormDataCopyWith<$Res> implements $FormDataCopyWith<$Res> factory _$FormDataCopyWith(_FormData value, $Res Function(_FormData) _then) = __$FormDataCopyWithImpl; @override @useResult $Res call({ - String type, String billClientId, String debitRef, String debitCurrency, String amount, String creditAccount, String? creditPhone, String? creditName, String? creditEmail, String billName, String? errorMessage, String status, String paymentProcessorLabel, String paymentProcessorName, String paymentProcessorImage, String providerImage, String providerLabel, String userId, String? debitPhone, String? debitAccount, String? productUid, String? trace, String? authType, String? charge, String? gatewayCharge, String? tax, String? totalAmount + String type, String billClientId, String id, String debitRef, String debitCurrency, String amount, String creditAccount, String? creditPhone, String? creditName, String? creditEmail, String billName, String? errorMessage, String status, String paymentProcessorLabel, String paymentProcessorName, String paymentProcessorImage, String providerImage, String providerLabel, String userId, String? debitPhone, String? debitAccount, String? productUid, String? trace, String? authType, String? charge, String? gatewayCharge, String? tax, String? totalAmount }); @@ -189,10 +191,11 @@ class __$FormDataCopyWithImpl<$Res> /// Create a copy of FormData /// with the given fields replaced by the non-null parameter values. -@override @pragma('vm:prefer-inline') $Res call({Object? type = null,Object? billClientId = null,Object? debitRef = null,Object? debitCurrency = null,Object? amount = null,Object? creditAccount = null,Object? creditPhone = freezed,Object? creditName = freezed,Object? creditEmail = freezed,Object? billName = null,Object? errorMessage = freezed,Object? status = null,Object? paymentProcessorLabel = null,Object? paymentProcessorName = null,Object? paymentProcessorImage = null,Object? providerImage = null,Object? providerLabel = null,Object? userId = null,Object? debitPhone = freezed,Object? debitAccount = freezed,Object? productUid = freezed,Object? trace = freezed,Object? authType = freezed,Object? charge = freezed,Object? gatewayCharge = freezed,Object? tax = freezed,Object? totalAmount = freezed,}) { +@override @pragma('vm:prefer-inline') $Res call({Object? type = null,Object? billClientId = null,Object? id = null,Object? debitRef = null,Object? debitCurrency = null,Object? amount = null,Object? creditAccount = null,Object? creditPhone = freezed,Object? creditName = freezed,Object? creditEmail = freezed,Object? billName = null,Object? errorMessage = freezed,Object? status = null,Object? paymentProcessorLabel = null,Object? paymentProcessorName = null,Object? paymentProcessorImage = null,Object? providerImage = null,Object? providerLabel = null,Object? userId = null,Object? debitPhone = freezed,Object? debitAccount = freezed,Object? productUid = freezed,Object? trace = freezed,Object? authType = freezed,Object? charge = freezed,Object? gatewayCharge = freezed,Object? tax = freezed,Object? totalAmount = freezed,}) { return _then(_FormData( type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable as String,billClientId: null == billClientId ? _self.billClientId : billClientId // ignore: cast_nullable_to_non_nullable +as String,id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable as String,debitRef: null == debitRef ? _self.debitRef : debitRef // ignore: cast_nullable_to_non_nullable as String,debitCurrency: null == debitCurrency ? _self.debitCurrency : debitCurrency // ignore: cast_nullable_to_non_nullable as String,amount: null == amount ? _self.amount : amount // ignore: cast_nullable_to_non_nullable diff --git a/lib/screens/transaction_controller.g.dart b/lib/screens/transaction_controller.g.dart index 01206c3..19be559 100644 --- a/lib/screens/transaction_controller.g.dart +++ b/lib/screens/transaction_controller.g.dart @@ -9,6 +9,7 @@ part of 'transaction_controller.dart'; _FormData _$FormDataFromJson(Map json) => _FormData( type: json['type'] as String, billClientId: json['billClientId'] as String, + id: json['id'] as String, debitRef: json['debitRef'] as String, debitCurrency: json['debitCurrency'] as String, amount: json['amount'] as String, @@ -39,6 +40,7 @@ _FormData _$FormDataFromJson(Map json) => _FormData( Map _$FormDataToJson(_FormData instance) => { 'type': instance.type, 'billClientId': instance.billClientId, + 'id': instance.id, 'debitRef': instance.debitRef, 'debitCurrency': instance.debitCurrency, 'amount': instance.amount, diff --git a/lib/theme/app_theme.dart b/lib/theme/app_theme.dart index be90ccc..9581ce5 100644 --- a/lib/theme/app_theme.dart +++ b/lib/theme/app_theme.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; class AppTheme { - static const _primaryColor = Color(0xFFD4AF37); // Gold + static const _primaryColor = Color(0xFF9D6711); // #9D6711 static const _secondaryColor = Color(0xFF8B0000); // Dark Red static const _tertiaryColor = Color(0xFF000000); // Black static const _backgroundColor = Color(0xFFFFFBFE); @@ -56,19 +56,19 @@ class AppTheme { fillColor: _surfaceColor, border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), - borderSide: BorderSide.none, + borderSide: BorderSide(color: Colors.grey.shade300, width: 1), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), - borderSide: BorderSide.none, + borderSide: BorderSide(color: Colors.grey.shade300, width: 1), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), - borderSide: const BorderSide(color: _primaryColor), + borderSide: const BorderSide(color: _primaryColor, width: 2), ), errorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), - borderSide: const BorderSide(color: _errorColor), + borderSide: const BorderSide(color: _errorColor, width: 1), ), contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), ),