diff --git a/assets/pngs/forgot_password.png b/assets/pngs/forgot_password.png new file mode 100644 index 0000000..5e9c728 Binary files /dev/null and b/assets/pngs/forgot_password.png differ diff --git a/lib/core/config/dependencies.dart b/lib/core/config/dependencies.dart index 1e564dc..f4e8a77 100644 --- a/lib/core/config/dependencies.dart +++ b/lib/core/config/dependencies.dart @@ -85,7 +85,11 @@ List get viewModels { create: (context) => HomeViewModel(homeRepository: context.read()), ), ChangeNotifierProvider( - create: (context) => TendersViewModel(tendersRepository: context.read()), + create: + (context) => TendersViewModel( + tendersRepository: context.read(), + homeViewModel: context.read(), + ), ), ChangeNotifierProvider( create: @@ -93,6 +97,7 @@ List get viewModels { tenderDetailRepository: context.read(), tendersRepository: context.read(), tendersViewModel: context.read(), + homeViewModel: context.read(), ), ), diff --git a/lib/core/constants/assets.dart b/lib/core/constants/assets.dart index deccf2c..3ee2671 100644 --- a/lib/core/constants/assets.dart +++ b/lib/core/constants/assets.dart @@ -22,6 +22,9 @@ class AssetsManager { static const passwordVisibilityOff = 'assets/svgs/password_visibility_off.svg'; + //forgot password page + static const forgotPasswordImage = 'assets/pngs/forgot_password.png'; + // tenders page static const tenderLogo = 'assets/icons/tenderLogo.png'; static const like = 'assets/icons/like.svg'; diff --git a/lib/core/constants/strings.dart b/lib/core/constants/strings.dart index ad77dff..9c14e7b 100644 --- a/lib/core/constants/strings.dart +++ b/lib/core/constants/strings.dart @@ -25,6 +25,14 @@ class AppStrings { static const passwordLabel = 'Password'; static const loginButton = 'Login'; + //forgot password + static const String forgotPasswordTitle = 'Forgot Password'; + static const String forgotPasswordSubtitle = + 'Enter your registered email and company ID and submit your request.'; + static const String emailAddressHint = 'Email Address'; + static const String backToSignInButton = 'Back to Sign In'; + static const String resetLink = 'Reset Link'; + //Home static const String home = 'Home'; static const String partnership = 'Partnership'; diff --git a/lib/core/routes/app_routes.dart b/lib/core/routes/app_routes.dart index ad01d6c..101dda3 100644 --- a/lib/core/routes/app_routes.dart +++ b/lib/core/routes/app_routes.dart @@ -4,6 +4,7 @@ import 'package:tm_app/views/detail/pages/detail_screen.dart'; import 'package:tm_app/views/home/pages/home_screen.dart'; import 'package:tm_app/views/login/pages/login_screen.dart'; +import '../../views/forgot_password/pages/forgot_password_screen.dart'; import '../../views/profile/pages/profile_screen.dart'; import '../../views/tenders/pages/tenders_screen.dart'; import '../../views/your_tenders/pages/your_tenders_screen.dart'; @@ -157,3 +158,14 @@ class TenderDetailRouteData extends GoRouteData with _$TenderDetailRouteData { return TenderDetailScreen(tenderId: tenderId); } } + +@TypedGoRoute(path: '/forgot-password') +class ForgotPasswordRouteData extends GoRouteData + with _$ForgotPasswordRouteData { + const ForgotPasswordRouteData(); + + @override + Widget build(BuildContext context, GoRouterState state) { + return ForgotPasswordScreen(); + } +} diff --git a/lib/core/routes/app_routes.g.dart b/lib/core/routes/app_routes.g.dart index 708bf42..aa78fd8 100644 --- a/lib/core/routes/app_routes.g.dart +++ b/lib/core/routes/app_routes.g.dart @@ -11,6 +11,7 @@ List get $appRoutes => [ $appShellRouteData, $yourTendersRouteData, $tenderDetailRouteData, + $forgotPasswordRouteData, ]; RouteBase get $loginScreenRoute => @@ -198,3 +199,30 @@ mixin _$TenderDetailRouteData on GoRouteData { @override void replace(BuildContext context) => context.replace(location); } + +RouteBase get $forgotPasswordRouteData => GoRouteData.$route( + path: '/forgot-password', + + factory: _$ForgotPasswordRouteData._fromState, +); + +mixin _$ForgotPasswordRouteData on GoRouteData { + static ForgotPasswordRouteData _fromState(GoRouterState state) => + const ForgotPasswordRouteData(); + + @override + String get location => GoRouteData.$location('/forgot-password'); + + @override + void go(BuildContext context) => context.go(location); + + @override + Future push(BuildContext context) => context.push(location); + + @override + void pushReplacement(BuildContext context) => + context.pushReplacement(location); + + @override + void replace(BuildContext context) => context.replace(location); +} diff --git a/lib/gen/assets.gen.dart b/lib/gen/assets.gen.dart index 230abd3..c78f850 100644 --- a/lib/gen/assets.gen.dart +++ b/lib/gen/assets.gen.dart @@ -17,6 +17,9 @@ class $AssetsIconsGen { /// File path: assets/icons/SE.png AssetGenImage get se => const AssetGenImage('assets/icons/SE.png'); + /// File path: assets/icons/approve.svg + String get approve => 'assets/icons/approve.svg'; + /// File path: assets/icons/arrow-circle-left.svg String get arrowCircleLeft => 'assets/icons/arrow-circle-left.svg'; @@ -59,6 +62,12 @@ class $AssetsIconsGen { /// File path: assets/icons/location.svg String get location => 'assets/icons/location.svg'; + /// File path: assets/icons/logo_big.svg + String get logoBig => 'assets/icons/logo_big.svg'; + + /// File path: assets/icons/logo_small.svg + String get logoSmall => 'assets/icons/logo_small.svg'; + /// File path: assets/icons/menu.svg String get menu => 'assets/icons/menu.svg'; @@ -96,6 +105,9 @@ class $AssetsIconsGen { /// File path: assets/icons/thumb.svg String get thumb => 'assets/icons/thumb.svg'; + /// File path: assets/icons/thumb_like.svg + String get thumbLike => 'assets/icons/thumb_like.svg'; + /// File path: assets/icons/tick-circle.svg String get tickCircle => 'assets/icons/tick-circle.svg'; @@ -105,6 +117,7 @@ class $AssetsIconsGen { /// List of all assets List get values => [ se, + approve, arrowCircleLeft, arrowCircleRight, arrowLeftSmall, @@ -119,6 +132,8 @@ class $AssetsIconsGen { homeActive, like, location, + logoBig, + logoSmall, menu, moon, notification, @@ -131,6 +146,7 @@ class $AssetsIconsGen { tenderLogoPng, tenderLogoSvg, thumb, + thumbLike, tickCircle, tick, ]; diff --git a/lib/view_models/auth_view_model.dart b/lib/view_models/auth_view_model.dart index 02c5641..fde0344 100644 --- a/lib/view_models/auth_view_model.dart +++ b/lib/view_models/auth_view_model.dart @@ -19,8 +19,10 @@ class AuthViewModel with ChangeNotifier { final TextEditingController usernameController = TextEditingController(); final TextEditingController passwordController = TextEditingController(); + final TextEditingController emailController = TextEditingController(); final FocusNode usernameFocus = FocusNode(); final FocusNode passwordFocus = FocusNode(); + final FocusNode emailFocus = FocusNode(); bool _obscurePassword = true; bool get obscurePassword => _obscurePassword; diff --git a/lib/view_models/tender_detail_view_model.dart b/lib/view_models/tender_detail_view_model.dart index ca13cd2..627c9ca 100644 --- a/lib/view_models/tender_detail_view_model.dart +++ b/lib/view_models/tender_detail_view_model.dart @@ -8,20 +8,24 @@ import 'package:tm_app/data/services/model/tender_detail_response/tender_detail_ import '../data/services/model/tender_approvals_by_id_response/tender_approvals_by_id_response.dart'; import '../data/services/model/tender_approvals_data/tender_approvals_data.dart'; import '../data/services/model/tender_approvals_toggle_response/tender_approvals_toggle_response.dart'; +import 'home_view_model.dart'; import 'tenders_view_model.dart'; class TenderDetailViewModel with ChangeNotifier { final TenderDetailRepository _tenderDetailRepository; final TendersRepository _tendersRepository; final TendersViewModel _tendersViewModel; + final HomeViewModel _homeViewModel; TenderDetailViewModel({ required TenderDetailRepository tenderDetailRepository, required TendersRepository tendersRepository, required TendersViewModel tendersViewModel, + required HomeViewModel homeViewModel, }) : _tenderDetailRepository = tenderDetailRepository, _tendersRepository = tendersRepository, - _tendersViewModel = tendersViewModel; + _tendersViewModel = tendersViewModel, + _homeViewModel = homeViewModel; bool _isLoading = false; bool _isSubmitApprovalLoading = false; @@ -122,6 +126,10 @@ class TenderDetailViewModel with ChangeNotifier { _tendersViewModel.getTenderApprovals(tenderId); } + void updateHome() async { + _homeViewModel.init(); + } + Future getTenderApprovals(String tenderId) async { _errorMessage = null; notifyListeners(); diff --git a/lib/view_models/tenders_view_model.dart b/lib/view_models/tenders_view_model.dart index f2a9759..a1142f7 100644 --- a/lib/view_models/tenders_view_model.dart +++ b/lib/view_models/tenders_view_model.dart @@ -11,12 +11,17 @@ import 'package:tm_app/data/services/model/tenders/tenders_response/tenders_resp import '../core/utils/result.dart'; import '../data/services/model/tender_approvals_by_id_response/tender_approvals_by_id_response.dart'; import '../data/services/model/tender_approvals_data/tender_approvals_data.dart'; +import 'home_view_model.dart'; class TendersViewModel with ChangeNotifier { final TendersRepository _tendersRepository; + final HomeViewModel _homeViewModel; - TendersViewModel({required TendersRepository tendersRepository}) - : _tendersRepository = tendersRepository { + TendersViewModel({ + required TendersRepository tendersRepository, + required HomeViewModel homeViewModel, + }) : _tendersRepository = tendersRepository, + _homeViewModel = homeViewModel { WidgetsBinding.instance.addPostFrameCallback((_) { getTenders(); }); @@ -457,4 +462,8 @@ class TendersViewModel with ChangeNotifier { _tenderApprovalErrorMessage = null; notifyListeners(); } + + void updateHome() { + _homeViewModel.init(); + } } diff --git a/lib/views/detail/pages/detail_desktop_page.dart b/lib/views/detail/pages/detail_desktop_page.dart index 4748e33..7cde0ce 100644 --- a/lib/views/detail/pages/detail_desktop_page.dart +++ b/lib/views/detail/pages/detail_desktop_page.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; import 'package:provider/provider.dart'; import 'package:tm_app/core/theme/colors.dart'; import 'package:tm_app/core/utils/size_config.dart'; @@ -9,7 +8,7 @@ import 'package:tm_app/views/detail/widgets/tender_detail_card.dart'; import 'package:tm_app/views/detail/widgets/tender_detail_header.dart'; import '../../../core/constants/strings.dart'; -import '../../../view_models/home_view_model.dart'; +import '../../../core/routes/app_routes.dart'; import '../../shared/desktop_navigation_widget.dart'; class TenderDetailDesktopPage extends StatefulWidget { @@ -80,9 +79,7 @@ class _TenderDetailDesktopPageState extends State { return PopScope( onPopInvokedWithResult: (didPop, result) { WidgetsBinding.instance.addPostFrameCallback((_) { - context.read().updateTenderApprovals( - widget.tenderId, - ); + viewModel.updateTenderApprovals(widget.tenderId); }); }, child: Scaffold( @@ -112,14 +109,20 @@ class _TenderDetailDesktopPageState extends State { // Navigate to different screens based on index switch (index) { case 0: - Router.neglect(context, () => context.go('/home')); - context.read().init(); + Router.neglect( + context, + () => HomeRouteData().go(context), + ); + viewModel.updateHome(); break; case 1: break; case 2: // Navigate to profile - Router.neglect(context, () => context.go('/profile')); + Router.neglect( + context, + () => ProfileRouteData().go(context), + ); break; } }, diff --git a/lib/views/detail/pages/detail_tablet_page.dart b/lib/views/detail/pages/detail_tablet_page.dart index 191286c..aac0f6f 100644 --- a/lib/views/detail/pages/detail_tablet_page.dart +++ b/lib/views/detail/pages/detail_tablet_page.dart @@ -6,7 +6,6 @@ import 'package:provider/provider.dart'; import 'package:tm_app/core/constants/assets.dart'; import 'package:tm_app/core/theme/colors.dart'; import 'package:tm_app/core/utils/size_config.dart'; -import 'package:tm_app/view_models/home_view_model.dart'; import 'package:tm_app/view_models/tender_detail_view_model.dart'; import 'package:tm_app/views/detail/widgets/tender_detail_action.dart'; import 'package:tm_app/views/detail/widgets/tender_detail_card.dart'; @@ -14,6 +13,7 @@ import 'package:tm_app/views/detail/widgets/tender_detail_header.dart'; import 'package:tm_app/views/shared/tablet_navigation_widget.dart'; import '../../../core/constants/strings.dart'; +import '../../../core/routes/app_routes.dart'; class TenderDetailTabletPage extends StatefulWidget { final String tenderId; @@ -84,9 +84,7 @@ class _TenderDetailTabletPageState extends State { onPopInvokedWithResult: (didPop, result) { if (kIsWeb) { WidgetsBinding.instance.addPostFrameCallback((_) { - context.read().updateTenderApprovals( - widget.tenderId, - ); + viewModel.updateTenderApprovals(widget.tenderId); }); } }, @@ -124,15 +122,15 @@ class _TenderDetailTabletPageState extends State { // Handle navigation to other tabs switch (index) { case 0: - Router.neglect(context, () => context.go('/home')); - context.read().init(); + Router.neglect(context, () => HomeRouteData().go(context)); + viewModel.updateHome(); break; case 1: break; case 2: // Navigate to profile context.pop(); - Router.neglect(context, () => context.go('/profile')); + Router.neglect(context, () => ProfileRouteData().go(context)); break; } }, diff --git a/lib/views/forgot_password/pages/d_forgot_apssword_page.dart b/lib/views/forgot_password/pages/d_forgot_apssword_page.dart new file mode 100644 index 0000000..e76cdc5 --- /dev/null +++ b/lib/views/forgot_password/pages/d_forgot_apssword_page.dart @@ -0,0 +1,116 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:go_router/go_router.dart'; +import 'package:provider/provider.dart'; +import 'package:tm_app/core/utils/size_config.dart'; + +import '../../../core/constants/assets.dart'; +import '../../../core/constants/strings.dart'; +import '../../../core/theme/colors.dart'; +import '../../../view_models/auth_view_model.dart'; +import '../../login/widgets/login_textfield.dart'; +import '../../shared/base_button.dart'; + +class DesktopForgotPasswordPage extends StatelessWidget { + const DesktopForgotPasswordPage({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: AppColors.backgroundColor, + body: Center( + child: Consumer( + builder: (context, viewModel, _) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 120), + child: SizedBox( + width: 364, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: EdgeInsets.symmetric(horizontal: 22.0.w()), + child: SvgPicture.asset( + AssetsManager.logoBig, + width: double.infinity, + height: 50.0.h(), + ), + ), + SizedBox(height: 72.0.h()), + Text( + AppStrings.forgotPasswordTitle, + style: TextStyle( + fontSize: 28.0.sp(), + fontWeight: FontWeight.bold, + color: AppColors.grey80, + ), + ), + SizedBox(height: 16.0.h()), + Text( + AppStrings.forgotPasswordSubtitle, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 14.0.sp(), + fontWeight: FontWeight.w400, + color: AppColors.grey60, + ), + ), + SizedBox(height: 40.0.h()), + LoginTextField( + controller: viewModel.emailController, + focusNode: viewModel.emailFocus, + label: AppStrings.emailAddressHint, + isPassword: false, + ), + SizedBox(height: 70.0.h()), + BaseButton( + isEnabled: false, + text: AppStrings.resetLink, + onPressed: () async { + if (viewModel.isLoginEnabled) { + await viewModel.login(); + } + }, + ), + SizedBox(height: 12), + TextButton( + onPressed: () { + context.pop(); + }, + child: Text( + AppStrings.backToSignInButton, + style: TextStyle( + fontSize: 14.0.sp(), + fontWeight: FontWeight.w500, + color: AppColors.mainBlue, + ), + ), + ), + ], + ), + ), + ), + + Padding( + padding: EdgeInsets.symmetric(vertical: 20.0.h()), + child: ClipRRect( + borderRadius: BorderRadius.circular(32.0), + child: Image.asset( + AssetsManager.forgotPasswordImage, + width: 796.0, + height: 944.0, + fit: BoxFit.fitWidth, + ), + ), + ), + ], + ); + }, + ), + ), + ); + } +} diff --git a/lib/views/forgot_password/pages/forgot_password_screen.dart b/lib/views/forgot_password/pages/forgot_password_screen.dart new file mode 100644 index 0000000..617ad98 --- /dev/null +++ b/lib/views/forgot_password/pages/forgot_password_screen.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; + +import '../../shared/responsive_builder.dart'; +import 'd_forgot_apssword_page.dart'; +import 'm_forgot_password_page.dart'; +import 't_forgot_password_page.dart'; + +class ForgotPasswordScreen extends StatelessWidget { + const ForgotPasswordScreen({super.key}); + + @override + Widget build(BuildContext context) { + return ResponsiveBuilder( + mobile: MobileForgotPasswordPage(), + tablet: TabletForgotPasswordPage(), + desktop: DesktopForgotPasswordPage(), + ); + } +} diff --git a/lib/views/forgot_password/pages/m_forgot_password_page.dart b/lib/views/forgot_password/pages/m_forgot_password_page.dart new file mode 100644 index 0000000..101d239 --- /dev/null +++ b/lib/views/forgot_password/pages/m_forgot_password_page.dart @@ -0,0 +1,94 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:go_router/go_router.dart'; +import 'package:provider/provider.dart'; +import 'package:tm_app/core/utils/size_config.dart'; + +import '../../../core/constants/assets.dart'; +import '../../../core/constants/strings.dart'; +import '../../../core/theme/colors.dart'; +import '../../../view_models/auth_view_model.dart'; +import '../../login/widgets/login_textfield.dart'; +import '../../shared/base_button.dart'; + +class MobileForgotPasswordPage extends StatelessWidget { + const MobileForgotPasswordPage({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: AppColors.backgroundColor, + body: Center( + child: SingleChildScrollView( + padding: EdgeInsets.symmetric(horizontal: 24.0.w()), + child: Consumer( + builder: (context, viewModel, _) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: EdgeInsets.symmetric(horizontal: 22.0.w()), + child: SvgPicture.asset( + AssetsManager.logoBig, + width: double.infinity, + height: 50.0.h(), + ), + ), + SizedBox(height: 72.0.h()), + Text( + AppStrings.forgotPasswordTitle, + style: TextStyle( + fontSize: 28.0.sp(), + fontWeight: FontWeight.bold, + color: AppColors.grey80, + ), + ), + SizedBox(height: 16.0.h()), + Text( + AppStrings.forgotPasswordSubtitle, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 14.0.sp(), + fontWeight: FontWeight.w400, + color: AppColors.grey60, + ), + ), + SizedBox(height: 40.0.h()), + LoginTextField( + controller: viewModel.emailController, + focusNode: viewModel.emailFocus, + label: AppStrings.emailAddressHint, + isPassword: false, + ), + SizedBox(height: 70.0.h()), + BaseButton( + isEnabled: false, + text: AppStrings.resetLink, + onPressed: () async { + if (viewModel.isLoginEnabled) { + await viewModel.login(); + } + }, + ), + TextButton( + onPressed: () { + context.pop(); + }, + child: Text( + AppStrings.backToSignInButton, + style: TextStyle( + fontSize: 14.0.sp(), + fontWeight: FontWeight.w500, + color: AppColors.mainBlue, + ), + ), + ), + ], + ); + }, + ), + ), + ), + ); + } +} diff --git a/lib/views/forgot_password/pages/t_forgot_password_page.dart b/lib/views/forgot_password/pages/t_forgot_password_page.dart new file mode 100644 index 0000000..83cc1f4 --- /dev/null +++ b/lib/views/forgot_password/pages/t_forgot_password_page.dart @@ -0,0 +1,94 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:go_router/go_router.dart'; +import 'package:provider/provider.dart'; +import 'package:tm_app/core/utils/size_config.dart'; + +import '../../../core/constants/assets.dart'; +import '../../../core/constants/strings.dart'; +import '../../../core/theme/colors.dart'; +import '../../../view_models/auth_view_model.dart'; +import '../../login/widgets/login_textfield.dart'; +import '../../shared/base_button.dart'; + +class TabletForgotPasswordPage extends StatelessWidget { + const TabletForgotPasswordPage({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Consumer( + builder: (context, viewModel, _) { + return Center( + child: SizedBox( + width: 364, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: EdgeInsets.symmetric(horizontal: 22.0.w()), + child: SvgPicture.asset( + AssetsManager.logoBig, + width: double.infinity, + height: 50.0.h(), + ), + ), + SizedBox(height: 72.0.h()), + Text( + AppStrings.forgotPasswordTitle, + style: TextStyle( + fontSize: 28.0.sp(), + fontWeight: FontWeight.bold, + color: AppColors.grey80, + ), + ), + SizedBox(height: 16.0.h()), + Text( + AppStrings.forgotPasswordSubtitle, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 14.0.sp(), + fontWeight: FontWeight.w400, + color: AppColors.grey60, + ), + ), + SizedBox(height: 40.0.h()), + LoginTextField( + controller: viewModel.emailController, + focusNode: viewModel.emailFocus, + label: AppStrings.emailAddressHint, + isPassword: false, + ), + SizedBox(height: 70.0.h()), + BaseButton( + isEnabled: false, + text: AppStrings.resetLink, + onPressed: () async { + if (viewModel.isLoginEnabled) { + await viewModel.login(); + } + }, + ), + SizedBox(height: 12), + TextButton( + onPressed: () { + context.pop(); + }, + child: Text( + AppStrings.backToSignInButton, + style: TextStyle( + fontSize: 14.0.sp(), + fontWeight: FontWeight.w500, + color: AppColors.mainBlue, + ), + ), + ), + ], + ), + ), + ); + }, + ), + ); + } +} diff --git a/lib/views/home/pages/d_home_page.dart b/lib/views/home/pages/d_home_page.dart index 9c79fdc..408c8fb 100644 --- a/lib/views/home/pages/d_home_page.dart +++ b/lib/views/home/pages/d_home_page.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; import 'package:provider/provider.dart'; import 'package:tm_app/core/constants/assets.dart'; import 'package:tm_app/core/constants/strings.dart'; @@ -44,11 +43,17 @@ class DesktopHomePage extends StatelessWidget { break; case 1: // Navigate to tenders - Router.neglect(context, () => context.go('/tenders')); + Router.neglect( + context, + () => TendersRouteData().go(context), + ); break; case 2: // Navigate to profile - Router.neglect(context, () => context.go('/profile')); + Router.neglect( + context, + () => ProfileRouteData().go(context), + ); break; } }, diff --git a/lib/views/home/pages/t_home_page.dart b/lib/views/home/pages/t_home_page.dart index 22aa5b4..05ba91e 100644 --- a/lib/views/home/pages/t_home_page.dart +++ b/lib/views/home/pages/t_home_page.dart @@ -59,12 +59,12 @@ class TabletHomePage extends StatelessWidget { case 1: // Navigate to tenders context.pop(); - Router.neglect(context, () => context.go('/tenders')); + Router.neglect(context, () => TendersRouteData().go(context)); break; case 2: // Navigate to profile context.pop(); - Router.neglect(context, () => context.go('/profile')); + Router.neglect(context, () => ProfileRouteData().go(context)); break; } }, diff --git a/lib/views/login/pages/login_desktop_page.dart b/lib/views/login/pages/login_desktop_page.dart index fb31680..af88a3f 100644 --- a/lib/views/login/pages/login_desktop_page.dart +++ b/lib/views/login/pages/login_desktop_page.dart @@ -7,6 +7,7 @@ import 'package:tm_app/core/theme/colors.dart'; import '../../../core/constants/assets.dart'; import '../../../core/constants/strings.dart'; +import '../../../core/routes/app_routes.dart'; import '../../../core/utils/size_config.dart'; import '../../../view_models/auth_view_model.dart'; import '../widgets/widgets.dart'; @@ -46,7 +47,6 @@ class _LoginDesktopPageState extends State { @override Widget build(BuildContext context) { - SizeConfig.init(context); return Scaffold( backgroundColor: AppColors.backgroundColor, body: Center( @@ -55,7 +55,7 @@ class _LoginDesktopPageState extends State { kIsWeb ? MainAxisAlignment.spaceAround : MainAxisAlignment.center, children: [ SizedBox( - width: 526, + width: 536, child: Consumer( builder: (context, viewModel, _) { return Column( @@ -108,6 +108,20 @@ class _LoginDesktopPageState extends State { } }, ), + SizedBox(height: 32.0), + TextButton( + onPressed: () { + ForgotPasswordRouteData().push(context); + }, + child: Text( + AppStrings.forgotPassword, + style: TextStyle( + fontSize: 14.0.sp(), + fontWeight: FontWeight.w500, + color: AppColors.mainBlue, + ), + ), + ), ], ); }, diff --git a/lib/views/login/pages/login_mobile_page.dart b/lib/views/login/pages/login_mobile_page.dart index 0981c1d..43c354e 100644 --- a/lib/views/login/pages/login_mobile_page.dart +++ b/lib/views/login/pages/login_mobile_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; import 'package:go_router/go_router.dart'; import 'package:provider/provider.dart'; +import 'package:tm_app/core/routes/app_routes.dart'; import 'package:tm_app/core/theme/colors.dart'; import '../../../core/constants/assets.dart'; @@ -103,6 +104,20 @@ class _LoginMobilePageState extends State { } }, ), + SizedBox(height: 32.0.h()), + TextButton( + onPressed: () { + ForgotPasswordRouteData().push(context); + }, + child: Text( + AppStrings.forgotPassword, + style: TextStyle( + fontSize: 14.0.sp(), + fontWeight: FontWeight.w500, + color: AppColors.mainBlue, + ), + ), + ), ], ); }, diff --git a/lib/views/login/pages/login_tablet_page.dart b/lib/views/login/pages/login_tablet_page.dart index ca37a9d..1047e57 100644 --- a/lib/views/login/pages/login_tablet_page.dart +++ b/lib/views/login/pages/login_tablet_page.dart @@ -5,6 +5,7 @@ import 'package:tm_app/core/theme/colors.dart'; import '../../../core/constants/assets.dart'; import '../../../core/constants/strings.dart'; +import '../../../core/routes/app_routes.dart'; import '../../../core/utils/size_config.dart'; import '../../../view_models/auth_view_model.dart'; import '../widgets/widgets.dart'; @@ -45,7 +46,6 @@ class _LoginTabletPageState extends State { @override Widget build(BuildContext context) { - SizeConfig.init(context); return Scaffold( backgroundColor: AppColors.backgroundColor, body: Center( @@ -101,6 +101,20 @@ class _LoginTabletPageState extends State { } : null, ), + SizedBox(height: 32.0), + TextButton( + onPressed: () { + ForgotPasswordRouteData().push(context); + }, + child: Text( + AppStrings.forgotPassword, + style: TextStyle( + fontSize: 14.0.sp(), + fontWeight: FontWeight.w500, + color: AppColors.mainBlue, + ), + ), + ), ], ); }, diff --git a/lib/views/login/widgets/login_textfield.dart b/lib/views/login/widgets/login_textfield.dart index d0dd050..b2351a1 100644 --- a/lib/views/login/widgets/login_textfield.dart +++ b/lib/views/login/widgets/login_textfield.dart @@ -9,7 +9,7 @@ class LoginTextField extends StatelessWidget { final TextEditingController controller; final FocusNode focusNode; final String label; - final String iconPath; + final String? iconPath; final bool isPassword; final bool obscureText; final double? borderRedus; @@ -21,7 +21,7 @@ class LoginTextField extends StatelessWidget { required this.controller, required this.focusNode, required this.label, - required this.iconPath, + this.iconPath, super.key, this.isPassword = false, this.obscureText = false, @@ -45,18 +45,23 @@ class LoginTextField extends StatelessWidget { horizontal: 12.0.w(), ), labelText: label, - prefixIcon: Padding( - padding: EdgeInsets.all(prefixIconPadding ?? 12.0.w()), - child: SvgPicture.asset( - iconPath, - width: 24.0.w(), - height: 24.0.h(), - colorFilter: ColorFilter.mode( - focusNode.hasFocus ? AppColors.grey80 : AppColors.grey60, - BlendMode.srcIn, - ), - ), - ), + prefixIcon: + iconPath != null + ? Padding( + padding: EdgeInsets.all(prefixIconPadding ?? 12.0.w()), + child: SvgPicture.asset( + iconPath!, + width: 24.0.w(), + height: 24.0.h(), + colorFilter: ColorFilter.mode( + focusNode.hasFocus + ? AppColors.grey80 + : AppColors.grey60, + BlendMode.srcIn, + ), + ), + ) + : null, suffixIcon: isPassword ? IconButton( diff --git a/lib/views/profile/pages/d_profile_page.dart b/lib/views/profile/pages/d_profile_page.dart index a43eff8..1db81c4 100644 --- a/lib/views/profile/pages/d_profile_page.dart +++ b/lib/views/profile/pages/d_profile_page.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; -import 'package:go_router/go_router.dart'; import 'package:provider/provider.dart'; import 'package:tm_app/core/routes/app_routes.dart'; import 'package:tm_app/view_models/profile_view_model.dart'; @@ -39,7 +38,7 @@ class _DesktopProfilePageState extends State { ); } if (viewModel.loggedOut) { - LoginScreenRoute().go(context); + Router.neglect(context, () => LoginScreenRoute().go(context)); } } @@ -77,11 +76,17 @@ class _DesktopProfilePageState extends State { switch (index) { case 0: // Navigate to home - Router.neglect(context, () => context.go('/home')); + Router.neglect( + context, + () => HomeRouteData().go(context), + ); break; case 1: // Navigate to profile - Router.neglect(context, () => context.go('/tenders')); + Router.neglect( + context, + () => TendersRouteData().go(context), + ); break; case 2: // Already on profile @@ -163,6 +168,7 @@ class _DesktopProfilePageState extends State { alignment: Alignment.centerRight, child: InkWell( onTap: () => viewModel.logout(), + borderRadius: BorderRadius.circular(100), child: Container( width: 156, height: 56.0.h(), diff --git a/lib/views/profile/pages/m_profile_page.dart b/lib/views/profile/pages/m_profile_page.dart index 9da448c..5710bd1 100644 --- a/lib/views/profile/pages/m_profile_page.dart +++ b/lib/views/profile/pages/m_profile_page.dart @@ -38,7 +38,7 @@ class _MobileProfilePageState extends State { ); } if (viewModel.loggedOut) { - LoginScreenRoute().go(context); + Router.neglect(context, () => LoginScreenRoute().go(context)); } } @@ -143,6 +143,7 @@ class _MobileProfilePageState extends State { Spacer(), InkWell( onTap: () => viewModel.logout(), + borderRadius: BorderRadius.circular(100), child: Container( width: double.infinity, height: 56.0.h(), diff --git a/lib/views/profile/pages/t_profile_page.dart b/lib/views/profile/pages/t_profile_page.dart index 6ef28e5..ef544c6 100644 --- a/lib/views/profile/pages/t_profile_page.dart +++ b/lib/views/profile/pages/t_profile_page.dart @@ -5,7 +5,6 @@ import 'package:provider/provider.dart'; import 'package:tm_app/core/constants/assets.dart'; import 'package:tm_app/core/constants/strings.dart'; import 'package:tm_app/core/routes/app_routes.dart'; -import 'package:tm_app/view_models/home_view_model.dart'; import 'package:tm_app/view_models/profile_view_model.dart'; import '../../../core/theme/colors.dart'; @@ -40,7 +39,7 @@ class _TabletProfilePageState extends State { ); } if (viewModel.loggedOut) { - LoginScreenRoute().go(context); + Router.neglect(context, () => LoginScreenRoute().go(context)); } } @@ -103,12 +102,15 @@ class _TabletProfilePageState extends State { switch (index) { case 0: context.pop(); - Router.neglect(context, () => context.go('/home')); - context.read().init(); + Router.neglect(context, () => HomeRouteData().go(context)); + break; case 1: context.pop(); - Router.neglect(context, () => context.go('/tenders')); + Router.neglect( + context, + () => TendersRouteData().go(context), + ); break; } }, @@ -190,6 +192,7 @@ class _TabletProfilePageState extends State { alignment: Alignment.centerRight, child: InkWell( onTap: () => viewModel.logout(), + borderRadius: BorderRadius.circular(100), child: Container( width: 156, height: 56.0.h(), diff --git a/lib/views/tenders/pages/d_tenders_page.dart b/lib/views/tenders/pages/d_tenders_page.dart index 25b071e..5fcd1fa 100644 --- a/lib/views/tenders/pages/d_tenders_page.dart +++ b/lib/views/tenders/pages/d_tenders_page.dart @@ -1,14 +1,13 @@ import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; import 'package:provider/provider.dart'; import 'package:tm_app/core/theme/colors.dart'; import 'package:tm_app/core/utils/size_config.dart'; -import 'package:tm_app/view_models/home_view_model.dart'; import 'package:tm_app/view_models/tenders_view_model.dart'; import 'package:tm_app/views/shared/desktop_navigation_widget.dart'; import 'package:tm_app/views/tenders/widgets/main_tenders_slider.dart'; import '../../../core/constants/strings.dart'; +import '../../../core/routes/app_routes.dart'; class DesktopTendersPage extends StatefulWidget { const DesktopTendersPage({super.key}); @@ -64,16 +63,15 @@ class _DesktopTendersPageState extends State { switch (index) { case 0: // Navigate to home - Router.neglect(context, () => context.go('/home')); - context.read().init(); + Router.neglect(context, () => HomeRouteData().go(context)); + viewModel.updateHome(); break; case 1: // Already on tenders break; case 2: // Navigate to profile - // context.go('/profile'); - Router.neglect(context, () => context.go('/profile')); + Router.neglect(context, () => ProfileRouteData().go(context)); break; } }, diff --git a/lib/views/tenders/pages/t_tenders_page.dart b/lib/views/tenders/pages/t_tenders_page.dart index bcf2735..60e008e 100644 --- a/lib/views/tenders/pages/t_tenders_page.dart +++ b/lib/views/tenders/pages/t_tenders_page.dart @@ -4,17 +4,30 @@ import 'package:go_router/go_router.dart'; import 'package:provider/provider.dart'; import 'package:tm_app/core/theme/colors.dart'; import 'package:tm_app/core/utils/size_config.dart'; -import 'package:tm_app/view_models/home_view_model.dart'; import 'package:tm_app/view_models/tenders_view_model.dart'; import 'package:tm_app/views/shared/tablet_navigation_widget.dart'; import 'package:tm_app/views/tenders/widgets/main_tenders_slider.dart'; import '../../../core/constants/assets.dart'; import '../../../core/constants/strings.dart'; +import '../../../core/routes/app_routes.dart'; -class TabletTendersPage extends StatelessWidget { +class TabletTendersPage extends StatefulWidget { const TabletTendersPage({super.key}); + @override + State createState() => _TabletTendersPageState(); +} + +class _TabletTendersPageState extends State { + late final TendersViewModel viewModel; + + @override + void initState() { + super.initState(); + viewModel = context.read(); + } + @override Widget build(BuildContext context) { final GlobalKey key = GlobalKey(); @@ -52,12 +65,12 @@ class TabletTendersPage extends StatelessWidget { switch (index) { case 0: context.pop(); - Router.neglect(context, () => context.go('/home')); - context.read().init(); + Router.neglect(context, () => HomeRouteData().go(context)); + viewModel.updateHome(); break; case 2: context.pop(); - Router.neglect(context, () => context.go('/profile')); + Router.neglect(context, () => ProfileRouteData().go(context)); break; }