diff --git a/lib/api/login_api_client.dart b/lib/api/login_api_client.dart index 24d248a..34a0569 100644 --- a/lib/api/login_api_client.dart +++ b/lib/api/login_api_client.dart @@ -1,5 +1,7 @@ import 'dart:async'; +import 'dart:io'; +import 'package:flutter/material.dart'; import 'package:mohem_flutter_app/api/api_client.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/consts.dart'; @@ -169,4 +171,77 @@ class LoginApiClient { return responseData; }, url, postParams); } + + Future memberLoginDependent({ + required String nationalId, + required String birthDate, + BuildContext? context, + }) async { + String url = "${ApiConsts.erpRest}MemberLogin_Dependent"; + Map postParams = { + "VersionID": 20, + "Channel": 31, + "LanguageID": context != null ? AppState().getLanguageID(context) : 2, + "MobileType": Platform.isAndroid ? "android" : "ios", + "P_DEP_NATIONAL_ID": nationalId, + "P_DEP_BIRTH_DATE": birthDate, + }; + return await ApiClient().postJsonForObject((json) { + GenericResponseModel responseData = GenericResponseModel.fromJson(json); + if (responseData.logInTokenID != null) { + AppState().postParamsObject?.setLogInTokenID = responseData.logInTokenID; + } + return responseData; + }, url, postParams); + } + + Future mohemmSendOTPDependent({ + required String nationalId, + required String mobileNumber, + required String logInTokenID, + BuildContext? context, + }) async { + String url = "${ApiConsts.erpRest}Mohemm_SendOTP_Dependent"; + Map postParams = { + "VersionID": 20, + "Channel": 31, + "LanguageID": context != null ? AppState().getLanguageID(context) : 2, + "MobileType": Platform.isAndroid ? "android" : "ios", + "OTP_SendType": 1, + "MobileNumber": mobileNumber, + "IsMobileFingerPrint": 0, + "P_DEP_NATIONAL_ID": nationalId, + "P_USER_NAME": nationalId, + "LogInTokenID": logInTokenID, + }; + return await ApiClient().postJsonForObject((json) { + GenericResponseModel responseData = GenericResponseModel.fromJson(json); + return responseData; + }, url, postParams); + } + + Future mohemmCheckOTPDependent({ + required String logInTokenID, + required String activationCode, + required String nationalId, + required String mobileNumber, + BuildContext? context, + }) async { + String url = "${ApiConsts.erpRest}Mohemm_CheckOTP_Dependent"; + Map postParams = { + "LogInTokenID": logInTokenID, + "activationCode": activationCode, + "P_USER_NAME": nationalId, + "P_DEP_NATIONAL_ID": nationalId, + "MobileNumber": mobileNumber, + "VersionID": 20, + "Channel": 31, + "LanguageID": context != null ? AppState().getLanguageID(context) : 2, + "MobileType": Platform.isAndroid ? "android" : "ios", + }; + return await ApiClient().postJsonForObject((json) { + GenericResponseModel responseData = GenericResponseModel.fromJson(json); + return responseData; + }, url, postParams); + } } diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart index 5e980a9..46627f8 100644 --- a/lib/classes/consts.dart +++ b/lib/classes/consts.dart @@ -12,9 +12,9 @@ class ApiConsts { // static String baseUrl = "https://mohemm.hmg.com"; // New Live server static String ssoBaseUrl = "https://sso.hmg.com"; // New Live server // - // static String baseUrl = "https://uat.hmgwebservices.com"; // UAT ser343622ver + static String baseUrl = "https://uat.hmgwebservices.com"; // UAT ser343622ver // static String baseUrl = "http://10.20.200.111:1010/"; - static String baseUrl = "https://mohemm.hmg.com"; // New Live server + // static String baseUrl = "https://mohemm.hmg.com"; // New Live server //static String eLearningBaseUrl = "https://elearning.hmg.com/moodle_dev/api/"; // Elearning server UAT static String eLearningBaseUrl = "https://elearning.hmg.com/moodle/api/"; // Elearning server live static String enSATLiveURL = "https://ensat.hmg.com/auth/autologin"; // ENSAT server live @@ -34,6 +34,11 @@ class ApiConsts { static String user = baseUrlServices + "api/User/"; static String cocRest = baseUrlServices + "COCWS.svc/REST/"; + // Dependent Login Endpoints + static String memberLoginDependent = erpRest + "MemberLogin_Dependent"; + static String mohemmSendOTPDependent = erpRest + "Mohemm_SendOTP_Dependent"; + static String mohemmCheckOTPDependent = erpRest + "Mohemm_CheckOTP_Dependent"; + static String ssoAuthRedirection = ssoBaseUrl + "/api/auth/connect"; @@ -85,6 +90,10 @@ class ApiConsts { static String notificationCenterAppGUID = "VDZreDY5TTNmTDVudzc4aDkwU3ZqcW9OcmI0"; static String notificationCenterLoginServiceGUID = "D8BD5C76-4B50-4122-81D0-C3AA25DB08A2"; static String notificationCenterForgotPasswordServiceGUID = "5CCCC2C9-EEA0-4207-9BE9-EE1FBBFFE8D0"; + + static bool ISDependetLogin = false; + static bool get isDependetLogin => ISDependetLogin; + static set isDependetLogin(bool value) => ISDependetLogin = value; } class SharedPrefsConsts { @@ -100,3 +109,7 @@ class SharedPrefsConsts { static String mohemmWifiPassword = "mohemmWifiPassword"; static String editItemForSale = "editItemForSale"; } + + + + diff --git a/lib/config/routes.dart b/lib/config/routes.dart index 9bae8df..727cf5d 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -14,6 +14,7 @@ import 'package:mohem_flutter_app/ui/etqan_ovr/etqan_create_request.dart'; import 'package:mohem_flutter_app/ui/etqan_ovr/etqan_ovr_home.dart'; import 'package:mohem_flutter_app/ui/etqan_ovr/etqan_request_detailed.dart'; import 'package:mohem_flutter_app/ui/landing/dashboard_screen.dart'; +import 'package:mohem_flutter_app/ui/landing/dashboard_screen_for_dependet.dart'; import 'package:mohem_flutter_app/ui/landing/itg/change_itg_ad_password_screen.dart'; import 'package:mohem_flutter_app/ui/landing/itg/its_add_screen_video_image.dart'; import 'package:mohem_flutter_app/ui/landing/itg/survey_screen.dart'; @@ -97,6 +98,7 @@ class AppRoutes { static const String newPassword = "/newPassword"; static const String loginVerification = "/loginVerification"; static const String dashboard = "/dashboard"; + static const String dashboardScreenForDependet = "/dashboardScreenForDependet"; static const String todayAttendance = "/todayAttendance"; static const String subMenuScreen = "/submenuScreen"; static const String addEitScreen = "/addeitScreen"; @@ -231,6 +233,7 @@ class AppRoutes { verifyLogin: (BuildContext context) => VerifyLoginScreen(), verifyLastLogin: (BuildContext context) => VerifyLastLoginScreen(), dashboard: (BuildContext context) => DashboardScreen(), + dashboardScreenForDependet: (BuildContext context) => const DashboardScreenForDependet(), survey: (BuildContext context) => SurveyScreen(), advertisement: (BuildContext context) => ITGAdsScreen(), diff --git a/lib/models/generic_response_model.dart b/lib/models/generic_response_model.dart index 01e3056..c4bd672 100644 --- a/lib/models/generic_response_model.dart +++ b/lib/models/generic_response_model.dart @@ -400,6 +400,9 @@ class GenericResponseModel { PortalDirectionData? portalDirectionData; String? Mohemm_ENSAT_GetItgToken_Response; + GetWalaplusDepInfoModel? getWalaplusDepInfoModel; + dynamic getWalaplusDepInfoModelInternal; + GenericResponseModel({ this.date, this.languageID, @@ -672,6 +675,8 @@ class GenericResponseModel { this.isSMSSent, this.getFADisposalNtfDetails, this.Mohemm_ENSAT_GetItgToken_Response, + this.getWalaplusDepInfoModel, + this.getWalaplusDepInfoModelInternal, }); GenericResponseModel.fromJson(Map json) { @@ -1466,6 +1471,9 @@ class GenericResponseModel { isActiveCode = json['isActiveCode']; isSMSSent = json['isSMSSent']; Mohemm_ENSAT_GetItgToken_Response = json['Mohemm_ENSAT_GetItgToken_Response']; + + getWalaplusDepInfoModel = json['GET_WALAPLUS_DEP_INFO_Model'] != null ? GetWalaplusDepInfoModel.fromJson(json['GET_WALAPLUS_DEP_INFO_Model']) : null; + getWalaplusDepInfoModelInternal = json['GET_WALAPLUS_DEP_INFO_Model_Internal']; } Map toJson() { @@ -1952,6 +1960,12 @@ class GenericResponseModel { data['isActiveCode'] = this.isActiveCode; data['isSMSSent'] = this.isSMSSent; data['Mohemm_ENSAT_GetItgToken_Response'] = this.Mohemm_ENSAT_GetItgToken_Response; + + data['GET_WALAPLUS_DEP_INFO_Model_Internal'] = this.getWalaplusDepInfoModelInternal; + if (this.getWalaplusDepInfoModel != null) { + data['GET_WALAPLUS_DEP_INFO_Model'] = this.getWalaplusDepInfoModel!.toJson(); + } + return data; } } @@ -1977,3 +1991,14 @@ class TicketBookingResult { TicketBookingResult(this.success, this.clientId); } + +class GetWalaplusDepInfoModel { + String? pEmpEmailAddress; + String? pEmpMobileNumber; + + GetWalaplusDepInfoModel({this.pEmpEmailAddress, this.pEmpMobileNumber}); + + factory GetWalaplusDepInfoModel.fromJson(Map json) => GetWalaplusDepInfoModel(pEmpEmailAddress: json["P_EMP_EMAIL_ADDRESS"], pEmpMobileNumber: json["P_EMP_MOBILE_NUMBER"]); + + Map toJson() => {"P_EMP_EMAIL_ADDRESS": pEmpEmailAddress, "P_EMP_MOBILE_NUMBER": pEmpMobileNumber}; +} diff --git a/lib/ui/landing/dashboard_screen_for_dependet.dart b/lib/ui/landing/dashboard_screen_for_dependet.dart new file mode 100644 index 0000000..3c69df3 --- /dev/null +++ b/lib/ui/landing/dashboard_screen_for_dependet.dart @@ -0,0 +1,231 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:mohem_flutter_app/app_state/app_state.dart'; +import 'package:mohem_flutter_app/classes/colors.dart'; +import 'package:mohem_flutter_app/classes/consts.dart'; +import 'package:mohem_flutter_app/classes/utils.dart'; +import 'package:mohem_flutter_app/config/routes.dart'; +import 'package:mohem_flutter_app/extensions/int_extensions.dart'; +import 'package:mohem_flutter_app/extensions/string_extensions.dart'; +import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; +import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; +import 'package:mohem_flutter_app/models/offers_and_discounts/get_offers_list.dart'; +import 'package:mohem_flutter_app/widgets/button/default_button.dart'; +import 'package:mohem_flutter_app/widgets/dynamic_forms/dynamic_textfield_widget.dart'; + +class DashboardScreenForDependet extends StatefulWidget { + const DashboardScreenForDependet({Key? key}) : super(key: key); + + @override + State createState() => _DashboardScreenForDependetState(); +} + +class _DashboardScreenForDependetState extends State { + List offersList = []; + List _filteredOffersList = []; + bool isLoading = false; + String searchQuery = ""; + + @override + void initState() { + super.initState(); + fetchOffers(); + } + + /// Offers API placeholder - will be connected when the endpoint is provided + Future fetchOffers() async { + setState(() { + isLoading = true; + }); + + try { + // TODO: Call Dependent Offers API here once provided + // Example: + // offersList = await OffersApiClient().getDependentOffers(); + await Future.delayed(const Duration(milliseconds: 300)); + _filteredOffersList = offersList; + } catch (e) { + print("Error fetching dependent offers: $e"); + } finally { + if (mounted) { + setState(() { + isLoading = false; + }); + } + } + } + + void _runFilter(String query) { + searchQuery = query; + if (query.isEmpty) { + _filteredOffersList = offersList; + } else { + bool isAr = AppState().isArabic(context); + _filteredOffersList = + offersList.where((offer) { + String title = isAr ? (offer.titleAr ?? "") : (offer.titleEn ?? ""); + return title.toLowerCase().contains(query.toLowerCase()); + }).toList(); + } + setState(() {}); + } + + void _performLogout() { + Utils.confirmDialog( + context, + AppState().isArabic(context) ? "هل أنت متأكد من تسجيل الخروج؟" : "Are you sure you want to log out?", + onTap: () { + ApiConsts.ISDependetLogin = false; + Navigator.pushNamedAndRemoveUntil(context, AppRoutes.login, (Route route) => false); + }, + ); + } + + @override + Widget build(BuildContext context) { + bool isAr = AppState().isArabic(context); + + return Scaffold( + backgroundColor: MyColors.backgroundColor, + appBar: AppBar( + backgroundColor: Colors.white, + elevation: 0, + surfaceTintColor: Colors.transparent, + automaticallyImplyLeading: false, + title: Row(children: [(isAr ? "عروض التابعين" : "Offers & Discounts").toText20(color: MyColors.darkTextColor, isBold: true)]), + actions: [ + // Logout button + Container( + margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + decoration: BoxDecoration(color: MyColors.redColor.withOpacity(0.08), borderRadius: BorderRadius.circular(8)), + child: InkWell( + borderRadius: BorderRadius.circular(8), + onTap: _performLogout, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [const Icon(Icons.logout_rounded, size: 18, color: MyColors.redColor), 6.width, (isAr ? "خروج" : "Logout").toText13(color: MyColors.redColor)], + ), + ), + ), + ), + ], + ), + body: RefreshIndicator( + onRefresh: fetchOffers, + color: MyColors.textMixColor, + child: SingleChildScrollView( + physics: const AlwaysScrollableScrollPhysics(parent: BouncingScrollPhysics()), + padding: const EdgeInsets.all(21), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Search input + DynamicTextFieldWidget( + LocaleKeys.search.tr(), + LocaleKeys.searchItems.tr(), + isEnable: true, + suffixIconData: Icons.search, + isPopup: false, + lines: 1, + isInputTypeNum: false, + isReadOnly: false, + onChange: (String value) { + _runFilter(value); + }, + ), + 20.height, + + // Header + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + (isAr ? "العروض المتاحة" : "Available Offers").toText18(isBold: true, color: MyColors.darkTextColor), + if (offersList.isNotEmpty) "${_filteredOffersList.length} ${isAr ? "عرض" : "offers"}".toText12(color: MyColors.grey70Color), + ], + ), + 16.height, + + // Content Area + if (isLoading) + const Center(child: Padding(padding: EdgeInsets.symmetric(vertical: 60), child: CircularProgressIndicator(color: MyColors.textMixColor))) + else if (_filteredOffersList.isEmpty) + _buildEmptyOffersWidget(isAr) + else + GridView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2, childAspectRatio: 162 / 266, crossAxisSpacing: 12, mainAxisSpacing: 12), + itemCount: _filteredOffersList.length, + itemBuilder: (context, index) { + return _buildOfferCard(_filteredOffersList[index], isAr); + }, + ), + ], + ), + ), + ), + ); + } + + Widget _buildEmptyOffersWidget(bool isAr) { + return Center( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 60, horizontal: 24), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 80, + height: 80, + decoration: BoxDecoration(color: MyColors.textMixColor.withOpacity(0.1), shape: BoxShape.circle), + child: const Icon(Icons.local_offer_outlined, size: 40, color: MyColors.textMixColor), + ), + 16.height, + (isAr ? "لا توجد عروض متاحة حالياً" : "No Offers Available Currently").toText16(isBold: true, color: MyColors.darkTextColor), + 8.height, + (isAr ? "سيتم إتاحة العروض الخاصة بك قريباً" : "Exclusive offers for dependents will be available soon.").toText13(color: MyColors.grey70Color), + 24.height, + DefaultButton(isAr ? "تحديث" : "Refresh", fetchOffers, colors: const [MyColors.gradiantStartColor, MyColors.gradiantEndColor]), + ], + ), + ), + ); + } + + Widget _buildOfferCard(OffersListModel item, bool isAr) { + return Container( + padding: const EdgeInsets.all(10.0), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(10), + boxShadow: [BoxShadow(color: const Color(0xff000000).withOpacity(.05), blurRadius: 26, offset: const Offset(0, -3))], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + AspectRatio( + aspectRatio: 118 / 127, + child: ClipRRect( + borderRadius: BorderRadius.circular(6), + child: + (item.bannerImage != null && item.bannerImage!.isNotEmpty) + ? Image.network( + item.bannerImage!, + fit: BoxFit.contain, + errorBuilder: (_, __, ___) => Container(color: MyColors.greyF7Color, child: const Icon(Icons.broken_image, color: MyColors.grey70Color)), + ) + : Container(color: MyColors.greyF7Color, child: const Icon(Icons.image_outlined, color: MyColors.grey70Color)), + ), + ), + 8.height, + (isAr ? (item.titleAr ?? "") : (item.titleEn ?? "")).toText16(isBold: true, color: MyColors.darkTextColor, maxlines: 1), + if (item.discountDescription != null && item.discountDescription!.isNotEmpty) ...[4.height, item.discountDescription!.toText14(isBold: true, maxlines: 1)], + ], + ), + ); + } +} diff --git a/lib/ui/login/login_screen.dart b/lib/ui/login/login_screen.dart index 13d8853..1d8fcc2 100644 --- a/lib/ui/login/login_screen.dart +++ b/lib/ui/login/login_screen.dart @@ -12,6 +12,7 @@ import 'package:flutter/services.dart'; // import 'package:huawei_hmsavailability/huawei_hmsavailability.dart'; import 'package:mohem_flutter_app/api/login_api_client.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart'; +import 'package:intl/intl.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; import 'package:mohem_flutter_app/classes/consts.dart'; import 'package:mohem_flutter_app/classes/notifications.dart'; @@ -48,6 +49,9 @@ class LoginScreen extends StatefulWidget { class _LoginScreenState extends State { TextEditingController username = TextEditingController(); TextEditingController password = TextEditingController(); + TextEditingController nationalId = TextEditingController(); + TextEditingController dobController = TextEditingController(); + DateTime? selectedDob; CheckMobileAppVersionModel? _checkMobileAppVersion; MemberLoginListModel? _memberLoginList; @@ -70,6 +74,7 @@ class _LoginScreenState extends State { @override void initState() { super.initState(); + ApiConsts.ISDependetLogin = false; // hmsApiAvailability = HmsApiAvailability(); // checkFirebaseToken(); if (kReleaseMode) { @@ -99,6 +104,7 @@ class _LoginScreenState extends State { @override void dispose() { + dobController.dispose(); super.dispose(); } @@ -279,6 +285,264 @@ class _LoginScreenState extends State { } } + Widget _buildTabSwitcher() { + return Container( + height: 46, + decoration: BoxDecoration( + color: const Color(0xffF2F4F7), + borderRadius: BorderRadius.circular(12), + ), + padding: const EdgeInsets.all(4), + child: Row( + children: [ + Expanded( + child: Container( + decoration: BoxDecoration( + color: !ApiConsts.isDependetLogin ? Colors.white : Colors.transparent, + borderRadius: BorderRadius.circular(9), + boxShadow: !ApiConsts.isDependetLogin + ? [ + BoxShadow( + color: Colors.black.withOpacity(0.06), + blurRadius: 4, + offset: const Offset(0, 2), + ) + ] + : null, + ), + alignment: Alignment.center, + child: (AppState().isArabic(context) ? "المستخدم" : "Default User").toText14( + color: !ApiConsts.isDependetLogin ? MyColors.textMixColor : MyColors.grey70Color, + isBold: !ApiConsts.isDependetLogin, + ), + ).onPress(() { + if (ApiConsts.isDependetLogin) { + setState(() { + ApiConsts.isDependetLogin = false; + }); + } + }), + ), + Expanded( + child: Container( + decoration: BoxDecoration( + color: ApiConsts.isDependetLogin ? Colors.white : Colors.transparent, + borderRadius: BorderRadius.circular(9), + boxShadow: ApiConsts.isDependetLogin + ? [ + BoxShadow( + color: Colors.black.withOpacity(0.06), + blurRadius: 4, + offset: const Offset(0, 2), + ) + ] + : null, + ), + alignment: Alignment.center, + child: (AppState().isArabic(context) ? "التابع" : "Dependent").toText14( + color: ApiConsts.isDependetLogin ? MyColors.textMixColor : MyColors.grey70Color, + isBold: ApiConsts.isDependetLogin, + ), + ).onPress(() { + if (!ApiConsts.isDependetLogin) { + setState(() { + ApiConsts.isDependetLogin = true; + }); + } + }), + ), + ], + ), + ); + } + + Future _selectDateOfBirth(BuildContext context) async { + DateTime initialDate = selectedDob ?? DateTime(2000, 1, 1); + DateTime firstDate = DateTime(1920, 1, 1); + DateTime lastDate = DateTime.now(); + + DateTime? picked = await showDatePicker( + context: context, + initialDate: initialDate.isAfter(lastDate) ? lastDate : initialDate, + firstDate: firstDate, + lastDate: lastDate, + builder: (context, child) { + return Theme( + data: Theme.of(context).copyWith( + colorScheme: const ColorScheme.light( + primary: MyColors.textMixColor, + onPrimary: Colors.white, + onSurface: MyColors.darkTextColor, + ), + ), + child: child!, + ); + }, + ); + + if (picked != null) { + setState(() { + selectedDob = picked; + dobController.text = DateFormat('yyyy-MM-dd', 'en_US').format(picked); + }); + } + } + + String getFormattedBirthDate() { + DateTime? date = selectedDob; + if (date == null && dobController.text.trim().isNotEmpty) { + String text = dobController.text.trim(); + try { + if (text.contains('-')) { + List parts = text.split('-'); + if (parts[0].length == 4) { + // yyyy-MM-dd + date = DateTime.utc(int.parse(parts[0]), int.parse(parts[1]), int.parse(parts[2])); + } else if (parts[2].length == 4) { + // dd-MM-yyyy + date = DateTime.utc(int.parse(parts[2]), int.parse(parts[1]), int.parse(parts[0])); + } + } else if (text.contains('/')) { + List parts = text.split('/'); + if (parts[0].length == 4) { + // yyyy/MM/dd + date = DateTime.utc(int.parse(parts[0]), int.parse(parts[1]), int.parse(parts[2])); + } else if (parts[2].length == 4) { + // dd/MM/yyyy + date = DateTime.utc(int.parse(parts[2]), int.parse(parts[1]), int.parse(parts[0])); + } + } else { + DateTime parsed = DateTime.parse(text); + date = DateTime.utc(parsed.year, parsed.month, parsed.day); + } + } catch (_) { + return ""; + } + } + + if (date != null) { + int utcMillis = DateTime.utc(date.year, date.month, date.day).millisecondsSinceEpoch; + return "/Date($utcMillis)/"; + } + return ""; + } + + void performDependentLogin() async { + if (nationalId.text.trim().isEmpty) { + Utils.showToast(AppState().isArabic(context) ? "الرجاء إدخال رقم الهوية الوطنية" : "Please enter National ID"); + return; + } + String birthDateStr = getFormattedBirthDate(); + if (birthDateStr.isEmpty) { + Utils.showToast(AppState().isArabic(context) ? "الرجاء تحديد تاريخ الميلاد" : "Please select Date of Birth"); + return; + } + + Utils.showLoading(context); + try { + GenericResponseModel? depLoginResponse = await LoginApiClient().memberLoginDependent( + nationalId: nationalId.text.trim(), + birthDate: birthDateStr, + context: context, + ); + + if (depLoginResponse?.errorMessage != null && depLoginResponse!.errorMessage!.isNotEmpty) { + Utils.hideLoading(context); + Utils.showToast(depLoginResponse.errorMessage!); + return; + } + + String? logInTokenID = depLoginResponse?.logInTokenID; + String? mobileNumber = depLoginResponse?.getWalaplusDepInfoModel?.pEmpMobileNumber; + + if (logInTokenID == null || mobileNumber == null || mobileNumber.isEmpty) { + Utils.hideLoading(context); + Utils.showToast(AppState().isArabic(context) ? "تعذر الحصول على معلومات التابع" : "Unable to retrieve dependent information"); + return; + } + + // Step 2: Send OTP + GenericResponseModel? sendOtpResponse = await LoginApiClient().mohemmSendOTPDependent( + nationalId: nationalId.text.trim(), + mobileNumber: mobileNumber, + logInTokenID: logInTokenID, + context: context, + ); + + Utils.hideLoading(context); + + if (sendOtpResponse?.messageStatus == 1) { + // Step 3: Open OTP Dialog + _showDependentOtpDialog(logInTokenID, mobileNumber); + } else { + Utils.showToast(sendOtpResponse?.errorMessage ?? (AppState().isArabic(context) ? "فشل إرسال رمز التحقق" : "Failed to send OTP")); + } + } catch (ex) { + print(ex); + Utils.hideLoading(context); + Utils.handleException(ex, context, null); + } + } + + void _showDependentOtpDialog(String logInTokenID, String mobileNumber) { + OtpDialog( + context, + 1, + int.tryParse(mobileNumber), + (value, TextEditingController _pinPutController) async { + Utils.showLoading(context); + try { + GenericResponseModel? checkOtpResponse = await LoginApiClient().mohemmCheckOTPDependent( + logInTokenID: logInTokenID, + activationCode: value, + nationalId: nationalId.text.trim(), + mobileNumber: mobileNumber, + context: context, + ); + Utils.hideLoading(context); + + if (checkOtpResponse?.errorMessage != null && checkOtpResponse!.errorMessage!.isNotEmpty) { + Utils.showToast(checkOtpResponse.errorMessage!); + _pinPutController.clear(); + otpFieldClear.value = ""; + return; + } + + Navigator.pop(context); + Utils.showToast(AppState().isArabic(context) ? "تم التحقق بنجاح" : "Verified successfully"); + if (ApiConsts.ISDependetLogin) { + Navigator.pushNamedAndRemoveUntil(context, AppRoutes.dashboardScreenForDependet, (route) => false); + } + } catch (ex) { + print(ex); + _pinPutController.clear(); + otpFieldClear.value = ""; + Utils.hideLoading(context); + Utils.handleException(ex, context, null); + } + }, + () { + Navigator.pop(context); + }, + onResendCode: () async { + Utils.showLoading(context); + try { + await LoginApiClient().mohemmSendOTPDependent( + nationalId: nationalId.text.trim(), + mobileNumber: mobileNumber, + logInTokenID: logInTokenID, + context: context, + ); + Utils.hideLoading(context); + Utils.showToast(AppState().isArabic(context) ? "تم إعادة إرسال رمز التحقق" : "Verification code resent"); + } catch (ex) { + Utils.hideLoading(context); + Utils.handleException(ex, context, null); + } + }, + ).displayDialog(context); + } + @override Widget build(BuildContext context) { if (isAppOpenBySystem == null) { @@ -338,23 +602,56 @@ class _LoginScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: [ + _buildTabSwitcher(), + 20.height, LocaleKeys.login.tr().toText24(isBold: true), LocaleKeys.pleaseEnterLoginDetails.tr().toText16(), 16.height, - InputWidget(LocaleKeys.username.tr(), "123456", username), - 12.height, - InputWidget(LocaleKeys.password.tr(), "xxxxxx", password, isTextIsPassword: true), - 9.height, - Align( - alignment: Alignment.centerRight, - child: LocaleKeys.forgotPassword.tr().toText12(isUnderLine: true, color: MyColors.textMixColor).onPress( - () { - // Navigator.pushNamed(context, AppRoutes.forgotPassword); - performForgotPassword(); - }, + if (!ApiConsts.isDependetLogin) ...[ + InputWidget(LocaleKeys.username.tr(), "123456", username), + 12.height, + InputWidget(LocaleKeys.password.tr(), "xxxxxx", password, isTextIsPassword: true), + 9.height, + Align( + alignment: Alignment.centerRight, + child: LocaleKeys.forgotPassword.tr().toText12(isUnderLine: true, color: MyColors.textMixColor).onPress( + () { + // Navigator.pushNamed(context, AppRoutes.forgotPassword); + performForgotPassword(); + }, + ), ), - ), - 20.height, + 20.height, + ] else ...[ + InputWidget(AppState().isArabic(context) ? "الهوية الوطنية" : "National ID", "1234567890", nationalId, isInputTypeNum: true), + 12.height, + GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () => _selectDateOfBirth(context), + child: Stack( + alignment: AlignmentDirectional.centerEnd, + children: [ + IgnorePointer( + child: InputWidget( + LocaleKeys.dateOfBirth.tr(), + "YYYY-MM-DD", + dobController, + ), + ), + Positioned.directional( + textDirection: Directionality.of(context), + end: 16, + child: const Icon( + Icons.calendar_today_outlined, + size: 20, + color: MyColors.grey70Color, + ), + ), + ], + ), + ), + 20.height, + ], // DefaultButton( // "Connect HMG Network", // () async { @@ -371,7 +668,11 @@ class _LoginScreenState extends State { ), DefaultButton(LocaleKeys.login.tr(), () async { SystemChannels.textInput.invokeMethod('TextInput.hide'); - performLogin(); + if (ApiConsts.ISDependetLogin) { + performDependentLogin(); + } else { + performLogin(); + } }).insideContainer ], ),