From 8b698a8498e77ecd7bfdd6bb3e089a2da0e91524 Mon Sep 17 00:00:00 2001 From: "mr.mojtaba" Date: Sat, 2 Aug 2025 15:09:06 +0330 Subject: [PATCH] feat : new auth in chicken --- .../service/app_navigation_observer.dart | 7 +- lib/presentation/pages/modules/logic.dart | 2 +- lib/presentation/pages/modules/view.dart | 4 +- .../local/chicken_local.dart | 0 .../local/chicken_local_imp.dart | 3 +- .../data_source/remote/auth/auth_remote.dart | 13 + .../remote/auth/auth_remote_imp.dart | 50 ++ .../remote/chicken}/chicken_remote.dart | 0 .../remote/chicken}/chicken_remote_imp.dart | 4 +- packages/chicken/lib/data/di/chicken_di.dart | 79 ++- .../repositories/auth/auth_repository.dart | 12 + .../auth/auth_repository_imp.dart | 25 + .../{ => chicken}/chicken_repository.dart | 2 +- .../{ => chicken}/chicken_repository_imp.dart | 4 +- .../lib/presentation/pages/auth/logic.dart | 165 ++++++ .../lib/presentation/pages/auth/view.dart | 543 ++++++++++++++++++ .../lib/presentation/pages/root/logic.dart | 7 +- .../lib/presentation/routes/pages.dart | 12 + .../presentation/widget/captcha/logic.dart | 34 ++ .../lib/presentation/widget/captcha/view.dart | 111 ++++ .../presentation/widget/buttons/buttons.dart | 3 +- .../widget/buttons}/clear_button.dart | 0 .../lib/presentation/widget/logo_widget.dart | 0 .../core/lib/presentation/widget/widget.dart | 17 +- .../lib/presentation/pages/auth/view.dart | 95 +-- .../lib/presentation/routes/app_pages.dart | 18 +- .../lib/presentation/widget/captcha/view.dart | 14 +- 27 files changed, 1115 insertions(+), 109 deletions(-) rename packages/chicken/lib/data/{datasource => data_source}/local/chicken_local.dart (100%) rename packages/chicken/lib/data/{datasource => data_source}/local/chicken_local_imp.dart (95%) create mode 100644 packages/chicken/lib/data/data_source/remote/auth/auth_remote.dart create mode 100644 packages/chicken/lib/data/data_source/remote/auth/auth_remote_imp.dart rename packages/chicken/lib/data/{datasource/remote => data_source/remote/chicken}/chicken_remote.dart (100%) rename packages/chicken/lib/data/{datasource/remote => data_source/remote/chicken}/chicken_remote_imp.dart (99%) create mode 100644 packages/chicken/lib/data/repositories/auth/auth_repository.dart create mode 100644 packages/chicken/lib/data/repositories/auth/auth_repository_imp.dart rename packages/chicken/lib/data/repositories/{ => chicken}/chicken_repository.dart (98%) rename packages/chicken/lib/data/repositories/{ => chicken}/chicken_repository_imp.dart (98%) create mode 100644 packages/chicken/lib/presentation/pages/auth/logic.dart create mode 100644 packages/chicken/lib/presentation/pages/auth/view.dart create mode 100644 packages/chicken/lib/presentation/widget/captcha/logic.dart create mode 100644 packages/chicken/lib/presentation/widget/captcha/view.dart rename packages/{inspection/lib/presentation/widget => core/lib/presentation/widget/buttons}/clear_button.dart (100%) rename packages/{inspection => core}/lib/presentation/widget/logo_widget.dart (100%) diff --git a/lib/infrastructure/service/app_navigation_observer.dart b/lib/infrastructure/service/app_navigation_observer.dart index 3cebc64..6438add 100644 --- a/lib/infrastructure/service/app_navigation_observer.dart +++ b/lib/infrastructure/service/app_navigation_observer.dart @@ -16,10 +16,11 @@ class CustomNavigationObserver extends NavigatorObserver { void didPush(Route route, Route? previousRoute) async { super.didPush(route, previousRoute); final routeName = route.settings.name; - if (!_isWorkDone && routeName == ChickenRoutes.init) { + if (!_isWorkDone &&( routeName == ChickenRoutes.init || routeName == ChickenRoutes.auth)) { _isWorkDone = true; - setupChickenDI(); - } else if (!_isWorkDone && (routeName == InspectionRoutes.init || routeName == InspectionRoutes.auth)) { + await setupChickenDI(); + } else if (!_isWorkDone && + (routeName == InspectionRoutes.init || routeName == InspectionRoutes.auth)) { _isWorkDone = true; await setupInspectionDI(); diff --git a/lib/presentation/pages/modules/logic.dart b/lib/presentation/pages/modules/logic.dart index d35b096..e73eefe 100644 --- a/lib/presentation/pages/modules/logic.dart +++ b/lib/presentation/pages/modules/logic.dart @@ -5,7 +5,7 @@ class ModulesLogic extends GetxController { List moduleList = [ ModuleModel(title: 'بازرسی', icon: Assets.icons.inspection.path, module: Module.inspection), - ModuleModel(title: 'دام', icon: Assets.icons.liveStock.path, module: Module.liveStocks), +// ModuleModel(title: 'دام', icon: Assets.icons.liveStock.path, module: Module.liveStocks), ModuleModel(title: 'مرغ', icon: Assets.icons.liveStock.path, module: Module.chicken), ]; diff --git a/lib/presentation/pages/modules/view.dart b/lib/presentation/pages/modules/view.dart index b1d1bbf..880050a 100644 --- a/lib/presentation/pages/modules/view.dart +++ b/lib/presentation/pages/modules/view.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:rasadyar_chicken/chicken.dart'; import 'package:rasadyar_core/core.dart'; import 'package:rasadyar_inspection/inspection.dart'; @@ -33,8 +34,9 @@ class ModulesPage extends GetView { Get.toNamed(InspectionRoutes.init); break; case Module.liveStocks: + //TODO: Implement liveStocks module navigation case Module.chicken: - Get.toNamed(InspectionRoutes.init); + Get.toNamed(ChickenRoutes.init); break; } }, diff --git a/packages/chicken/lib/data/datasource/local/chicken_local.dart b/packages/chicken/lib/data/data_source/local/chicken_local.dart similarity index 100% rename from packages/chicken/lib/data/datasource/local/chicken_local.dart rename to packages/chicken/lib/data/data_source/local/chicken_local.dart diff --git a/packages/chicken/lib/data/datasource/local/chicken_local_imp.dart b/packages/chicken/lib/data/data_source/local/chicken_local_imp.dart similarity index 95% rename from packages/chicken/lib/data/datasource/local/chicken_local_imp.dart rename to packages/chicken/lib/data/data_source/local/chicken_local_imp.dart index 663af1f..a8f2055 100644 --- a/packages/chicken/lib/data/datasource/local/chicken_local_imp.dart +++ b/packages/chicken/lib/data/data_source/local/chicken_local_imp.dart @@ -1,8 +1,9 @@ import 'package:rasadyar_chicken/chicken.dart'; -import 'package:rasadyar_chicken/data/datasource/local/chicken_local.dart'; import 'package:rasadyar_chicken/data/models/local/widely_used_local_model.dart'; import 'package:rasadyar_core/core.dart'; +import 'chicken_local.dart'; + class ChickenLocalDataSourceImp implements ChickenLocalDataSource { HiveLocalStorage local = diCore.get(); final String boxName = 'Chicken_Widley_Box'; diff --git a/packages/chicken/lib/data/data_source/remote/auth/auth_remote.dart b/packages/chicken/lib/data/data_source/remote/auth/auth_remote.dart new file mode 100644 index 0000000..1705dcf --- /dev/null +++ b/packages/chicken/lib/data/data_source/remote/auth/auth_remote.dart @@ -0,0 +1,13 @@ +import 'package:rasadyar_chicken/data/models/response/captcha/captcha_response_model.dart'; +import 'package:rasadyar_chicken/data/models/response/user_info/user_info_model.dart'; +import 'package:rasadyar_chicken/data/models/response/user_profile_model/user_profile_model.dart'; + +abstract class AuthRemoteDataSource { + Future login({required Map authRequest}); + + Future logout(); + + Future hasAuthenticated(); + + Future getUserInfo(String phoneNumber); +} diff --git a/packages/chicken/lib/data/data_source/remote/auth/auth_remote_imp.dart b/packages/chicken/lib/data/data_source/remote/auth/auth_remote_imp.dart new file mode 100644 index 0000000..f25e8c2 --- /dev/null +++ b/packages/chicken/lib/data/data_source/remote/auth/auth_remote_imp.dart @@ -0,0 +1,50 @@ +import 'package:rasadyar_chicken/data/models/response/user_info/user_info_model.dart'; +import 'package:rasadyar_chicken/data/models/response/user_profile_model/user_profile_model.dart'; +import 'package:rasadyar_core/core.dart'; + +import 'auth_remote.dart'; + +class AuthRemoteDataSourceImp extends AuthRemoteDataSource { + final DioRemote _httpClient; + final String _BASE_URL = 'auth/api/v1/'; + + AuthRemoteDataSourceImp(this._httpClient); + + @override + Future login({required Map authRequest}) async { + var res = await _httpClient.post( + '/api/login/', + data: authRequest, + fromJson: UserProfileModel.fromJson, + headers: {'Content-Type': 'application/json'}, + ); + return res.data; + } + + @override + Future logout() { + // TODO: implement logout + throw UnimplementedError(); + } + + @override + Future hasAuthenticated() async { + final response = await _httpClient.get( + '$_BASE_URL/login/', + headers: {'Content-Type': 'application/json'}, + ); + + return response.data ?? false; + } + + @override + Future getUserInfo(String phoneNumber) async { + var res = await _httpClient.post( + 'https://userbackend.rasadyaar.ir/api/send_otp/', + data: {"mobile": phoneNumber, "state": ""}, + fromJson: UserInfoModel.fromJson, + headers: {'Content-Type': 'application/json'}, + ); + return res.data; + } +} diff --git a/packages/chicken/lib/data/datasource/remote/chicken_remote.dart b/packages/chicken/lib/data/data_source/remote/chicken/chicken_remote.dart similarity index 100% rename from packages/chicken/lib/data/datasource/remote/chicken_remote.dart rename to packages/chicken/lib/data/data_source/remote/chicken/chicken_remote.dart diff --git a/packages/chicken/lib/data/datasource/remote/chicken_remote_imp.dart b/packages/chicken/lib/data/data_source/remote/chicken/chicken_remote_imp.dart similarity index 99% rename from packages/chicken/lib/data/datasource/remote/chicken_remote_imp.dart rename to packages/chicken/lib/data/data_source/remote/chicken/chicken_remote_imp.dart index 700f669..57df1fa 100644 --- a/packages/chicken/lib/data/datasource/remote/chicken_remote_imp.dart +++ b/packages/chicken/lib/data/data_source/remote/chicken/chicken_remote_imp.dart @@ -1,4 +1,4 @@ -import 'package:rasadyar_chicken/data/datasource/remote/chicken_remote.dart'; + import 'package:rasadyar_chicken/data/models/request/change_password/change_password_request_model.dart'; import 'package:rasadyar_chicken/data/models/request/conform_allocation/conform_allocation.dart'; import 'package:rasadyar_chicken/data/models/request/create_steward_free_bar/create_steward_free_bar.dart'; @@ -24,6 +24,8 @@ import 'package:rasadyar_chicken/data/models/response/waiting_arrival/waiting_ar hide ProductModel; import 'package:rasadyar_core/core.dart'; +import 'chicken_remote.dart'; + class ChickenRemoteDatasourceImp implements ChickenRemoteDatasource { final DioRemote _httpClient; diff --git a/packages/chicken/lib/data/di/chicken_di.dart b/packages/chicken/lib/data/di/chicken_di.dart index 45bf9ef..d85f410 100644 --- a/packages/chicken/lib/data/di/chicken_di.dart +++ b/packages/chicken/lib/data/di/chicken_di.dart @@ -1,17 +1,15 @@ -import 'package:rasadyar_chicken/data/datasource/local/chicken_local_imp.dart'; -import 'package:rasadyar_chicken/data/datasource/remote/chicken_remote_imp.dart'; -import 'package:rasadyar_chicken/data/repositories/chicken_repository_imp.dart'; -import 'package:rasadyar_chicken/hive_registrar.g.dart'; -import 'package:rasadyar_chicken/presentation/routes/routes.dart'; +import 'package:rasadyar_chicken/chicken.dart'; +import 'package:rasadyar_chicken/data/common/dio_error_handler.dart'; +import 'package:rasadyar_chicken/data/data_source/remote/auth/auth_remote.dart'; +import 'package:rasadyar_chicken/data/data_source/remote/auth/auth_remote_imp.dart'; +import 'package:rasadyar_chicken/data/repositories/auth/auth_repository_imp.dart'; import 'package:rasadyar_core/core.dart'; GetIt diChicken = GetIt.instance; Future setupChickenDI() async { + diChicken.registerSingleton(DioErrorHandler()); var tokenService = Get.find(); - Hive.registerAdapters(); - diChicken.registerLazySingleton(() => ChickenLocalDataSourceImp()); - diChicken.get().openBox(); diChicken.registerLazySingleton( () => AppInterceptor( @@ -24,32 +22,53 @@ Future setupChickenDI() async { await tokenService.deleteTokens(); Get.offAllNamed(ChickenRoutes.auth, arguments: Module.chicken); }, - authArguments: Module.chicken, ), instanceName: 'chickenInterceptor', ); - tokenService.getBaseUrl(); - - diChicken.registerLazySingleton(() { - return DioRemote( - baseUrl: tokenService.baseurl.value, - interceptors: diChicken.get(instanceName: 'chickenInterceptor'), - ); - }, instanceName: 'chickenDioRemote'); - - final dioRemote = diChicken.get(instanceName: 'chickenDioRemote'); - - await dioRemote.init(); - - diChicken.registerLazySingleton(() => ChickenRemoteDatasourceImp(dioRemote)); - - diChicken.registerLazySingleton( - () => ChickenRepositoryImp( - local: diChicken.get(), - remote: diChicken.get(), - ), + diChicken.registerLazySingleton( + () => + DioRemote(interceptors: diChicken.get(instanceName: 'chickenInterceptor')), ); - diChicken.registerSingleton(ImagePicker()); + final dioRemote = diChicken.get(); + await dioRemote.init(); + + diChicken.registerLazySingleton( + () => AuthRemoteDataSourceImp(diChicken.get()), + ); + + diChicken.registerLazySingleton( + () => AuthRepositoryImpl(diChicken.get()), + ); +} + +Future newSetupAuthDI(String newUrl) async { + var tokenService = Get.find(); + if (tokenService.baseurl.value == null) { + await tokenService.saveBaseUrl(newUrl); + } + + if (diChicken.isRegistered()) { + await diChicken.unregister(); + diChicken.registerLazySingleton( + () => DioRemote(baseUrl: newUrl, interceptors: diChicken.get()), + ); + final dioRemote = diChicken.get(); + await dioRemote.init(); + } + + if (diChicken.isRegistered()) { + await diChicken.unregister(); + diChicken.registerLazySingleton( + () => AuthRemoteDataSourceImp(diChicken.get()), + ); + } + + if (diChicken.isRegistered()) { + await diChicken.unregister(); + diChicken.registerLazySingleton( + () => AuthRepositoryImpl(diChicken.get()), + ); + } } diff --git a/packages/chicken/lib/data/repositories/auth/auth_repository.dart b/packages/chicken/lib/data/repositories/auth/auth_repository.dart new file mode 100644 index 0000000..c7914c5 --- /dev/null +++ b/packages/chicken/lib/data/repositories/auth/auth_repository.dart @@ -0,0 +1,12 @@ +import 'package:rasadyar_chicken/data/models/response/user_info/user_info_model.dart'; +import 'package:rasadyar_chicken/data/models/response/user_profile_model/user_profile_model.dart'; + +abstract class AuthRepository { + Future login({required Map authRequest}); + + Future logout(); + + Future hasAuthenticated(); + + Future getUserInfo(String phoneNumber); +} diff --git a/packages/chicken/lib/data/repositories/auth/auth_repository_imp.dart b/packages/chicken/lib/data/repositories/auth/auth_repository_imp.dart new file mode 100644 index 0000000..1b84d61 --- /dev/null +++ b/packages/chicken/lib/data/repositories/auth/auth_repository_imp.dart @@ -0,0 +1,25 @@ +import 'package:rasadyar_chicken/data/data_source/remote/auth/auth_remote.dart'; +import 'package:rasadyar_chicken/data/models/response/user_info/user_info_model.dart'; +import 'package:rasadyar_chicken/data/models/response/user_profile_model/user_profile_model.dart'; + +import 'auth_repository.dart'; + +class AuthRepositoryImpl implements AuthRepository { + final AuthRemoteDataSource authRemote; + + AuthRepositoryImpl(this.authRemote); + + @override + Future login({required Map authRequest}) async => + await authRemote.login(authRequest: authRequest); + + @override + Future logout() async => await authRemote.logout(); + + @override + Future hasAuthenticated() async => await authRemote.hasAuthenticated(); + + @override + Future getUserInfo(String phoneNumber) async => + await authRemote.getUserInfo(phoneNumber); +} diff --git a/packages/chicken/lib/data/repositories/chicken_repository.dart b/packages/chicken/lib/data/repositories/chicken/chicken_repository.dart similarity index 98% rename from packages/chicken/lib/data/repositories/chicken_repository.dart rename to packages/chicken/lib/data/repositories/chicken/chicken_repository.dart index 1d0aee9..62b4404 100644 --- a/packages/chicken/lib/data/repositories/chicken_repository.dart +++ b/packages/chicken/lib/data/repositories/chicken/chicken_repository.dart @@ -23,7 +23,7 @@ import 'package:rasadyar_chicken/data/models/response/waiting_arrival/waiting_ar hide ProductModel; import 'package:rasadyar_core/core.dart'; -import '../models/request/create_steward_free_bar/create_steward_free_bar.dart'; +import '../../models/request/create_steward_free_bar/create_steward_free_bar.dart'; abstract class ChickenRepository { //region Remote diff --git a/packages/chicken/lib/data/repositories/chicken_repository_imp.dart b/packages/chicken/lib/data/repositories/chicken/chicken_repository_imp.dart similarity index 98% rename from packages/chicken/lib/data/repositories/chicken_repository_imp.dart rename to packages/chicken/lib/data/repositories/chicken/chicken_repository_imp.dart index 6eaf786..d9d829e 100644 --- a/packages/chicken/lib/data/repositories/chicken_repository_imp.dart +++ b/packages/chicken/lib/data/repositories/chicken/chicken_repository_imp.dart @@ -1,5 +1,5 @@ -import 'package:rasadyar_chicken/data/datasource/local/chicken_local_imp.dart'; -import 'package:rasadyar_chicken/data/datasource/remote/chicken_remote_imp.dart'; +import 'package:rasadyar_chicken/data/data_source/local/chicken_local_imp.dart'; +import 'package:rasadyar_chicken/data/data_source/remote/chicken/chicken_remote_imp.dart'; import 'package:rasadyar_chicken/data/models/local/widely_used_local_model.dart'; import 'package:rasadyar_chicken/data/models/request/change_password/change_password_request_model.dart'; import 'package:rasadyar_chicken/data/models/request/conform_allocation/conform_allocation.dart'; diff --git a/packages/chicken/lib/presentation/pages/auth/logic.dart b/packages/chicken/lib/presentation/pages/auth/logic.dart new file mode 100644 index 0000000..bec91ab --- /dev/null +++ b/packages/chicken/lib/presentation/pages/auth/logic.dart @@ -0,0 +1,165 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:rasadyar_chicken/data/common/dio_error_handler.dart'; +import 'package:rasadyar_chicken/data/di/chicken_di.dart'; +import 'package:rasadyar_chicken/data/models/request/login_request/login_request_model.dart'; +import 'package:rasadyar_chicken/data/models/response/user_info/user_info_model.dart'; +import 'package:rasadyar_chicken/data/models/response/user_profile_model/user_profile_model.dart'; +import 'package:rasadyar_chicken/data/repositories/auth/auth_repository_imp.dart'; +import 'package:rasadyar_chicken/presentation/widget/captcha/logic.dart'; +import 'package:rasadyar_core/core.dart'; + +enum AuthType { useAndPass, otp } + +enum AuthStatus { init } + +enum OtpStatus { init, sent, verified, reSend } + +class AuthLogic extends GetxController with GetTickerProviderStateMixin { + GlobalKey formKey = GlobalKey(); + + late AnimationController _textAnimationController; + late Animation textAnimation; + RxBool showCard = false.obs; + + Rx> formKeyOtp = GlobalKey().obs; + Rx> formKeySentOtp = GlobalKey().obs; + Rx usernameController = TextEditingController().obs; + Rx passwordController = TextEditingController().obs; + Rx phoneOtpNumberController = TextEditingController().obs; + Rx otpCodeController = TextEditingController().obs; + + var captchaController = Get.find(); + + RxnString phoneNumber = RxnString(null); + RxBool isLoading = false.obs; + RxBool isDisabled = true.obs; + TokenStorageService tokenStorageService = Get.find(); + + Rx authType = AuthType.useAndPass.obs; + Rx authStatus = AuthStatus.init.obs; + Rx otpStatus = OtpStatus.init.obs; + + RxInt secondsRemaining = 120.obs; + Timer? _timer; + + AuthRepositoryImpl authRepository = diChicken.get(); + + final Module _module = Get.arguments; + + @override + void onInit() { + super.onInit(); + + _textAnimationController = + AnimationController(vsync: this, duration: const Duration(milliseconds: 1200)) + ..repeat(reverse: true, count: 2).whenComplete(() { + showCard.value = true; + }); + + textAnimation = CurvedAnimation(parent: _textAnimationController, curve: Curves.easeInOut); + } + + @override + void onReady() { + super.onReady(); + + } + + @override + void onClose() { + _timer?.cancel(); + super.onClose(); + } + + void startTimer() { + _timer?.cancel(); + secondsRemaining.value = 120; + + _timer = Timer.periodic(const Duration(seconds: 1), (timer) { + if (secondsRemaining.value > 0) { + secondsRemaining.value--; + } else { + timer.cancel(); + } + }); + } + + void stopTimer() { + _timer?.cancel(); + } + + String get timeFormatted { + final minutes = secondsRemaining.value ~/ 60; + final seconds = secondsRemaining.value % 60; + return '${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}'; + } + + bool _isFormValid() { + final isCaptchaValid = captchaController.formKey.currentState?.validate() ?? false; + final isFormValid = formKey.currentState?.validate() ?? false; + return isCaptchaValid && isFormValid; + } + + LoginRequestModel _buildLoginRequest() { + final phone = usernameController.value.text; + final pass = passwordController.value.text; + final code = captchaController.textController.value.text; + final key = captchaController.captchaKey.value; + + return LoginRequestModel.createWithCaptcha( + username: phone, + password: pass, + captchaCode: code, + captchaKey: key!, + ); + } + + Future submitLoginForm() async { + if (!_isFormValid()) return; + AuthRepositoryImpl authTmp = diChicken.get(instanceName: 'newUrl'); + isLoading.value = true; + await safeCall( + call: () => authTmp.login( + authRequest: { + "username": usernameController.value.text, + "password": passwordController.value.text, + }, + ), + onSuccess: (result) async { + await tokenStorageService.saveModule(_module); + await tokenStorageService.saveAccessToken(result?.accessToken ?? ''); + await tokenStorageService.saveRefreshToken(result?.accessToken ?? ''); + }, + onError: (error, stackTrace) { + if (error is DioException) { + diChicken.get().handle(error); + } + captchaController.getCaptcha(); + }, + ); + isLoading.value = false; + } + + Future getUserInfo(String value) async { + isLoading.value = true; + await safeCall( + call: () async => await authRepository.getUserInfo(value), + onSuccess: (result) async { + if (result != null) { + await newSetupAuthDI(result.backend ?? ''); + await tokenStorageService.saveApiKey(result.apiKey ?? ''); + await tokenStorageService.saveBaseUrl(result.backend ?? ''); + } + }, + onError: (error, stackTrace) { + if (error is DioException) { + diChicken.get().handle(error); + } + captchaController.getCaptcha(); + }, + ); + isLoading.value = false; + } +} diff --git a/packages/chicken/lib/presentation/pages/auth/view.dart b/packages/chicken/lib/presentation/pages/auth/view.dart new file mode 100644 index 0000000..1700994 --- /dev/null +++ b/packages/chicken/lib/presentation/pages/auth/view.dart @@ -0,0 +1,543 @@ +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:rasadyar_chicken/presentation/widget/captcha/view.dart'; +import 'package:rasadyar_core/core.dart'; + +import 'logic.dart'; + +class AuthPage extends GetView { + const AuthPage({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Stack( + alignment: Alignment.center, + + children: [ + Assets.vec.bgAuthSvg.svg(fit: BoxFit.fill), + + Padding( + padding: EdgeInsets.symmetric(horizontal: 10.r), + child: FadeTransition( + opacity: controller.textAnimation, + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + spacing: 12, + children: [ + Text( + 'به سامانه رصدیار خوش آمدید!', + textAlign: TextAlign.right, + style: AppFonts.yekan25Bold.copyWith(color: Colors.white), + ), + Text( + 'سامانه رصد و پایش زنجیره تامین، تولید و توزیع کالا های اساسی', + textAlign: TextAlign.center, + style: AppFonts.yekan16.copyWith(color: Colors.white), + ), + ], + ), + ), + ), + + Obx(() { + final screenHeight = MediaQuery.of(context).size.height; + final targetTop = (screenHeight - 676) / 2; + + return AnimatedPositioned( + duration: const Duration(milliseconds: 1200), + curve: Curves.linear, + top: controller.showCard.value ? targetTop : screenHeight, + left: 10.r, + right: 10.r, + child: Container( + width: 381.w, + height: 676.h, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(40), + ), + child: Column( + children: [ + SizedBox(height: 50.h), + LogoWidget(), + SizedBox(height: 20.h), + useAndPassFrom(), + SizedBox(height: 24.h), + RichText( + text: TextSpan( + children: [ + TextSpan( + text: 'مطالعه بیانیه ', + style: AppFonts.yekan16.copyWith(color: AppColor.darkGreyDark), + ), + TextSpan( + recognizer: TapGestureRecognizer() + ..onTap = () { + Get.bottomSheet( + privacyPolicyWidget(), + isScrollControlled: true, + enableDrag: true, + ignoreSafeArea: false, + ); + }, + text: 'حریم خصوصی', + style: AppFonts.yekan16.copyWith(color: AppColor.blueNormal), + ), + ], + ), + ), + ], + ), + ), + ); + }), + ], + ), + ); + } + + Widget useAndPassFrom() { + return Padding( + padding: EdgeInsets.symmetric(horizontal: 30.r), + child: Form( + key: controller.formKey, + child: AutofillGroup( + child: Column( + children: [ + RTextField( + label: 'نام کاربری', + maxLength: 11, + maxLines: 1, + controller: controller.usernameController.value, + keyboardType: TextInputType.number, + initText: controller.usernameController.value.text, + autofillHints: [AutofillHints.username], + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide(color: AppColor.textColor, width: 1), + ), + onChanged: (value) async { + controller.usernameController.value.text = value; + if (value.length == 11) { + await controller.getUserInfo(value); + } + }, + prefixIcon: Padding( + padding: const EdgeInsets.fromLTRB(0, 8, 6, 8), + child: Assets.vec.callSvg.svg(width: 12, height: 12), + ), + suffixIcon: controller.usernameController.value.text.trim().isNotEmpty + ? clearButton(() { + controller.usernameController.value.clear(); + controller.usernameController.refresh(); + }) + : null, + validator: (value) { + if (value == null || value.isEmpty) { + return '⚠️ شماره موبایل را وارد کنید'; + } else if (value.length < 10) { + return '⚠️ شماره موبایل باید 11 رقم باشد'; + } + return null; + }, + style: AppFonts.yekan13, + errorStyle: AppFonts.yekan13.copyWith(color: AppColor.redNormal), + labelStyle: AppFonts.yekan13, + boxConstraints: const BoxConstraints( + maxHeight: 40, + minHeight: 40, + maxWidth: 40, + minWidth: 40, + ), + ), + const SizedBox(height: 26), + ObxValue( + (passwordController) => RTextField( + label: 'رمز عبور', + filled: false, + obscure: true, + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide(color: AppColor.textColor, width: 1), + ), + controller: passwordController.value, + autofillHints: [AutofillHints.password], + variant: RTextFieldVariant.password, + initText: passwordController.value.text, + onChanged: (value) { + passwordController.refresh(); + }, + validator: (value) { + if (value == null || value.isEmpty) { + return '⚠️ رمز عبور را وارد کنید'; + } + return null; + }, + style: AppFonts.yekan13, + errorStyle: AppFonts.yekan13.copyWith(color: AppColor.redNormal), + labelStyle: AppFonts.yekan13, + prefixIcon: Padding( + padding: const EdgeInsets.fromLTRB(0, 8, 8, 8), + child: Assets.vec.keySvg.svg(width: 12, height: 12), + ), + boxConstraints: const BoxConstraints( + maxHeight: 34, + minHeight: 34, + maxWidth: 34, + minWidth: 34, + ), + ), + controller.passwordController, + ), + SizedBox(height: 26), + CaptchaWidget(), + SizedBox(height: 23), + + Obx(() { + return RElevated( + text: 'ورود', + isLoading: controller.isLoading.value, + onPressed: controller.isDisabled.value + ? null + : () async { + await controller.submitLoginForm(); + }, + width: Get.width, + height: 48, + ); + }), + ], + ), + ), + ), + ); + } + + Widget privacyPolicyWidget() { + return BaseBottomSheet( + child: Column( + spacing: 5, + children: [ + Container( + padding: EdgeInsets.all(8.w), + + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8), + border: Border.all(color: AppColor.darkGreyLight, width: 1), + ), + child: Column( + spacing: 3, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'بيانيه حريم خصوصی', + style: AppFonts.yekan16Bold.copyWith(color: AppColor.blueNormal), + ), + Text( + 'اطلاعات مربوط به هر شخص، حریم خصوصی وی محسوب می‌شود. حفاظت و حراست از اطلاعات شخصی در سامانه رصد یار، نه تنها موجب حفظ امنیت کاربران می‌شود، بلکه باعث اعتماد بیشتر و مشارکت آنها در فعالیت‌های جاری می‌گردد. هدف از این بیانیه، آگاه ساختن شما درباره ی نوع و نحوه ی استفاده از اطلاعاتی است که در هنگام استفاده از سامانه رصد یار ، از جانب شما دریافت می‌گردد. شرکت هوشمند سازان خود را ملزم به رعایت حریم خصوصی همه شهروندان و کاربران سامانه دانسته و آن دسته از اطلاعات کاربران را که فقط به منظور ارائه خدمات کفایت می‌کند، دریافت کرده و از انتشار آن یا در اختیار قرار دادن آن به دیگران خودداری مینماید.', + style: AppFonts.yekan14.copyWith(color: AppColor.bgDark, height: 1.8), + ), + ], + ), + ), + Container( + padding: EdgeInsets.all(8.w), + + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8), + border: Border.all(color: AppColor.darkGreyLight, width: 1), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + spacing: 4, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + 'چگونگی جمع آوری و استفاده از اطلاعات کاربران', + style: AppFonts.yekan16Bold.copyWith(color: AppColor.blueNormal), + ), + Text( + '''الف: اطلاعاتی که شما خود در اختيار این سامانه قرار می‌دهيد، شامل موارد زيرهستند: +اقلام اطلاعاتی شامل شماره تلفن همراه، تاریخ تولد، کد پستی و کد ملی کاربران را دریافت مینماییم که از این اقلام، صرفا جهت احراز هویت کاربران استفاده خواهد شد. +ب: برخی اطلاعات ديگر که به صورت خودکار از شما دريافت میشود شامل موارد زير می‌باشد: +⦁ دستگاهی که از طریق آن سامانه رصد یار را مشاهده می‌نمایید( تلفن همراه، تبلت، رایانه). +⦁ نام و نسخه سیستم عامل و browser کامپیوتر شما. +⦁ اطلاعات صفحات بازدید شده. +⦁ تعداد بازدیدهای روزانه در درگاه. +⦁ هدف ما از دریافت این اطلاعات استفاده از آنها در تحلیل عملکرد کاربران درگاه می باشد تا بتوانیم در خدمت رسانی بهتر عمل کنیم. +''', + style: AppFonts.yekan14.copyWith(color: AppColor.bgDark, height: 1.8), + ), + ], + ), + ), + Container( + padding: EdgeInsets.all(8.w), + + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8), + border: Border.all(color: AppColor.darkGreyLight, width: 1), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + spacing: 4, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + 'امنیت اطلاعات', + style: AppFonts.yekan16Bold.copyWith(color: AppColor.blueNormal), + ), + Text( + 'متعهدیم که امنیت اطلاعات شما را تضمین نماییم و برای جلوگیری از هر نوع دسترسی غیرمجاز و افشای اطلاعات شما از همه شیوه‌‌های لازم استفاده می‌کنیم تا امنیت اطلاعاتی را که به صورت آنلاین گردآوری می‌کنیم، حفظ شود. لازم به ذکر است در سامانه ما، ممکن است به سایت های دیگری لینک شوید، وقتی که شما از طریق این لینک‌ها از سامانه ما خارج می‌شوید، توجه داشته باشید که ما بر دیگر سایت ها کنترل نداریم و سازمان تعهدی بر حفظ حریم شخصی آنان در سایت مقصد نخواهد داشت و مراجعه کنندگان میبایست به بیانیه حریم شخصی آن سایت ها مراجعه نمایند.', + style: AppFonts.yekan14.copyWith(color: AppColor.bgDark, height: 1.8), + ), + ], + ), + ), + ], + ), + ); + } + + /* + Widget sendCodeForm() { + return ObxValue((data) { + return Form( + key: data.value, + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 30, vertical: 50), + child: Column( + children: [ + SizedBox(height: 26), + ObxValue((phoneController) { + return TextFormField( + controller: phoneController.value, + decoration: InputDecoration( + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + gapPadding: 11, + ), + labelText: 'شماره موبایل', + labelStyle: AppFonts.yekan13, + errorStyle: AppFonts.yekan13.copyWith( + color: AppColor.redNormal, + ), + prefixIconConstraints: BoxConstraints( + maxHeight: 40, + minHeight: 40, + maxWidth: 40, + minWidth: 40, + ), + prefixIcon: Padding( + padding: const EdgeInsets.fromLTRB(0, 8, 6, 8), + child: vecWidget(Assets.vecCallSvg), + ), + suffix: + phoneController.value.text.trim().isNotEmpty + ? clearButton(() { + phoneController.value.clear(); + phoneController.refresh(); + }) + : null, + counterText: '', + ), + keyboardType: TextInputType.numberWithOptions( + decimal: false, + signed: false, + ), + maxLines: 1, + maxLength: 11, + onChanged: (value) { + if (controller.isOnError.value) { + controller.isOnError.value = !controller.isOnError.value; + data.value.currentState?.reset(); + data.refresh(); + phoneController.value.text = value; + } + phoneController.refresh(); + }, + textInputAction: TextInputAction.next, + validator: (value) { + if (value == null) { + return '⚠️ شماره موبایل را وارد کنید'; + } else if (value.length < 11) { + return '⚠️ شماره موبایل باید 11 رقم باشد'; + } + return null; + }, + style: AppFonts.yekan13, + ); + }, controller.phoneOtpNumberController), + + SizedBox(height: 26), + + CaptchaWidget(), + + SizedBox(height: 23), + RElevated( + text: 'ارسال رمز یکبار مصرف', + onPressed: () { + if (data.value.currentState?.validate() == true) { + controller.otpStatus.value = OtpStatus.sent; + controller.startTimer(); + } + }, + width: Get.width, + height: 48, + ), + ], + ), + ), + ); + }, controller.formKeyOtp); + } + + Widget confirmCodeForm() { + return ObxValue((data) { + return Form( + key: data.value, + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 30, vertical: 50), + child: Column( + children: [ + SizedBox(height: 26), + + ObxValue((passwordController) { + return TextFormField( + controller: passwordController.value, + obscureText: controller.hidePassword.value, + decoration: InputDecoration( + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + gapPadding: 11, + ), + labelText: 'رمز عبور', + labelStyle: AppFonts.yekan13, + errorStyle: AppFonts.yekan13.copyWith( + color: AppColor.redNormal, + ), + + prefixIconConstraints: BoxConstraints( + maxHeight: 34, + minHeight: 34, + maxWidth: 34, + minWidth: 34, + ), + prefixIcon: Padding( + padding: const EdgeInsets.fromLTRB(0, 8, 8, 8), + child: vecWidget(Assets.vecKeySvg), + ), + suffix: + passwordController.value.text.trim().isNotEmpty + ? GestureDetector( + onTap: () { + controller.hidePassword.value = + !controller.hidePassword.value; + }, + child: Icon( + controller.hidePassword.value + ? CupertinoIcons.eye + : CupertinoIcons.eye_slash, + ), + ) + : null, + counterText: '', + ), + textInputAction: TextInputAction.done, + keyboardType: TextInputType.visiblePassword, + maxLines: 1, + onChanged: (value) { + if (controller.isOnError.value) { + controller.isOnError.value = !controller.isOnError.value; + data.value.currentState?.reset(); + passwordController.value.text = value; + } + passwordController.refresh(); + }, + validator: (value) { + if (value == null || value.isEmpty) { + return '⚠️ رمز عبور را وارد کنید'; // "Please enter the password" + } + return null; + }, + style: AppFonts.yekan13, + ); + }, controller.passwordController), + + SizedBox(height: 23), + + ObxValue((timer) { + if (timer.value == 0) { + return TextButton( + onPressed: () { + controller.otpStatus.value = OtpStatus.reSend; + controller.startTimer(); + }, + child: Text( + style: AppFonts.yekan13.copyWith( + color: AppColor.blueNormal, + ), + 'ارسال مجدد کد یکبار مصرف', + ), + ); + } else { + return Text( + 'اعتبار رمز ارسال شده ${controller.timeFormatted}', + style: AppFonts.yekan13, + ); + } + }, controller.secondsRemaining), + + RichText( + text: TextSpan( + children: [ + TextSpan( + text: ' کد ارسال شده به شماره ', + style: AppFonts.yekan14.copyWith( + color: AppColor.darkGreyDark, + ), + ), + TextSpan( + text: controller.phoneOtpNumberController.value.text, + style: AppFonts.yekan13Bold.copyWith( + color: AppColor.darkGreyDark, + ), + ), + TextSpan( + recognizer: + TapGestureRecognizer() + ..onTap = () { + controller.otpStatus.value = OtpStatus.init; + }, + text: ' ویرایش', + style: AppFonts.yekan14.copyWith( + color: AppColor.blueNormal, + ), + ), + ], + ), + ), + + SizedBox(height: 23), + RElevated( + text: 'ورود', + onPressed: () { + if (controller.formKeyOtp.value.currentState?.validate() == + true) {} + }, + width: Get.width, + height: 48, + ), + ], + ), + ), + ); + }, controller.formKeySentOtp); + }*/ +} diff --git a/packages/chicken/lib/presentation/pages/root/logic.dart b/packages/chicken/lib/presentation/pages/root/logic.dart index bc67ca1..570614a 100644 --- a/packages/chicken/lib/presentation/pages/root/logic.dart +++ b/packages/chicken/lib/presentation/pages/root/logic.dart @@ -1,14 +1,15 @@ import 'dart:async'; import 'package:flutter/widgets.dart'; -import 'package:rasadyar_chicken/data/datasource/local/chicken_local_imp.dart'; +import 'package:rasadyar_chicken/data/data_source/local/chicken_local_imp.dart'; import 'package:rasadyar_chicken/data/di/chicken_di.dart'; import 'package:rasadyar_chicken/data/models/local/widely_used_local_model.dart'; import 'package:rasadyar_chicken/data/models/response/inventory/inventory_model.dart'; import 'package:rasadyar_chicken/data/models/response/iran_province_city/iran_province_city_model.dart'; import 'package:rasadyar_chicken/data/models/response/roles_products/roles_products.dart'; -import 'package:rasadyar_chicken/data/repositories/chicken_repository.dart'; -import 'package:rasadyar_chicken/data/repositories/chicken_repository_imp.dart'; +import 'package:rasadyar_chicken/data/repositories/chicken/chicken_repository.dart'; +import 'package:rasadyar_chicken/data/repositories/chicken/chicken_repository_imp.dart'; + import 'package:rasadyar_chicken/presentation/pages/buy/view.dart'; import 'package:rasadyar_chicken/presentation/pages/home/view.dart'; import 'package:rasadyar_chicken/presentation/pages/profile/view.dart'; diff --git a/packages/chicken/lib/presentation/routes/pages.dart b/packages/chicken/lib/presentation/routes/pages.dart index 09f4f78..c86c424 100644 --- a/packages/chicken/lib/presentation/routes/pages.dart +++ b/packages/chicken/lib/presentation/routes/pages.dart @@ -1,3 +1,5 @@ +import 'package:rasadyar_chicken/presentation/pages/auth/logic.dart'; +import 'package:rasadyar_chicken/presentation/pages/auth/view.dart'; import 'package:rasadyar_chicken/presentation/pages/buy/logic.dart'; import 'package:rasadyar_chicken/presentation/pages/buy/view.dart'; import 'package:rasadyar_chicken/presentation/pages/buy_in_province/logic.dart'; @@ -23,6 +25,7 @@ import 'package:rasadyar_chicken/presentation/pages/sales_out_of_province_sales_ import 'package:rasadyar_chicken/presentation/pages/segmentation/logic.dart'; import 'package:rasadyar_chicken/presentation/routes/routes.dart'; import 'package:rasadyar_chicken/presentation/widget/base_page/logic.dart'; +import 'package:rasadyar_chicken/presentation/widget/captcha/logic.dart'; import 'package:rasadyar_chicken/presentation/widget/search/logic.dart'; import 'package:rasadyar_core/core.dart'; @@ -30,6 +33,15 @@ sealed class ChickenPages { ChickenPages._(); static final pages = [ + GetPage( + name: ChickenRoutes.auth, + page: () => AuthPage(), + binding: BindingsBuilder(() { + Get.lazyPut(() => AuthLogic()); + Get.lazyPut(() => CaptchaWidgetLogic()); + }), + ), + GetPage( name: ChickenRoutes.init, page: () => RootPage(), diff --git a/packages/chicken/lib/presentation/widget/captcha/logic.dart b/packages/chicken/lib/presentation/widget/captcha/logic.dart new file mode 100644 index 0000000..806df2a --- /dev/null +++ b/packages/chicken/lib/presentation/widget/captcha/logic.dart @@ -0,0 +1,34 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:rasadyar_core/core.dart'; + +class CaptchaWidgetLogic extends GetxController with StateMixin { + TextEditingController textController = TextEditingController(); + RxnString captchaKey = RxnString(); + GlobalKey formKey = GlobalKey(); + + final Random random = Random(); + + @override + void onInit() { + super.onInit(); + + getCaptcha(); + } + + @override + void onClose() { + textController.clear(); + textController.dispose(); + super.onClose(); + } + + Future getCaptcha() async { + change(null, status: RxStatus.loading()); + textController.clear(); + await Future.delayed(Duration(milliseconds: 500)); + captchaKey.value = (random.nextInt(900_000) + 100_000).toString(); + change(captchaKey.value, status: RxStatus.success()); + } +} diff --git a/packages/chicken/lib/presentation/widget/captcha/view.dart b/packages/chicken/lib/presentation/widget/captcha/view.dart new file mode 100644 index 0000000..364bf9c --- /dev/null +++ b/packages/chicken/lib/presentation/widget/captcha/view.dart @@ -0,0 +1,111 @@ +import 'dart:convert'; +import 'dart:math'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:rasadyar_chicken/presentation/pages/auth/logic.dart'; +import 'package:rasadyar_core/core.dart'; + + +import 'logic.dart'; + +class CaptchaWidget extends GetView { + const CaptchaWidget({super.key}); + + @override + Widget build(BuildContext context) { + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + GestureDetector( + onTap: controller.getCaptcha, + child: Container( + width: 135, + height: 50, + alignment: Alignment.center, + clipBehavior: Clip.antiAliasWithSaveLayer, + decoration: BoxDecoration( + color: AppColor.whiteNormalHover, + border: Border.all(color: Colors.grey.shade300), + borderRadius: BorderRadius.circular(8), + ), + child: controller.obx( + (state) =>Text( + state ?? '', + style: AppFonts.yekan20Bold.copyWith(color: Colors.black,letterSpacing: 2.5), + textAlign: TextAlign.center, + ), + onLoading: const Center( + child: CupertinoActivityIndicator(color: AppColor.blueNormal), + ), + onError: (error) { + return Center( + child: Text('خطا ', style: AppFonts.yekan13.copyWith(color: Colors.red)), + ); + }, + ), + ), + ), + + const SizedBox(width: 8), + Expanded( + child: Form( + key: controller.formKey, + autovalidateMode: AutovalidateMode.disabled, + child: RTextField( + label: 'کد امنیتی', + controller: controller.textController, + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide(color: AppColor.textColor, width: 1), + ), + keyboardType: TextInputType.numberWithOptions(decimal: false, signed: false), + maxLines: 1, + maxLength: 6, + suffixIcon: (controller.textController.text.trim().isNotEmpty ?? false) + ? clearButton(() => controller.textController.clear()) + : null, + + validator: (value) { + if (value == null || value.isEmpty) { + return 'کد امنیتی را وارد کنید'; + } + return null; + }, + onChanged: (pass) { + if (pass.length == 6) { + if (controller.formKey.currentState?.validate() ?? false) { + Get.find().isDisabled.value = false; + } + } + }, + style: AppFonts.yekan13, + ), + ), + ), + ], + ); + } +} + +class _CaptchaLinePainter extends CustomPainter { + @override + void paint(Canvas canvas, Size size) { + final random = Random(); + final paint1 = Paint() + ..color = Colors.deepOrange + ..strokeWidth = 2; + final paint2 = Paint() + ..color = Colors.blue + ..strokeWidth = 2; + + // First line: top-left to bottom-right + canvas.drawLine(Offset(0, 0), Offset(size.width, size.height), paint1); + + // Second line: bottom-left to top-right + canvas.drawLine(Offset(0, size.height), Offset(size.width, 0), paint2); + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) => false; +} diff --git a/packages/core/lib/presentation/widget/buttons/buttons.dart b/packages/core/lib/presentation/widget/buttons/buttons.dart index bbb8a45..7dd4917 100644 --- a/packages/core/lib/presentation/widget/buttons/buttons.dart +++ b/packages/core/lib/presentation/widget/buttons/buttons.dart @@ -4,4 +4,5 @@ export 'fab.dart'; export 'fab_outlined.dart'; export 'outline_elevated.dart'; export 'outline_elevated_icon.dart'; -export 'text_button.dart'; \ No newline at end of file +export 'text_button.dart'; +export 'clear_button.dart'; \ No newline at end of file diff --git a/packages/inspection/lib/presentation/widget/clear_button.dart b/packages/core/lib/presentation/widget/buttons/clear_button.dart similarity index 100% rename from packages/inspection/lib/presentation/widget/clear_button.dart rename to packages/core/lib/presentation/widget/buttons/clear_button.dart diff --git a/packages/inspection/lib/presentation/widget/logo_widget.dart b/packages/core/lib/presentation/widget/logo_widget.dart similarity index 100% rename from packages/inspection/lib/presentation/widget/logo_widget.dart rename to packages/core/lib/presentation/widget/logo_widget.dart diff --git a/packages/core/lib/presentation/widget/widget.dart b/packages/core/lib/presentation/widget/widget.dart index 42bb4a4..9a8b1a3 100644 --- a/packages/core/lib/presentation/widget/widget.dart +++ b/packages/core/lib/presentation/widget/widget.dart @@ -3,6 +3,7 @@ export 'bottom_navigation/r_bottom_navigation.dart'; export 'bottom_navigation/wave_bottom_navigation.dart'; export 'bottom_sheet/base_bottom_sheet.dart'; export 'bottom_sheet/date_picker_bottom_sheet.dart'; +//buttons export 'buttons/buttons.dart'; export 'card/card_with_icon_with_border.dart'; export 'chips/r_chips.dart'; @@ -12,6 +13,13 @@ export 'draggable_bottom_sheet/draggable_bottom_sheet.dart'; export 'draggable_bottom_sheet/draggable_bottom_sheet2.dart'; export 'draggable_bottom_sheet/draggable_bottom_sheet_controller.dart'; export 'empty_widget.dart'; +//inputs +export 'inputs/inputs.dart'; +//list_item +export 'list_item/list_item.dart'; +export 'list_item/list_item2.dart'; +export 'list_item/list_item_with_out_number.dart'; +export 'list_row_item.dart'; export 'list_view/list_view.dart'; export 'loading_widget.dart'; export 'overlay_dropdown_widget/view.dart'; @@ -21,11 +29,6 @@ export 'tabs/new_tab.dart'; export 'tabs/r_segment.dart'; export 'tabs/tab.dart'; export 'vec_widget.dart'; -export 'list_row_item.dart'; -//inputs -export 'inputs/inputs.dart'; -//list_item -export 'list_item/list_item.dart'; -export 'list_item/list_item2.dart'; -export 'list_item/list_item_with_out_number.dart'; +// other +export 'logo_widget.dart'; diff --git a/packages/inspection/lib/presentation/pages/auth/view.dart b/packages/inspection/lib/presentation/pages/auth/view.dart index 054ba37..6ab8612 100644 --- a/packages/inspection/lib/presentation/pages/auth/view.dart +++ b/packages/inspection/lib/presentation/pages/auth/view.dart @@ -2,9 +2,6 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:rasadyar_core/core.dart'; import 'package:rasadyar_inspection/presentation/widget/captcha/view.dart'; -import 'package:rasadyar_inspection/presentation/widget/clear_button.dart'; -import 'package:rasadyar_inspection/presentation/widget/logo_widget.dart'; - import 'logic.dart'; class AuthPage extends GetView { @@ -45,7 +42,10 @@ class AuthPage extends GetView { ), Obx(() { - final screenHeight = MediaQuery.of(context).size.height; + final screenHeight = MediaQuery + .of(context) + .size + .height; final targetTop = (screenHeight - 676) / 2; return AnimatedPositioned( @@ -129,11 +129,13 @@ class AuthPage extends GetView { padding: const EdgeInsets.fromLTRB(0, 8, 6, 8), child: Assets.vec.callSvg.svg(width: 12, height: 12), ), - suffixIcon: controller.usernameController.value.text.trim().isNotEmpty + suffixIcon: controller.usernameController.value.text + .trim() + .isNotEmpty ? clearButton(() { - controller.usernameController.value.clear(); - controller.usernameController.refresh(); - }) + controller.usernameController.value.clear(); + controller.usernameController.refresh(); + }) : null, validator: (value) { if (value == null || value.isEmpty) { @@ -155,41 +157,42 @@ class AuthPage extends GetView { ), const SizedBox(height: 26), ObxValue( - (passwordController) => RTextField( - label: 'رمز عبور', - filled: false, - obscure: true, - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(8), - borderSide: BorderSide(color: AppColor.textColor, width: 1), - ), - controller: passwordController.value, - autofillHints: [AutofillHints.password], - variant: RTextFieldVariant.password, - initText: passwordController.value.text, - onChanged: (value) { - passwordController.refresh(); - }, - validator: (value) { - if (value == null || value.isEmpty) { - return '⚠️ رمز عبور را وارد کنید'; - } - return null; - }, - style: AppFonts.yekan13, - errorStyle: AppFonts.yekan13.copyWith(color: AppColor.redNormal), - labelStyle: AppFonts.yekan13, - prefixIcon: Padding( - padding: const EdgeInsets.fromLTRB(0, 8, 8, 8), - child: Assets.vec.keySvg.svg(width: 12, height: 12), - ), - boxConstraints: const BoxConstraints( - maxHeight: 34, - minHeight: 34, - maxWidth: 34, - minWidth: 34, - ), - ), + (passwordController) => + RTextField( + label: 'رمز عبور', + filled: false, + obscure: true, + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide(color: AppColor.textColor, width: 1), + ), + controller: passwordController.value, + autofillHints: [AutofillHints.password], + variant: RTextFieldVariant.password, + initText: passwordController.value.text, + onChanged: (value) { + passwordController.refresh(); + }, + validator: (value) { + if (value == null || value.isEmpty) { + return '⚠️ رمز عبور را وارد کنید'; + } + return null; + }, + style: AppFonts.yekan13, + errorStyle: AppFonts.yekan13.copyWith(color: AppColor.redNormal), + labelStyle: AppFonts.yekan13, + prefixIcon: Padding( + padding: const EdgeInsets.fromLTRB(0, 8, 8, 8), + child: Assets.vec.keySvg.svg(width: 12, height: 12), + ), + boxConstraints: const BoxConstraints( + maxHeight: 34, + minHeight: 34, + maxWidth: 34, + minWidth: 34, + ), + ), controller.passwordController, ), SizedBox(height: 26), @@ -203,8 +206,8 @@ class AuthPage extends GetView { onPressed: controller.isDisabled.value ? null : () async { - await controller.submitLoginForm(); - }, + await controller.submitLoginForm(); + }, width: Get.width, height: 48, ); @@ -305,7 +308,7 @@ class AuthPage extends GetView { ); } - /* +/* Widget sendCodeForm() { return ObxValue((data) { return Form( diff --git a/packages/inspection/lib/presentation/routes/app_pages.dart b/packages/inspection/lib/presentation/routes/app_pages.dart index 02ac975..e6cc067 100644 --- a/packages/inspection/lib/presentation/routes/app_pages.dart +++ b/packages/inspection/lib/presentation/routes/app_pages.dart @@ -13,6 +13,15 @@ sealed class InspectionPages { InspectionPages._(); static final pages = [ + + GetPage( + name: InspectionRoutes.auth, + page: () => AuthPage(), + binding: BindingsBuilder(() { + Get.lazyPut(() => AuthLogic()); + Get.lazyPut(() => CaptchaWidgetLogic()); + }), + ), GetPage( name: InspectionRoutes.init, page: () => RootPage(), @@ -64,13 +73,6 @@ sealed class InspectionPages { page: () => AddMobileInspectorPage(), binding: BindingsBuilder.put(() => AddMobileInspectorLogic()), ), - GetPage( - name: InspectionRoutes.auth, - page: () => AuthPage(), - binding: BindingsBuilder(() { - Get.lazyPut(() => AuthLogic()); - Get.lazyPut(() => CaptchaWidgetLogic()); - }), - ), + ]; } diff --git a/packages/inspection/lib/presentation/widget/captcha/view.dart b/packages/inspection/lib/presentation/widget/captcha/view.dart index 1c727b7..d66d57a 100644 --- a/packages/inspection/lib/presentation/widget/captcha/view.dart +++ b/packages/inspection/lib/presentation/widget/captcha/view.dart @@ -5,7 +5,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:rasadyar_core/core.dart'; import 'package:rasadyar_inspection/presentation/pages/auth/logic.dart'; -import 'package:rasadyar_inspection/presentation/widget/clear_button.dart'; + import 'logic.dart'; @@ -29,7 +29,8 @@ class CaptchaWidget extends GetView { borderRadius: BorderRadius.circular(8), ), child: controller.obx( - (state) => Image.memory(base64Decode(state?.captchaImage ?? ''), fit: BoxFit.cover), + (state) => + Image.memory(base64Decode(state?.captchaImage ?? ''), fit: BoxFit.cover), onLoading: const Center( child: CupertinoActivityIndicator(color: AppColor.blueNormal), ), @@ -57,7 +58,9 @@ class CaptchaWidget extends GetView { keyboardType: TextInputType.numberWithOptions(decimal: false, signed: false), maxLines: 1, maxLength: 6, - suffixIcon: (controller.textController.text.trim().isNotEmpty ?? false) + suffixIcon: (controller.textController.text + .trim() + .isNotEmpty ?? false) ? clearButton(() => controller.textController.clear()) : null, @@ -70,7 +73,10 @@ class CaptchaWidget extends GetView { onChanged: (pass) { if (pass.length == 6) { if (controller.formKey.currentState?.validate() ?? false) { - Get.find().isDisabled.value = false; + Get + .find() + .isDisabled + .value = false; } } },