diff --git a/lib/core/dependencies.dart b/lib/core/dependencies.dart index 73a93c6..2a5c749 100644 --- a/lib/core/dependencies.dart +++ b/lib/core/dependencies.dart @@ -221,7 +221,7 @@ class AppDependencies { ); getIt.registerLazySingleton( - () => HmgServicesViewModel(bookAppointmentsRepo: getIt(), hmgServicesRepo: getIt(), errorHandlerService: getIt()), + () => HmgServicesViewModel(bookAppointmentsRepo: getIt(), hmgServicesRepo: getIt(), errorHandlerService: getIt(), navigationService: getIt()), ); // Screen-specific VMs → Factory diff --git a/lib/features/hmg_services/hmg_services_repo.dart b/lib/features/hmg_services/hmg_services_repo.dart index 6e1d533..4103569 100644 --- a/lib/features/hmg_services/hmg_services_repo.dart +++ b/lib/features/hmg_services/hmg_services_repo.dart @@ -8,6 +8,7 @@ import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/check_activation_e_referral_req_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/cmc_create_new_order_req_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/order_update_req_model.dart'; +import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/search_e_referral_req_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_all_cities_resp_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_all_orders_resp_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_services_resp_model.dart'; @@ -18,6 +19,7 @@ import 'package:provider/provider.dart'; import 'models/req_models/create_e_referral_model.dart'; import 'models/req_models/send_activation_code_ereferral_req_model.dart'; import 'models/resq_models/relationship_type_resp_mode.dart'; +import 'models/resq_models/search_e_referral_resp_model.dart'; abstract class HmgServicesRepo { Future>>> getAllComprehensiveCheckupOrders(); @@ -56,6 +58,10 @@ abstract class HmgServicesRepo { Future>> createEReferral(CreateEReferralRequestModel requestModel); + + Future>>> searchEReferral(SearchEReferralRequestModel requestModel); + + } class HmgServicesRepoImp implements HmgServicesRepo { @@ -757,4 +763,58 @@ class HmgServicesRepoImp implements HmgServicesRepo { return Left(UnknownFailure(e.toString())); } } + + + + @override + Future>>> searchEReferral(SearchEReferralRequestModel requestBody) async { + + + try { + GenericApiModel>? apiResponse; + Failure? failure; + + await apiClient.post( + ApiConsts.getEReferrals, + body: requestBody.toJson(), + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + loggerService.logError("EReferral Services API Failed: $error, Status: $statusCode"); + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + List searchReferral = []; + + if (response['List_EReferrals'] != null && response['List_EReferrals'] is List) { + final servicesList = response['List_EReferrals'] as List; + + for (var serviceJson in servicesList) { + if (serviceJson is Map) { + searchReferral.add(SearchEReferralResponseModel.fromJson(serviceJson)); + } + } + } + + apiResponse = GenericApiModel>( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: errorMessage, + data: searchReferral, + ); + } catch (e) { + loggerService.logError("Error parsing E-Referral services: ${e.toString()}"); + failure = DataParsingFailure(e.toString()); + } + }, + ); + + if (failure != null) return Left(failure!); + if (apiResponse == null) return Left(ServerFailure("Unknown error")); + return Right(apiResponse!); + } catch (e) { + log("Unknown error in Search Referral: ${e.toString()}"); + return Left(UnknownFailure(e.toString())); + } + } + } diff --git a/lib/features/hmg_services/hmg_services_view_model.dart b/lib/features/hmg_services/hmg_services_view_model.dart index 1e97a63..81cd060 100644 --- a/lib/features/hmg_services/hmg_services_view_model.dart +++ b/lib/features/hmg_services/hmg_services_view_model.dart @@ -1,16 +1,20 @@ import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart'; +import 'package:hmg_patient_app_new/core/enums.dart'; import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_repo.dart'; import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_repo.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/create_e_referral_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/cmc_create_new_order_req_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/order_update_req_model.dart'; +import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/search_e_referral_req_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/send_activation_code_ereferral_req_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_all_cities_resp_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_all_orders_resp_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_services_resp_model.dart'; +import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/search_e_referral_resp_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart'; import 'package:hmg_patient_app_new/services/error_handler_service.dart'; +import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'models/req_models/check_activation_e_referral_req_model.dart'; import 'models/resq_models/relationship_type_resp_mode.dart'; @@ -19,8 +23,9 @@ class HmgServicesViewModel extends ChangeNotifier { final HmgServicesRepo hmgServicesRepo; final BookAppointmentsRepo bookAppointmentsRepo; final ErrorHandlerService errorHandlerService; + final NavigationService navigationService; + HmgServicesViewModel({required this.bookAppointmentsRepo, required this.hmgServicesRepo, required this.errorHandlerService, required this.navigationService}); - HmgServicesViewModel({required this.bookAppointmentsRepo, required this.hmgServicesRepo, required this.errorHandlerService}); bool isCmcOrdersLoading = false; bool isCmcServicesLoading = false; @@ -51,8 +56,9 @@ class HmgServicesViewModel extends ChangeNotifier { List relationTypes =[]; List getAllCitiesList =[]; + List searchReferralList =[]; - Future getOrdersList() async { + Future getOrdersList() async {} // HHC multiple services selection List selectedHhcServices = []; @@ -702,4 +708,84 @@ class HmgServicesViewModel extends ChangeNotifier { ); } + + + Future searchEReferral({ + required SearchEReferralRequestModel requestModel, + Function(dynamic)? onSuccess, + Function(String)? onError, + }) async { + + notifyListeners(); + + final result = await hmgServicesRepo.searchEReferral(requestModel); + + result.fold( + (failure) async { + + notifyListeners(); + await errorHandlerService.handleError(failure: failure); + if (onError != null) { + onError(failure.toString()); + } + }, + (apiResponse) { + + if (apiResponse.messageStatus == 1) { + searchReferralList = apiResponse.data ?? []; + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } else { + notifyListeners(); + if (onError != null) { + onError(apiResponse.errorMessage ?? 'Unknown error'); + } + } + }, + ); + } + + Future navigateToOTPScreen( + {required OTPTypeEnum otpTypeEnum, + required String phoneNumber, + required String loginToken, + required Function onSuccess, + }) async { + + navigationService.pushToOtpScreen( + phoneNumber: phoneNumber, + isFormFamilyFile: false, + checkActivationCode: (int activationCode) async { + + checkEReferralActivationCode( + requestModel: CheckActivationCodeForEReferralRequestModel( + logInTokenID: loginToken, + activationCode: activationCode.toString(), + ), + onSuccess: (GenericApiModel response) { + onSuccess(); + }, + onError: (String errorMessage) { + print(errorMessage); + }, + ); + }, + onResendOTPPressed: (String phoneNumber) async { + // await sendActivationCode( + // otpTypeEnum: otpTypeEnum, + // phoneNumber: phoneNumberController.text, + // nationalIdOrFileNumber: nationalIdController.text, + // isForRegister: isComingFromRegister, + // isComingFromResendOTP: true, + // payload: payload, + // isFormFamilyFile: isFormFamilyFile, + // isExcludedUser: isExcludedUser, + // responseID: responseID, + // ); + }, + ); + } + } diff --git a/lib/features/hmg_services/models/req_models/search_e_referral_req_model.dart b/lib/features/hmg_services/models/req_models/search_e_referral_req_model.dart new file mode 100644 index 0000000..deb3a92 --- /dev/null +++ b/lib/features/hmg_services/models/req_models/search_e_referral_req_model.dart @@ -0,0 +1,60 @@ +class SearchEReferralRequestModel { + String? patientMobileNumber; + double? versionID; + int? channel; + int? languageID; + String? iPAdress; + String? generalid; + int? patientOutSA; + dynamic sessionID; + bool? isDentalAllowedBackend; + int? deviceTypeID; + int? referralNumber; + String? identificationNo; + + SearchEReferralRequestModel( + {this.patientMobileNumber, + this.versionID, + this.channel, + this.languageID, + this.iPAdress, + this.generalid, + this.patientOutSA, + this.sessionID, + this.isDentalAllowedBackend, + this.deviceTypeID, + this.referralNumber, + this.identificationNo}); + + SearchEReferralRequestModel.fromJson(Map json) { + patientMobileNumber = json['PatientMobileNumber']; + versionID = json['VersionID']; + channel = json['Channel']; + languageID = json['LanguageID']; + iPAdress = json['IPAdress']; + generalid = json['generalid']; + patientOutSA = json['PatientOutSA']; + sessionID = json['SessionID']; + isDentalAllowedBackend = json['isDentalAllowedBackend']; + deviceTypeID = json['DeviceTypeID']; + referralNumber = json['ReferralNumber']; + identificationNo = json['IdentificationNo']; + } + + Map toJson() { + final Map data = new Map(); + data['PatientMobileNumber'] = this.patientMobileNumber; + data['VersionID'] = this.versionID; + data['Channel'] = this.channel; + data['LanguageID'] = this.languageID; + data['IPAdress'] = this.iPAdress; + data['generalid'] = this.generalid; + data['PatientOutSA'] = this.patientOutSA; + data['SessionID'] = this.sessionID; + data['isDentalAllowedBackend'] = this.isDentalAllowedBackend; + data['DeviceTypeID'] = this.deviceTypeID; + data['ReferralNumber'] = this.referralNumber; + data['IdentificationNo'] = this.identificationNo; + return data; + } +} diff --git a/lib/features/hmg_services/models/resq_models/search_e_referral_resp_model.dart b/lib/features/hmg_services/models/resq_models/search_e_referral_resp_model.dart new file mode 100644 index 0000000..df72930 --- /dev/null +++ b/lib/features/hmg_services/models/resq_models/search_e_referral_resp_model.dart @@ -0,0 +1,161 @@ +import 'dart:convert'; + +class SearchEReferralResponseModel { + dynamic acceptedBrachCode; + dynamic acceptedBranchName; + dynamic acceptedBranchNameAr; + dynamic channel; + dynamic identityCardAttachment; + String? identityNumber; + dynamic insuranceCardAttachment; + bool? isInsuredPatient; + List? medicalReportAttachment; + String? otherRelationship; + String? patientContactNo; + int? patientId; + String? patientName; + dynamic preferredBranchCode; + dynamic preferredBranchName; + String? referralDate; + int? referralNumber; + RelationshipType? relationshipType; + String? requesterContactNo; + String? requesterName; + String? status; + String? statusAr; + + SearchEReferralResponseModel({ + this.acceptedBrachCode, + this.acceptedBranchName, + this.acceptedBranchNameAr, + this.channel, + this.identityCardAttachment, + this.identityNumber, + this.insuranceCardAttachment, + this.isInsuredPatient, + this.medicalReportAttachment, + this.otherRelationship, + this.patientContactNo, + this.patientId, + this.patientName, + this.preferredBranchCode, + this.preferredBranchName, + this.referralDate, + this.referralNumber, + this.relationshipType, + this.requesterContactNo, + this.requesterName, + this.status, + this.statusAr, + }); + + factory SearchEReferralResponseModel.fromRawJson(String str) => SearchEReferralResponseModel.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory SearchEReferralResponseModel.fromJson(Map json) => SearchEReferralResponseModel( + acceptedBrachCode: json["AcceptedBrachCode"], + acceptedBranchName: json["AcceptedBranchName"], + acceptedBranchNameAr: json["AcceptedBranchNameAr"], + channel: json["Channel"], + identityCardAttachment: json["IdentityCardAttachment"], + identityNumber: json["IdentityNumber"], + insuranceCardAttachment: json["InsuranceCardAttachment"], + isInsuredPatient: json["IsInsuredPatient"], + medicalReportAttachment: json["MedicalReportAttachment"] == null ? [] : List.from(json["MedicalReportAttachment"]!.map((x) => MedicalReportAttachment.fromJson(x))), + otherRelationship: json["OtherRelationship"], + patientContactNo: json["PatientContactNo"], + patientId: json["PatientId"], + patientName: json["PatientName"], + preferredBranchCode: json["PreferredBranchCode"], + preferredBranchName: json["PreferredBranchName"], + referralDate: json["ReferralDate"], + referralNumber: json["ReferralNumber"], + relationshipType: json["RelationshipType"] == null ? null : RelationshipType.fromJson(json["RelationshipType"]), + requesterContactNo: json["RequesterContactNo"], + requesterName: json["RequesterName"], + status: json["Status"], + statusAr: json["StatusAr"], + ); + + Map toJson() => { + "AcceptedBrachCode": acceptedBrachCode, + "AcceptedBranchName": acceptedBranchName, + "AcceptedBranchNameAr": acceptedBranchNameAr, + "Channel": channel, + "IdentityCardAttachment": identityCardAttachment, + "IdentityNumber": identityNumber, + "InsuranceCardAttachment": insuranceCardAttachment, + "IsInsuredPatient": isInsuredPatient, + "MedicalReportAttachment": medicalReportAttachment == null ? [] : List.from(medicalReportAttachment!.map((x) => x.toJson())), + "OtherRelationship": otherRelationship, + "PatientContactNo": patientContactNo, + "PatientId": patientId, + "PatientName": patientName, + "PreferredBranchCode": preferredBranchCode, + "PreferredBranchName": preferredBranchName, + "ReferralDate": referralDate, + "ReferralNumber": referralNumber, + "RelationshipType": relationshipType?.toJson(), + "RequesterContactNo": requesterContactNo, + "RequesterName": requesterName, + "Status": status, + "StatusAr": statusAr, + }; +} + +class MedicalReportAttachment { + String? base64String; + String? fileName; + + MedicalReportAttachment({ + this.base64String, + this.fileName, + }); + + factory MedicalReportAttachment.fromRawJson(String str) => MedicalReportAttachment.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory MedicalReportAttachment.fromJson(Map json) => MedicalReportAttachment( + base64String: json["Base64String"], + fileName: json["FileName"], + ); + + Map toJson() => { + "Base64String": base64String, + "FileName": fileName, + }; +} + +class RelationshipType { + int? id; + String? text; + String? textAr; + String? textEn; + + RelationshipType({ + this.id, + this.text, + this.textAr, + this.textEn, + }); + + factory RelationshipType.fromRawJson(String str) => RelationshipType.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory RelationshipType.fromJson(Map json) => RelationshipType( + id: json["ID"], + text: json["Text"], + textAr: json["Text_Ar"], + textEn: json["Text_En"], + ); + + Map toJson() => { + "ID": id, + "Text": text, + "Text_Ar": textAr, + "Text_En": textEn, + }; +} diff --git a/lib/features/hmg_services/models/ui_models/e_referral_form_model.dart b/lib/features/hmg_services/models/ui_models/e_referral_form_model.dart index fded059..32964b1 100644 --- a/lib/features/hmg_services/models/ui_models/e_referral_form_model.dart +++ b/lib/features/hmg_services/models/ui_models/e_referral_form_model.dart @@ -9,7 +9,10 @@ import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/ class ReferralFormData { String requesterName = ''; String requesterPhone = ''; + String searchPhone =''; CountryEnum countryEnum = CountryEnum.saudiArabia; + CountryEnum patientCountryEnum = CountryEnum.saudiArabia; + GetAllRelationshipTypeResponseModel? relationship; String otherRelationshipName = ''; @@ -39,4 +42,7 @@ class FormValidationErrors { String? medicalReport; String? branch; String? insuredDocument; + + String? searchValue; + String? searchPhone; } \ No newline at end of file diff --git a/lib/presentation/e_referral/e-referral_validator.dart b/lib/presentation/e_referral/e-referral_validator.dart index 78efb49..3e3399d 100644 --- a/lib/presentation/e_referral/e-referral_validator.dart +++ b/lib/presentation/e_referral/e-referral_validator.dart @@ -10,12 +10,12 @@ class ReferralValidator { errors.requesterName = 'Referral requester name is required'; } - if (formData.requesterPhone.trim().isEmpty) { - errors.requesterPhone = 'Phone number is required'; - } else if (formData.countryEnum.countryCode == '966' && - !_isValidSaudiPhone(formData.requesterPhone)) { - errors.requesterPhone = 'Please enter a valid Saudi phone number (5xxxxxxxx)'; - } + // if (formData.requesterPhone.trim().isEmpty) { + // errors.requesterPhone = 'Phone number is required'; + // } else if (formData.countryEnum.countryCode == '966' && + // !_isValidSaudiPhone(formData.requesterPhone)) { + // errors.requesterPhone = 'Please enter a valid Saudi phone number (5xxxxxxxx)'; + // } if (formData.relationship == null) { errors.relationship = 'Please select a relationship'; diff --git a/lib/presentation/e_referral/e_referral_form_manager.dart b/lib/presentation/e_referral/e_referral_form_manager.dart index a69a2fe..f7093c5 100644 --- a/lib/presentation/e_referral/e_referral_form_manager.dart +++ b/lib/presentation/e_referral/e_referral_form_manager.dart @@ -16,7 +16,16 @@ class ReferralFormManager extends ChangeNotifier { ReferralFormData get formData => _formData; FormValidationErrors get errors => _errors; - // Field-specific update methods that don't notify listeners immediately + + int _searchCriteria = 0; // 0 = Identification Number, 1 = Referral Number + String? _searchValue; + String? _searchPhone; + +// Getters + int get searchCriteria => _searchCriteria; + String? get searchValue => _searchValue; + String? get searchPhone => _searchPhone; + void updateRequesterName(String value) { _formData.requesterName = value; _clearError('requesterName'); @@ -30,9 +39,13 @@ class ReferralFormManager extends ChangeNotifier { void updateCountryEnum(CountryEnum value) { _formData.countryEnum = value; } + void updatePatientCountryEnum(CountryEnum value) { + _formData.patientCountryEnum = value; + } void updateRelationship(GetAllRelationshipTypeResponseModel? value) { _formData.relationship = value; + notifyListeners(); _clearError('relationship'); } @@ -59,6 +72,7 @@ class ReferralFormManager extends ChangeNotifier { void updatePatientCity(GetAllCitiesResponseModel? value) { _formData.patientCity = value; _clearError('patientCity'); + notifyListeners(); } void updateBranch(HospitalsModel? value) { @@ -83,6 +97,7 @@ class ReferralFormManager extends ChangeNotifier { if (index >= 0 && index < _formData.medicalReportImages.length) { _formData.medicalReportImages.removeAt(index); } + notifyListeners(); } void addInsuranceDocument(EReferralAttachment attachment) { @@ -94,8 +109,11 @@ class ReferralFormManager extends ChangeNotifier { if (index >= 0 && index < _formData.insuredPatientImages.length) { _formData.insuredPatientImages.removeAt(index); } + + notifyListeners(); } + // Error management void setErrors(FormValidationErrors newErrors) { _errors.requesterName = newErrors.requesterName; @@ -203,4 +221,70 @@ class ReferralFormManager extends ChangeNotifier { notifyListeners(); } } + void updateSearchCriteria(int criteria) { + _searchCriteria = criteria; + _errors.searchValue = _validateSearchValue(); + notifyListeners(); + } + + void updateSearchValue(String value) { + _searchValue = value; + _errors.searchValue = _validateSearchValue(); + } + void updateSearchPhone(String value) { + _searchPhone = value; + _errors.searchPhone = _validateSearchPhone(); + } + + String? _validateSearchValue() { + if (_searchValue == null || _searchValue!.isEmpty) { + return _searchCriteria == 0 + ? 'Identification Number is required' + : 'Referral Number is required'; + } + + if (_searchCriteria == 0) { + // Validate identification number format + if (!_isValidIdentificationNumber(_searchValue!)) { + return 'Please enter a valid identification number'; + } + } else { + // Validate referral number format + if (!_isValidReferralNumber(_searchValue!)) { + return 'Please enter a valid referral number'; + } + } + + return null; + } + + bool _isValidIdentificationNumber(String value) { + // Add your identification number validation logic + return value.isNotEmpty; // Basic validation + } + + bool _isValidReferralNumber(String value) { + // Add your referral number validation logic + return value.isNotEmpty; // Basic validation + } + + void validateSearchForm() { + _errors.searchValue = _validateSearchValue(); + _errors.searchPhone = _validateSearchPhone(); + notifyListeners(); + } + + String? _validateSearchPhone() { + if (_searchPhone == null || _searchPhone!.isEmpty) { + return 'Phone number is required'; + } + return null; + } + bool get isSearchFormInValid { + return ( + _errors.searchValue != null && + _errors.searchValue!.isNotEmpty || + _errors.searchPhone != null && + _errors.searchPhone!.isNotEmpty); + } } \ No newline at end of file diff --git a/lib/presentation/e_referral/e_referral_page_home.dart b/lib/presentation/e_referral/e_referral_page_home.dart deleted file mode 100644 index d41defb..0000000 --- a/lib/presentation/e_referral/e_referral_page_home.dart +++ /dev/null @@ -1,103 +0,0 @@ -import 'dart:ui'; -import 'package:easy_localization/easy_localization.dart'; -import 'package:flutter/material.dart'; -import 'package:hmg_patient_app_new/core/app_export.dart'; -import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; -import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; -import 'package:hmg_patient_app_new/features/contact_us/contact_us_view_model.dart'; -import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; -import 'package:hmg_patient_app_new/presentation/e_referral/new_e_referral.dart'; -import 'package:hmg_patient_app_new/presentation/e_referral/search_e_referral.dart'; -import 'package:hmg_patient_app_new/theme/colors.dart'; -import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; -import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; -import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart'; -import 'package:provider/provider.dart'; - -class EReferralPage extends StatefulWidget { - const EReferralPage({super.key}); - - @override - _EReferralPageState createState() => _EReferralPageState(); -} - -class _EReferralPageState extends State { - @override - void initState() { - super.initState(); - } - - @override - void dispose() { - super.dispose(); - } - - bool isNewReferral = true; - VoidCallback? onNextStep; - int _currentPageIndex = 0; - int activeTabIndex = 0; - @override - Widget build(BuildContext context) { - return Scaffold( - backgroundColor: AppColors.bgScaffoldColor, - body: CollapsingListView( - title: "E Referral".needTranslation, - child: Consumer(builder: (context, contactUsVM, child) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox(height: 16.h), - CustomTabBar( - activeTextColor: AppColors.primaryRedColor, - activeBackgroundColor: AppColors.primaryRedColor.withValues(alpha: .1), - tabs: [ - CustomTabBarModel(null, "New Referral".needTranslation), - CustomTabBarModel(null, "Search Referral".needTranslation), - ], - onTabChange: (index) { - activeTabIndex =index; - setState(() { - - }); - }, - ).paddingSymmetrical(24.h, 0.h), - SizedBox(height: 24.h), - activeTabIndex ==0 ? NewReferralPage( - onNextStep: (nextStep) { - WidgetsBinding.instance.addPostFrameCallback((_) { - setState(() { - onNextStep = nextStep; - }); - }); - }, - onStepChanged: (int value) { - setState(() { - _currentPageIndex = value; - }); - }, - ) : - SearchEReferralPage( - onNextStep: (onNextStep) { - WidgetsBinding.instance.addPostFrameCallback((_) { - setState(() { - onNextStep = onNextStep; - }); - }); - }, - ) - ], - ); - }), - ), - bottomNavigationBar: Padding( - padding: EdgeInsets.all(20.h), - child: CustomButton( - text:activeTabIndex == 0 ? _currentPageIndex >= 2 ? LocaleKeys.submit.tr() : LocaleKeys.next.tr() : LocaleKeys.search.tr(), - onPressed: onNextStep ?? () {}, - backgroundColor: AppColors.primaryRedColor, - borderColor: AppColors.primaryRedColor, - textColor: AppColors.whiteColor, - )), - ); - } -} diff --git a/lib/presentation/e_referral/e_referral_search_result.dart b/lib/presentation/e_referral/e_referral_search_result.dart new file mode 100644 index 0000000..3f3966e --- /dev/null +++ b/lib/presentation/e_referral/e_referral_search_result.dart @@ -0,0 +1,310 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_export.dart'; +import 'package:hmg_patient_app_new/core/utils/date_util.dart'; +import 'package:hmg_patient_app_new/core/utils/utils.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart'; +import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/search_e_referral_resp_model.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; +import 'package:provider/provider.dart'; +import 'package:smooth_corner/smooth_corner.dart'; + +class SearchResultPage extends StatefulWidget { + const SearchResultPage({super.key}); + + @override + _SearchResultPageState createState() => _SearchResultPageState(); +} + +class _SearchResultPageState extends State { + HmgServicesViewModel? hmgServicesVM; + + String _selectedFilter = 'All'; + + @override + void initState() { + hmgServicesVM = context.read(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return CollapsingListView( + title: "Search Result".needTranslation, + child: Column( + children: [ + // List of referrals + ListView.builder( + padding: EdgeInsets.zero, + shrinkWrap: true, + itemCount: hmgServicesVM?.searchReferralList.length, + itemBuilder: (context, index) { + return _buildReferralCard(hmgServicesVM!.searchReferralList[index]); + }, + ), + ], + ), + ); + } + + Widget _buildReferralCard(SearchEReferralResponseModel referral) { + return SmoothCard( + borderRadius: BorderRadius.circular(16.h), + margin: EdgeInsets.symmetric(horizontal: 16, vertical: 8), + color: AppColors.whiteColor, + child: Padding( + padding: EdgeInsets.all(16.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + + 'Referral No ${referral.referralNumber}'.needTranslation.toText18(isBold: true, color: AppColors.textColor), + + + Container( + padding: EdgeInsets.symmetric(horizontal: 12, vertical: 6), + decoration: BoxDecoration( + color: _getStatusColor(referral.status!), + borderRadius: BorderRadius.circular(20), + ), + child: + referral.status!.toText12(color: AppColors.whiteColor, + // style: TextStyle( + // color: Colors.white, + // fontSize: 12, + // fontWeight: FontWeight.w500, + // ), + ), + ), + ], + ), + + SizedBox(height: 16), + + // Patient information + Row( + children: [ + Container( + width: 50, + height: 50, + decoration: BoxDecoration( + color: AppColors.lightGrayBGColor, + shape: BoxShape.circle, + ), + child: Icon( + Icons.person, + color: AppColors.lightGrayBGColor, + size: 30, + ), + ), + SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + //Text( + referral.patientName!.toText16(isBold: true, color: AppColors.textColor), + // style: TextStyle( + // fontSize: 16, + // fontWeight: FontWeight.bold, + // ), + // ), + SizedBox(height: 4), + // Text( + 'ID: ${referral.identityNumber}'.toText14(color: AppColors.greyTextColor), + // style: TextStyle( + // color: Colors.grey[600], + // ), + // ), + ], + ), + ), + ], + ), + + SizedBox(height: 16), + + // Details row + Row( + children: [ + _buildDetailItem( + Icons.phone, + referral.patientContactNo!, + ), + SizedBox(width: 16), + _buildDetailItem(Icons.calendar_today, Utils.getDayMonthYearDateFormatted(DateUtil.convertStringToDateNoTimeZone(referral.referralDate!))), + ], + ), + + SizedBox(height: 16), + + // Requester information + Container( + padding: EdgeInsets.all(5.h), + decoration: BoxDecoration( + color: AppColors.lightGrayBGColor, + borderRadius: BorderRadius.circular(8), + ), + child: Row( + children: [ + Icon( + Icons.person_outline, + color: Colors.grey[600], + size: 20, + ), + SizedBox(width: 8), + //Text( + 'Requester: ${referral.requesterName}'.toText14(), + // style: TextStyle( + // color: Colors.grey[700], + // ), + // ), + SizedBox(width: 16), + Icon( + Icons.phone, + color: Colors.grey[600], + size: 16, + ), + SizedBox(width: 4), + // Text( + referral.requesterContactNo!.toText14(), + // style: TextStyle( + // color: Colors.grey[700], + // ), + // ), + ], + ), + ), + + SizedBox(height: 12), + + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Icon( + Icons.group, + color: Colors.grey[600], + size: 16, + ), + SizedBox(width: 4), + //Text( + 'Relationship: ${referral.relationshipType?.text}'.toText14(color:AppColors.greyTextColor), + // style: TextStyle( + // color: Colors.grey[700], + // fontSize: 14, + // ), + // ), + ], + ), + Row( + children: [ + Icon( + Icons.attach_file, + color: Colors.grey[600], + size: 16, + ), + SizedBox(width: 4), + // Text( + '${referral.medicalReportAttachment?.length} file(s)'.toText14(color:AppColors.greyTextColor), + // style: TextStyle( + // color: Colors.grey[700], + // fontSize: 14, + // ), + // ), + ], + ), + ], + ), + + SizedBox(height: 16), + + // Action buttons + ], + ), + ), + ); + } + + Widget _buildDetailItem(IconData icon, String text) { + return Row( + children: [ + Icon( + icon, + color: Colors.grey[600], + size: 16, + ), + SizedBox(width: 4), + // Text( + text.toText14(color: AppColors.greyTextColor), + // style: TextStyle( + // color: Colors.grey[700], + // ), + // ), + ], + ); + } + + Color _getStatusColor(String status) { + switch (status) { + case 'Pending': + return AppColors.alertColor; + case 'Completed': + return AppColors.bgGreenColor; + case 'Rejected': + return AppColors.primaryRedColor; + default: + return AppColors.lightGrayColor; + } + } + + void _showFilterOptions() { + showModalBottomSheet( + context: context, + builder: (context) { + return Container( + padding: EdgeInsets.all(16), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + 'Filter Results', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 16), + _buildFilterOption('All'), + _buildFilterOption('Pending'), + _buildFilterOption('Completed'), + _buildFilterOption('Rejected'), + ], + ), + ); + }, + ); + } + + Widget _buildFilterOption(String filter) { + return ListTile( + leading: Icon( + _selectedFilter == filter ? Icons.radio_button_checked : Icons.radio_button_off, + color: _selectedFilter == filter ? Colors.blue[700] : Colors.grey, + ), + title: Text(filter), + onTap: () { + setState(() { + _selectedFilter = filter; + }); + Navigator.pop(context); + }, + ); + } +} diff --git a/lib/presentation/e_referral/new_e_referral.dart b/lib/presentation/e_referral/new_e_referral.dart index ca96153..e48d8ca 100644 --- a/lib/presentation/e_referral/new_e_referral.dart +++ b/lib/presentation/e_referral/new_e_referral.dart @@ -1,27 +1,35 @@ - +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_export.dart'; +import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart'; +import 'package:hmg_patient_app_new/core/utils/utils.dart'; +import 'package:hmg_patient_app_new/extensions/route_extensions.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart'; import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/create_e_referral_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/ui_models/e_referral_form_model.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/presentation/e_referral/search_e_referral.dart'; import 'package:hmg_patient_app_new/presentation/e_referral/widget/e-referral_otp.dart'; import 'package:hmg_patient_app_new/presentation/e_referral/widget/e_referral_other_details.dart'; import 'package:hmg_patient_app_new/presentation/e_referral/widget/e_referral_patient_info.dart'; import 'package:hmg_patient_app_new/presentation/e_referral/widget/e_referral_requester_form.dart'; -import 'package:hmg_patient_app_new/presentation/e_referral/widget/e_referral_stepper.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; +import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; +import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; +import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; +import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; +import 'package:hmg_patient_app_new/widgets/stepper/stepper_widget.dart'; import 'package:provider/provider.dart'; import 'e-referral_validator.dart'; import 'e_referral_form_manager.dart'; class NewReferralPage extends StatefulWidget { - final Function(VoidCallback) onNextStep; - final Function(int) onStepChanged; - const NewReferralPage({ super.key, - required this.onNextStep, - required this.onStepChanged, }); @override @@ -33,17 +41,14 @@ class _NewReferralPageState extends State { final ReferralFormManager _formManager = ReferralFormManager(); // Use manager int _currentStep = 0; - final List _steps = [ - 'Requester Info', - 'Patient Information', - 'Other details' - ]; + double widthOfOneState = ((ResponsiveExtension.screenWidth) / 3) - (20.h); + + final List _steps = ['Requester Info', 'Patient Information', 'Other details']; @override void initState() { super.initState(); _loadData(); - widget.onNextStep(_handleNextStep); } void _handleNextStep() { @@ -52,8 +57,7 @@ class _NewReferralPageState extends State { if (_validateCurrentStep()) { OTPService.openOTPScreen( context: context, - phoneNumber: _formManager.formData.requesterPhone ?? '', - countryEnum: _formManager.formData.countryEnum, + formManager: _formManager, onSuccess: _proceedToNextStep, ); } @@ -99,44 +103,45 @@ class _NewReferralPageState extends State { duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, ); - setState(() { - _currentStep++; - }); - widget.onStepChanged(_currentStep); + // setState(() { + _currentStep++; + // }); + // widget.onStepChanged(_currentStep); } void _submitReferral() { - - CreateEReferralRequestModel createReferrralRequestModel = - CreateEReferralRequestModel( + CreateEReferralRequestModel createReferralRequestModel = CreateEReferralRequestModel( isInsuredPatient: _formManager.formData.isPatientInsured, - cityCode: _formManager.formData.patientCity!.iD!.toString(), - cityName: _formManager.formData.patientCity!.description, + cityCode: _formManager.formData.patientCity!.iD!.toString(), + cityName: _formManager.formData.patientCity!.description, requesterName: _formManager.formData.requesterName, - requesterContactNo: _formManager.formData.requesterPhone, + requesterContactNo: _formManager.formData.countryEnum.countryCode + _formManager.formData.requesterPhone, requesterRelationship: _formManager.formData.relationship?.iD, otherRelationship: _formManager.formData.relationship!.iD.toString(), fullName: _formManager.formData.patientName, identificationNo: int.tryParse(_formManager.formData!.patientIdentification ?? '0'), - patientMobileNumber: _formManager.formData.patientPhone, + patientMobileNumber: _formManager.formData.patientCountryEnum.countryCode + _formManager.formData.patientPhone, preferredBranchCode: _formManager.formData.branch!.iD, medicalReportAttachment: _formManager.formData.medicalReportImages, insuranceCardAttachment: _formManager.formData.insuredPatientImages, - preferredBranchName: _formManager.formData.branch!.desciption - ); + preferredBranchName: _formManager.formData.branch!.desciption); final hmgServicesVM = context.read(); + LoaderBottomSheet.showLoader(); + hmgServicesVM.createEReferral( - requestModel: createReferrralRequestModel, - onSuccess: (response) { - print("E-Referral submitted successfully"); + requestModel: createReferralRequestModel, + onSuccess: (GenericApiModel response) { + + showSuccessBottomSheet(int.parse(response.data), hmgServicesVM); + LoaderBottomSheet.hideLoader(); + }, onError: (errorMessage) { // Handle error (e.g., show error message) print(errorMessage); }, ); - } void _loadData() { @@ -152,32 +157,107 @@ class _NewReferralPageState extends State { @override Widget build(BuildContext context) { - return ChangeNotifierProvider.value( - value: _formManager, - child: SizedBox( - height: MediaQuery.of(context).size.height, + return Scaffold( + backgroundColor: AppColors.bgScaffoldColor, + body: CollapsingListView( + title: "E Referral".needTranslation, + isClose: false, + search: () async { + await Navigator.of(context).push( + CustomPageRoute( + page: SearchEReferralPage(), + fullScreenDialog: true, + direction: AxisDirection.down, + ), + ); + }, + bottomChild: Container( + color: Colors.white, + padding: EdgeInsets.all(ResponsiveExtension(20).h), + child: CustomButton( + text: _currentStep <=1 ? LocaleKeys.next.tr() : LocaleKeys.submit.tr(), + // icon: AppAssets.search_icon, + iconColor: Colors.white, + onPressed: () => {_handleNextStep()}, + ), + ), + child: ChangeNotifierProvider.value( + value: _formManager, + child: SizedBox( + height: ResponsiveExtension.screenHeight * 0.65, + child: Column( + children: [ + const SizedBox(height: 8), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: List.generate(3, (index) { + if (_currentStep == index) { + return StepperWidget(widthOfOneState, AppColors.primaryRedColor, true, 4.h); + } else { + return StepperWidget(widthOfOneState, AppColors.greyLightColor, false, 4.h); + } + })), + Expanded( + child: PageView( + controller: _pageController, + physics: const NeverScrollableScrollPhysics(), + onPageChanged: (index) => { + // setState(() => _currentStep = index) + }, + children: [ + RequesterFormStep(), + PatientInformationStep(), + OtherDetailsStep(), + ], + )), + ], + )), + ))); + // ); + } + + + showSuccessBottomSheet(int requestId, HmgServicesViewModel hmgServicesViewModel) { + return showCommonBottomSheetWithoutHeight( + context, + child: Padding( + padding: EdgeInsets.all(16.w), child: Column( children: [ - const SizedBox(height: 8), - ProgressStepperWidget( - currentStep: _currentStep, - steps: _steps, + Utils.getSuccessWidget(loadingText: "Your Referral has been created Successfully.".needTranslation), + Row( + children: [ + "Here is your Referral #: ".needTranslation.toText14( + color: AppColors.textColorLight, + weight: FontWeight.w500, + ), + SizedBox(width: 4.w), + ("$requestId").toText16(isBold: true), + ], ), - Expanded( - child: PageView( - controller: _pageController, - physics: const NeverScrollableScrollPhysics(), - onPageChanged: (index) => setState(() => _currentStep = index), - children: [ - RequesterFormStep(), - PatientInformationStep(), - OtherDetailsStep(), - ], - ), + SizedBox(height: 24.h), + Row( + children: [ + Expanded( + child: CustomButton( + height: 56.h, + text: LocaleKeys.ok.tr(), + onPressed: () { + context.pop(); + context.pop(); + _currentStep =0; + }, + textColor: AppColors.whiteColor, + ), + ), + ], ), ], ), ), + isCloseButtonVisible: false, + isDismissible: false, + isFullScreen: false, ); } -} \ No newline at end of file +} diff --git a/lib/presentation/e_referral/search_e_referral.dart b/lib/presentation/e_referral/search_e_referral.dart index fabf115..ea749ee 100644 --- a/lib/presentation/e_referral/search_e_referral.dart +++ b/lib/presentation/e_referral/search_e_referral.dart @@ -1,19 +1,24 @@ - +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart'; -import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/create_e_referral_model.dart'; +import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/search_e_referral_req_model.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/e_referral/widget/search_e_referral_form.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; +import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; +import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; +import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:provider/provider.dart'; -import 'e-referral_validator.dart'; import 'e_referral_form_manager.dart'; +import 'e_referral_search_result.dart'; class SearchEReferralPage extends StatefulWidget { - final Function(VoidCallback) onNextStep; - const SearchEReferralPage({ super.key, - required this.onNextStep, }); @override @@ -21,78 +26,95 @@ class SearchEReferralPage extends StatefulWidget { } class _SearchEReferralPageState extends State { - final PageController _pageController = PageController(); - final ReferralFormManager _formManager = ReferralFormManager(); // Use manager - int _currentStep = 0; - - - + // final PageController _pageController = PageController(); + final ReferralFormManager _formManager = ReferralFormManager(); + late HmgServicesViewModel hmgServicesVM; @override void initState() { super.initState(); _loadData(); - widget.onNextStep(_handleNextStep); } - void _handleNextStep() { - _searchReferral(); + void _handleSearch() { + _formManager.validateSearchForm(); + if (!_formManager.isSearchFormInValid) { + _searchReferral(); } + } void _searchReferral() { + SearchEReferralRequestModel searchEReferralReq; - CreateEReferralRequestModel createReferrralRequestModel = - CreateEReferralRequestModel( - isInsuredPatient: _formManager.formData.isPatientInsured, - cityCode: _formManager.formData.patientCity!.iD!.toString(), - cityName: _formManager.formData.patientCity!.description, - requesterName: _formManager.formData.requesterName, - requesterContactNo: _formManager.formData.requesterPhone, - requesterRelationship: _formManager.formData.relationship?.iD, - otherRelationship: _formManager.formData.relationship!.iD.toString(), - fullName: _formManager.formData.patientName, - identificationNo: int.tryParse(_formManager.formData!.patientIdentification ?? '0'), - patientMobileNumber: _formManager.formData.patientPhone, - preferredBranchCode: _formManager.formData.branch!.iD, - medicalReportAttachment: _formManager.formData.medicalReportImages, - insuranceCardAttachment: _formManager.formData.insuredPatientImages, - preferredBranchName: _formManager.formData.branch!.desciption - ); + if (_formManager.searchCriteria == 0) { + searchEReferralReq = SearchEReferralRequestModel( + identificationNo: _formManager.searchValue, + patientMobileNumber: _formManager.formData.countryEnum.countryCode + _formManager.searchPhone!, + referralNumber: 0, + ); + } else { - final hmgServicesVM = context.read(); - hmgServicesVM.createEReferral( - requestModel: createReferrralRequestModel, - onSuccess: (response) { - print("E-Referral submitted successfully"); - }, - onError: (errorMessage) { - // Handle error (e.g., show error message) - print(errorMessage); + searchEReferralReq = SearchEReferralRequestModel(referralNumber: int.parse(_formManager.searchValue!), patientMobileNumber: _formManager.formData.patientPhone, identificationNo: ''); + } + + hmgServicesVM = context.read(); + LoaderBottomSheet.showLoader(); + hmgServicesVM.searchEReferral( + requestModel: searchEReferralReq, + onSuccess: (response) async { + LoaderBottomSheet.hideLoader(); + await Navigator.of(context).push( + CustomPageRoute( + page: SearchResultPage(), + fullScreenDialog: true, + direction: AxisDirection.down, + ), + ); }, - ); + ); } void _loadData() { - + WidgetsBinding.instance.addPostFrameCallback((_) {}); } @override Widget build(BuildContext context) { - return ChangeNotifierProvider.value( - value: _formManager, - child: SizedBox( - height: MediaQuery.of(context).size.height, - child: Column( - children: [ - - Expanded( - child: + return CollapsingListView( + title: "Search E-Referral".needTranslation, + isClose: true, + bottomChild: Container( + color: Colors.white, + padding: EdgeInsets.all(ResponsiveExtension(20).h), + child: CustomButton( + text: LocaleKeys.search.tr(), + icon: AppAssets.search_icon, + iconColor: Colors.white, + onPressed: () => { + _handleSearch() + }, + ), + ), + child: ChangeNotifierProvider.value( + value: _formManager, + child: Column( + children: [ + _buildHeader(), SearchEReferralFormForm(), - ), - ], - ), + ], + )), + ); + } + + Widget _buildHeader() { + return Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [SizedBox(height: 8), 'Please enter the required information to search for an e-referral'.needTranslation.toText12()], ), ); } -} \ No newline at end of file + +} diff --git a/lib/presentation/e_referral/widget/e-referral_otp.dart b/lib/presentation/e_referral/widget/e-referral_otp.dart index 3fcac6c..c3f5872 100644 --- a/lib/presentation/e_referral/widget/e-referral_otp.dart +++ b/lib/presentation/e_referral/widget/e-referral_otp.dart @@ -1,8 +1,17 @@ // services/otp_service.dart +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/enums.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/core/utils/validation_utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/presentation/e_referral/e_referral_form_manager.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/bottomsheet/generic_bottom_sheet.dart'; +import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; +import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; import 'package:provider/provider.dart'; import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart'; @@ -14,92 +23,101 @@ import 'package:hmg_patient_app_new/features/authentication/widgets/otp_verifica class OTPService { static void openOTPScreen({ required BuildContext context, - required String phoneNumber, - required CountryEnum countryEnum, + required ReferralFormManager formManager, required Function onSuccess, }) { - final hmgServicesViewModel = context.read(); - - hmgServicesViewModel.eReferralSendActivationCode( - requestModel: SendActivationCodeForEReferralRequestModel( - patientMobileNumber: int.parse(phoneNumber), - zipCode: countryEnum.countryCode, - patientOutSA: countryEnum.countryCode == '966' ? 0 : 1, - ), - onSuccess: (GenericApiModel response) { - _showOTPVerificationSheet( - context: context, - phoneNumber: phoneNumber, - loginTokenID: response.data, - onSuccess: onSuccess, - ); - }, - onError: (String errorMessage) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(errorMessage)) - ); - }, - ); + + + _showOTPVerificationSheet(context: context, formManager: formManager , onSuccess: onSuccess); + // + // LoaderBottomSheet.showLoader(); + } static void _showOTPVerificationSheet({ required BuildContext context, - required String phoneNumber, - required String loginTokenID, + required ReferralFormManager formManager, required Function onSuccess, }) { - showCommonBottomSheet( - context, - isFullScreen: true, - title: "OTP Verification", - isCloseButtonVisible: false, - height:ResponsiveExtension.screenHeight * 0.75, - child: OTPVerificationScreen( - phoneNumber: phoneNumber, - checkActivationCode: (int code) { - _verifyOTP( - context: context, - loginTokenID: loginTokenID, - code: code, - onSuccess: onSuccess, - ); - }, - onResendOTPPressed: (String phoneNumber) { - Navigator.pop(context); - openOTPScreen( - context: context, - phoneNumber: phoneNumber, - countryEnum: CountryEnum.saudiArabia, - onSuccess: onSuccess, - ); - }, - isFormFamilyFile: false, - ), - ); - } - static void _verifyOTP({ - required BuildContext context, - required String loginTokenID, - required int code, - required Function onSuccess, - }) { - final hmgServicesViewModel = context.read(); - - hmgServicesViewModel.checkEReferralActivationCode( - requestModel: CheckActivationCodeForEReferralRequestModel( - logInTokenID: loginTokenID, - activationCode: code.toString(), - ), - onSuccess: (GenericApiModel response) { - Navigator.pop(context); - onSuccess(); - }, - onError: (String errorMessage) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(errorMessage)) - ); - }, + showModalBottomSheet( + context: context, + isScrollControlled: true, + isDismissible: false, + useSafeArea: true, + backgroundColor: Colors.transparent, + builder: (bottomSheetContext) => Padding( + padding: EdgeInsets.only(bottom: MediaQuery.of(bottomSheetContext).viewInsets.bottom), + child: SingleChildScrollView( + child: GenericBottomSheet( + isEnableCountryDropdown:true, + textController: TextEditingController(), + onChange: (value) { + formManager.updateRequesterPhone(value ?? ''); + }, + onCountryChange: (value) { + formManager.updateCountryEnum(value); + }, + autoFocus: true, + buttons: [ + Padding( + padding: const EdgeInsets.only(bottom: 10), + child: CustomButton( + text: LocaleKeys.sendOTPSMS.tr(), + onPressed: () async { + + if (ValidationUtils.isValidatePhone( + phoneNumber: formManager.formData.requesterPhone, + onOkPress: () { + + Navigator.pop(context); + + + }, + )) { + Navigator.pop(context); + final hmgServicesViewModel = context.read(); + + LoaderBottomSheet.showLoader(); + hmgServicesViewModel.eReferralSendActivationCode( + requestModel: SendActivationCodeForEReferralRequestModel( + patientMobileNumber: int.parse(formManager.formData.requesterPhone), + zipCode: formManager.formData.countryEnum.countryCode, + patientOutSA: formManager.formData.countryEnum.countryCode == '966' ? 0 : 1, + ), + onSuccess: (GenericApiModel response) { + LoaderBottomSheet.hideLoader(); + hmgServicesViewModel.navigateToOTPScreen(otpTypeEnum: OTPTypeEnum.sms, phoneNumber: formManager.formData.requesterPhone, loginToken:response.data , onSuccess: (){ + Navigator.pop(context); + onSuccess(); + }); + + }, + onError: (String errorMessage) { + LoaderBottomSheet.hideLoader(); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(errorMessage)) + ); + }, + ); + + + } + }, + backgroundColor: AppColors.primaryRedColor, + borderColor: AppColors.primaryRedBorderColor, + textColor: AppColors.whiteColor, + icon: AppAssets.message, + ), + ), + + ], + ), + )), ); } -} \ No newline at end of file + + + + +} diff --git a/lib/presentation/e_referral/widget/e_referral_other_details.dart b/lib/presentation/e_referral/widget/e_referral_other_details.dart index d473dfe..cf8563d 100644 --- a/lib/presentation/e_referral/widget/e_referral_other_details.dart +++ b/lib/presentation/e_referral/widget/e_referral_other_details.dart @@ -1,8 +1,10 @@ // widgets/other_details_step.dart import 'dart:io'; - +import 'dart:convert'; import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_export.dart'; import 'package:hmg_patient_app_new/core/utils/validation_utils.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/create_e_referral_model.dart'; import 'package:hmg_patient_app_new/presentation/e_referral/e_referral_form_manager.dart'; @@ -55,6 +57,7 @@ class _OtherDetailsStepState extends State { _insuranceController.text = hasInsuranceDocs ? '${_formManager.formData.insuredPatientImages.length} file(s) selected' : ''; + } @override @@ -105,7 +108,7 @@ class _OtherDetailsStepState extends State { onTap: () { ImageOptions.showImageOptionsNew( context, - true, + false, (String image, File file) { _addMedicalReport(image, file, formManager); }, @@ -199,7 +202,7 @@ class _OtherDetailsStepState extends State { onTap: () { ImageOptions.showImageOptionsNew( context, - true, + false, (String image, File file) { _addInsuranceDocument(image, file, formManager); }, @@ -245,22 +248,28 @@ class _OtherDetailsStepState extends State { ); } - return ListView.separated( + return DecoratedBox( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: Colors.white, + customBorder: BorderRadius.all(Radius.circular(24.h)) , + + ), child: ListView.builder( shrinkWrap: true, + padding: EdgeInsets.all(16.h), physics: const BouncingScrollPhysics(), itemBuilder: (context, index) { final branch = hospitals[index]; return ListTile( - title: Text(branch.desciption ?? 'Unknown'), + title: (branch.desciption ?? 'Unknown').toText14(), onTap: () { formManager.updateBranch(branch); Navigator.pop(context); }, ); }, - separatorBuilder: (context, index) => const Divider(), + // separatorBuilder: (context, index) => const Divider(), itemCount: hospitals.length, - ); + )); }, ), useSafeArea: true, diff --git a/lib/presentation/e_referral/widget/e_referral_patient_info.dart b/lib/presentation/e_referral/widget/e_referral_patient_info.dart index 779804e..2bd8e92 100644 --- a/lib/presentation/e_referral/widget/e_referral_patient_info.dart +++ b/lib/presentation/e_referral/widget/e_referral_patient_info.dart @@ -1,6 +1,8 @@ // widgets/patient_information_step.dart import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_export.dart'; import 'package:hmg_patient_app_new/core/utils/validation_utils.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/presentation/e_referral/e_referral_form_manager.dart'; import 'package:provider/provider.dart'; @@ -160,7 +162,7 @@ class PatientInformationStepState extends State { formManager.updatePatientPhone(value ?? ''); }, onCountryChange: (value) { - formManager.updateCountryEnum(value); + formManager.updatePatientCountryEnum(value); }, prefix: '966', isBorderAllowed: false, @@ -192,50 +194,8 @@ class PatientInformationStepState extends State { }); } - // void _showCountryBottomSheet(BuildContext context, ReferralFormManager formManager) { - // final authVM = context.read(); - // - // showCommonBottomSheetWithoutHeight( - // context, - // title: "Select Country", - // child: Consumer( - // builder: (context, authVM, child) { - // final countries = authVM.countriesList; - // if (countries == null || countries.isEmpty) { - // return const Center( - // child: Padding( - // padding: EdgeInsets.all(16.0), - // child: Text('No countries available'), - // ), - // ); - // } - // - // return ListView.separated( - // shrinkWrap: true, - // physics: const BouncingScrollPhysics(), - // itemBuilder: (context, index) { - // final country = countries[index]; - // return ListTile( - // title: Text(country.name ?? 'Unknown'), - // onTap: () { - // formManager.updatePatientCountry(country); - // Navigator.pop(context); - // }, - // ); - // }, - // separatorBuilder: (context, index) => const Divider(), - // itemCount: countries.length, - // ); - // }, - // ), - // useSafeArea: true, - // isFullScreen: false, - // isCloseButtonVisible: true, - // ); - // } void _showCityBottomSheet(BuildContext context, ReferralFormManager formManager) { - final hmgServicesVM = context.read(); showCommonBottomSheetWithoutHeight( context, @@ -252,22 +212,28 @@ class PatientInformationStepState extends State { ); } - return ListView.separated( + return DecoratedBox( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: Colors.white, + customBorder: BorderRadius.all(Radius.circular(24.h)) , + + ), child: ListView.builder( shrinkWrap: true, + padding: EdgeInsets.all(16.h), physics: const BouncingScrollPhysics(), itemBuilder: (context, index) { final city = cities[index]; return ListTile( - title: Text(city.description ?? 'Unknown'), + title: (city.description ?? 'Unknown').toText14(), onTap: () { formManager.updatePatientCity(city); Navigator.pop(context); }, ); }, - separatorBuilder: (context, index) => const Divider(), + // separatorBuilder: (context, index) => const Divider(), itemCount: cities.length, - ); + )); }, ), useSafeArea: true, diff --git a/lib/presentation/e_referral/widget/e_referral_requester_form.dart b/lib/presentation/e_referral/widget/e_referral_requester_form.dart index b6952a5..5f9e4d8 100644 --- a/lib/presentation/e_referral/widget/e_referral_requester_form.dart +++ b/lib/presentation/e_referral/widget/e_referral_requester_form.dart @@ -1,6 +1,8 @@ // widgets/requester_form_step.dart import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_export.dart'; import 'package:hmg_patient_app_new/core/utils/validation_utils.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/ui_models/e_referral_form_model.dart'; import 'package:hmg_patient_app_new/presentation/e_referral/e-referral_validator.dart'; @@ -63,15 +65,17 @@ class RequesterFormStepState extends State { return Consumer( builder: (context, formManager, child) { return Padding( - padding: const EdgeInsets.symmetric(horizontal: 24.0), + padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 0.0), child: ListView( + padding: EdgeInsets.zero, + shrinkWrap: true, physics: const BouncingScrollPhysics(), children: [ - const SizedBox(height: 12), + // const SizedBox(height: 12), _buildSectionTitle('Referral requester information'), - const SizedBox(height: 12), + const SizedBox(height: 12), _buildNameField(formManager), - _buildPhoneField(formManager), + // _buildPhoneField(formManager), _buildRelationshipField(context, formManager), if (_showOtherNameField(formManager)) _buildOtherNameField(formManager), ], @@ -98,43 +102,43 @@ class RequesterFormStepState extends State { labelText: 'Requester Name', keyboardType: TextInputType.text, errorMessage: formManager.errors.requesterName, + isAllowLeadingIcon: true, + leadingIcon: AppAssets.user_circle, hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.requesterName), onChange: (value) { formManager.updateRequesterName(value ?? ''); }, - onSubmitted: (value) { - _phoneFocusNode.requestFocus(); - }, - ).paddingSymmetrical(0, 8), - ); - } - Widget _buildPhoneField(ReferralFormManager formManager) { - return Focus( - focusNode: _phoneFocusNode, - child: TextInputWidget( - labelText: 'Phone Number', - hintText: "5xxxxxxxx", - controller: _phoneController, - padding: const EdgeInsets.all(8), - keyboardType: TextInputType.number, - onChange: (value) { - formManager.updateRequesterPhone(value ?? ''); - }, - onCountryChange: (value) { - formManager.updateCountryEnum(value); - }, - prefix: '966', - isBorderAllowed: false, - isAllowLeadingIcon: true, - fontSize: 13, - isCountryDropDown: true, - leadingIcon: AppAssets.smart_phone, - errorMessage: formManager.errors.requesterPhone, - hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.requesterPhone) ).paddingSymmetrical(0, 8), ); } + // + // Widget _buildPhoneField(ReferralFormManager formManager) { + // return Focus( + // focusNode: _phoneFocusNode, + // child: TextInputWidget( + // labelText: 'Phone Number', + // hintText: "5xxxxxxxx", + // controller: _phoneController, + // padding: const EdgeInsets.all(8), + // keyboardType: TextInputType.number, + // onChange: (value) { + // formManager.updateRequesterPhone(value ?? ''); + // }, + // onCountryChange: (value) { + // formManager.updateCountryEnum(value); + // }, + // prefix: '966', + // isBorderAllowed: false, + // isAllowLeadingIcon: true, + // fontSize: 13, + // isCountryDropDown: true, + // leadingIcon: AppAssets.smart_phone, + // errorMessage: formManager.errors.requesterPhone, + // hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.requesterPhone) + // ).paddingSymmetrical(0, 8), + // ); + // } Widget _buildRelationshipField(BuildContext context, ReferralFormManager formManager) { return DropdownWidget( @@ -159,6 +163,7 @@ class RequesterFormStepState extends State { focusNode: _otherNameFocusNode, child: TextInputWidget( controller: _otherNameController, + keyboardType: TextInputType.text, padding: const EdgeInsets.symmetric(horizontal: 16.0), hintText: 'Other Name*', labelText: 'Other Name', @@ -192,22 +197,28 @@ class RequesterFormStepState extends State { ); } - return ListView.separated( + return DecoratedBox( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: Colors.white, + customBorder: BorderRadius.all(Radius.circular(24.h)) , + + ), child: ListView.builder( shrinkWrap: true, + padding: EdgeInsets.all(16.h), physics: const BouncingScrollPhysics(), itemBuilder: (context, index) { final relationship = hmgServicesVM.relationTypes[index]; return ListTile( - title: Text(relationship.textEn ?? 'Unknown'), + title:relationship.textEn?.toText14(), onTap: () { formManager.updateRelationship(relationship); Navigator.pop(context); }, ); }, - separatorBuilder: (context, index) => const Divider(), + //separatorBuilder: (context, index) => const Divider(), itemCount: hmgServicesVM.relationTypes.length, - ); + )); }, ), isFullScreen: false, diff --git a/lib/presentation/e_referral/widget/e_referral_stepper.dart b/lib/presentation/e_referral/widget/e_referral_stepper.dart deleted file mode 100644 index a6cd771..0000000 --- a/lib/presentation/e_referral/widget/e_referral_stepper.dart +++ /dev/null @@ -1,72 +0,0 @@ -// widgets/progress_stepper_widget.dart -import 'package:flutter/material.dart'; -import 'package:hmg_patient_app_new/theme/colors.dart'; - -class ProgressStepperWidget extends StatelessWidget { - final int currentStep; - final List steps; - - const ProgressStepperWidget({ - super.key, - required this.currentStep, - required this.steps, - }); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 24.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - for (int i = 0; i < steps.length; i++) - _buildStep( - title: steps[i], - active: i == currentStep, - showDivider: i < steps.length - 1, - ), - ], - ), - ); - } - - Widget _buildStep({required String title, required bool active, bool showDivider = true}) { - final Color activeColor = active ? AppColors.primaryRedColor : Colors.grey.shade400; - - return Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - CircleAvatar( - radius: 13, - backgroundColor: active ? activeColor : Colors.grey.shade300, - child: Icon(Icons.check, size: 14, color: Colors.white), - ), - if (showDivider) - Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: Divider(thickness: 1), - ), - ], - ), - const SizedBox(height: 6), - Text(title, style: const TextStyle(fontSize: 11, fontWeight: FontWeight.w600)), - const SizedBox(height: 6), - Container( - padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4), - decoration: BoxDecoration( - color: active ? activeColor.withOpacity(0.15) : Colors.grey.shade100, - borderRadius: BorderRadius.circular(6), - ), - child: Text( - active ? 'Active' : 'Inactive', - style: TextStyle(fontSize: 9, color: active ? activeColor : Colors.grey) - ), - ), - ], - ), - ); - } -} \ No newline at end of file diff --git a/lib/presentation/e_referral/widget/search_e_referral_form.dart b/lib/presentation/e_referral/widget/search_e_referral_form.dart index 37d4025..21bada6 100644 --- a/lib/presentation/e_referral/widget/search_e_referral_form.dart +++ b/lib/presentation/e_referral/widget/search_e_referral_form.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/utils/validation_utils.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; @@ -12,8 +11,10 @@ import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_mode import 'package:hmg_patient_app_new/widgets/dropdown/dropdown_widget.dart'; import 'package:hmg_patient_app_new/widgets/input_widget.dart'; import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; + class SearchEReferralFormForm extends StatefulWidget { - const SearchEReferralFormForm({super.key}); + final VoidCallback? onFormValidated; + const SearchEReferralFormForm({super.key, this.onFormValidated}); @override State createState() => SearchEReferralFormFormState(); @@ -22,35 +23,25 @@ class SearchEReferralFormForm extends StatefulWidget { class SearchEReferralFormFormState extends State { late TextEditingController _searchController; late TextEditingController _phoneController; - late FocusNode _searchFocusNode; + late FocusNode _searchFocusNode; late FocusNode _phoneFocusNode; - int criteria =0; - - List> criteriaList = [ - {0: 'Identification Number'}, - {1: 'Referral Number'}, - ]; - late ReferralFormManager _formManager; - @override void initState() { super.initState(); - _formManager = context.read(); _searchController = TextEditingController(); _phoneController = TextEditingController(); _searchFocusNode = FocusNode(); _phoneFocusNode = FocusNode(); - // Initialize controllers with current values - _searchController.text = ''; - _phoneController.text = ''; - - // Auto-focus the name field when the step loads + // Initialize controllers with current values from form manager WidgetsBinding.instance.addPostFrameCallback((_) { if (mounted) { - _searchFocusNode.requestFocus(); + final formManager = context.read(); + _searchController.text = formManager.searchValue ?? ''; + _phoneController.text = formManager.searchPhone ?? ''; + // _searchFocusNode.requestFocus(); } }); } @@ -62,14 +53,13 @@ class SearchEReferralFormFormState extends State { return Padding( padding: const EdgeInsets.symmetric(horizontal: 24.0), child: ListView( - physics: const BouncingScrollPhysics(), + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), children: [ - - const SizedBox(height: 12), + // const SizedBox(height: 12), _buildSelectionField(context, formManager), _buildNameField(formManager), _buildPhoneField(formManager), - ], ), ); @@ -77,24 +67,21 @@ class SearchEReferralFormFormState extends State { ); } - - Widget _buildNameField(ReferralFormManager formManager) { return Focus( focusNode: _searchFocusNode, + autofocus: true, child: TextInputWidget( controller: _searchController, padding: const EdgeInsets.symmetric(horizontal: 16.0), - hintText:criteria ==0 ? "Enter Identification Number" : "Enter Referral Number", - labelText: criteria ==0 ? "Identification Number" : "Referral Number", - keyboardType: TextInputType.text, - // errorMessage: formManager.errors.requesterName, - hasError: !ValidationUtils.isNullOrEmpty(_searchController.text), + hintText: formManager.searchCriteria == 0 ? "Enter Identification Number" : "Enter Referral Number", + labelText: formManager.searchCriteria == 0 ? "Identification Number" : "Referral Number", + keyboardType: TextInputType.number, + errorMessage: formManager.errors.searchValue, + hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.searchValue), onChange: (value) { - - }, - onSubmitted: (value) { - + formManager.updateSearchValue(value ?? ''); + //_validateForm(formManager); }, ).paddingSymmetrical(0, 8), ); @@ -103,17 +90,25 @@ class SearchEReferralFormFormState extends State { Widget _buildPhoneField(ReferralFormManager formManager) { return Focus( focusNode: _phoneFocusNode, + autofocus: false, child: TextInputWidget( + autoFocus: false, labelText: 'Phone Number', hintText: "5xxxxxxxx", controller: _phoneController, padding: const EdgeInsets.all(8), keyboardType: TextInputType.number, onChange: (value) { - formManager.updateRequesterPhone(value ?? ''); + formManager.updateSearchPhone(value ?? ''); + // _validateForm(formManager); }, + // onSubmitted: (value) { + // formManager.updateRequesterPhone(value ?? ''); + // _validateForm(formManager); + // }, onCountryChange: (value) { formManager.updateCountryEnum(value); + // _validateForm(formManager); }, prefix: '966', isBorderAllowed: false, @@ -121,8 +116,8 @@ class SearchEReferralFormFormState extends State { fontSize: 13, isCountryDropDown: true, leadingIcon: AppAssets.smart_phone, - errorMessage: formManager.errors.requesterPhone, - hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.requesterPhone) + errorMessage: formManager.errors.searchPhone, + hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.searchPhone), ).paddingSymmetrical(0, 8), ); } @@ -130,7 +125,7 @@ class SearchEReferralFormFormState extends State { Widget _buildSelectionField(BuildContext context, ReferralFormManager formManager) { return DropdownWidget( labelText: "Select the Search Criteria", - hintText: criteria ==0 ? "Identification Number" : "Referral Number", + hintText: formManager.searchCriteria == 0 ? "Identification Number" : "Referral Number", isEnable: false, hasSelectionCustomIcon: false, labelColor: Colors.black, @@ -138,38 +133,63 @@ class SearchEReferralFormFormState extends State { leadingIcon: AppAssets.search_icon, dropdownItems: [], ).paddingSymmetrical(0, 8).onPress(() { - _showCriteriaBottomSheet(context); + _showCriteriaBottomSheet(context, formManager); }); } - - - void _showCriteriaBottomSheet(BuildContext context,) { - + void _showCriteriaBottomSheet(BuildContext context, ReferralFormManager formManager) { + final criteriaList = [ + {0: 'Identification Number'}, + {1: 'Referral Number'}, + ]; showCommonBottomSheetWithoutHeight( context, title: "Select Criteria", child: ListView.separated( - shrinkWrap: true, - physics: const BouncingScrollPhysics(), - itemBuilder: (context, index) { - return ListTile( - title: Text(criteriaList[index].values.first), - onTap: () { - setState(() { - criteria = index; - }); + shrinkWrap: true, + physics: const BouncingScrollPhysics(), + itemBuilder: (context, index) { + final criteria = criteriaList[index]; + final criteriaKey = criteria.keys.first; + final criteriaValue = criteria.values.first; + + return ListTile( + leading: Radio( + value: criteriaKey, + groupValue: formManager.searchCriteria, + onChanged: (value) { + if (value != null) { + formManager.updateSearchCriteria(value); + _searchController.clear(); + _validateForm(formManager); Navigator.pop(context); - }, - ); + } + }, + ), + title: Text(criteriaValue), + onTap: () { + formManager.updateSearchCriteria(criteriaKey); + _searchController.clear(); + _validateForm(formManager); + Navigator.pop(context); }, - separatorBuilder: (context, index) => const Divider(), - itemCount: criteriaList.length, - ), + ); + }, + separatorBuilder: (context, index) => const Divider(), + itemCount: criteriaList.length, + ), isFullScreen: false, isCloseButtonVisible: true, - ); + ); + } + + void _validateForm(ReferralFormManager formManager) { + // Trigger validation + formManager.validateSearchForm(); + + // Notify parent if form validation state changes + widget.onFormValidated?.call(); } @override @@ -178,7 +198,6 @@ class SearchEReferralFormFormState extends State { _phoneController.dispose(); _searchFocusNode.dispose(); _phoneFocusNode.dispose(); - super.dispose(); } -} \ No newline at end of file +} diff --git a/lib/presentation/emergency_services/call_ambulance/widgets/pickup_location.dart b/lib/presentation/emergency_services/call_ambulance/widgets/pickup_location.dart index 9410af1..051c850 100644 --- a/lib/presentation/emergency_services/call_ambulance/widgets/pickup_location.dart +++ b/lib/presentation/emergency_services/call_ambulance/widgets/pickup_location.dart @@ -23,7 +23,7 @@ class PickupLocation extends StatelessWidget { children: [ "Select Pickup Direction" .needTranslation - .toText24(color: AppColors.textColor,isBold: true), + .toText24(color: AppColors.textColor, isBold: true), SizedBox( height: 16.h, ), @@ -34,128 +34,147 @@ class PickupLocation extends StatelessWidget { height: 12.h, ), Selector( - selector: (context, viewModel) => viewModel.callingPlace, - builder: (context, value, _) { - return Column( - spacing: 12.h, - children: [ - RadioGroup( - groupValue: value, - onChanged: (value) { - context - .read() - .updateCallingPlace(value); - }, - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - spacing: 24.h, - children: [ - Row( - children: [ - Radio( - value: AmbulanceCallingPlace.TO_HOSPITAL, - groupValue: value, - activeColor: AppColors.primaryRedColor, - - fillColor: MaterialStateProperty.all(AppColors.primaryRedColor), - ), - "To Hospital" - .needTranslation - .toText14(color: AppColors.textColor, weight: FontWeight.w500) - ], - ).onPress((){ - context - .read() - .updateCallingPlace(AmbulanceCallingPlace.TO_HOSPITAL); - }), - Row( - children: [ - Radio( - value: AmbulanceCallingPlace.FROM_HOSPITAL, - activeColor: AppColors.primaryRedColor, - - fillColor: MaterialStateProperty.all(AppColors.primaryRedColor), - ), - "From Hospital" - .needTranslation - .toText14(color: AppColors.textColor, weight: FontWeight.w500) - ], - ).onPress((){ - context - .read() - .updateCallingPlace(AmbulanceCallingPlace.FROM_HOSPITAL); - }), - ], + selector: (context, viewModel) => viewModel.callingPlace, + builder: (context, value, _) { + return Column( + children: [ + SizedBox(height: 12.h), + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Expanded( + child: Row( + children: [ + Radio( + value: AmbulanceCallingPlace.TO_HOSPITAL, + groupValue: value, + onChanged: (AmbulanceCallingPlace? newValue) { + if (newValue != null) { + context + .read() + .updateCallingPlace(newValue); + } + }, + activeColor: AppColors.primaryRedColor, + fillColor: MaterialStateProperty.all(AppColors.primaryRedColor), + ), + "To Hospital" + .needTranslation + .toText14(color: AppColors.textColor, weight: FontWeight.w500) + ], + ).onPress(() { + context + .read() + .updateCallingPlace(AmbulanceCallingPlace.TO_HOSPITAL); + }), ), - ), - Visibility( - visible: value == AmbulanceCallingPlace.TO_HOSPITAL, - child: Selector( - selector: (context, viewModel) => - viewModel.ambulanceDirection, - builder: (context, directionValue, _) { - return Column( - spacing: 12.h, - crossAxisAlignment: CrossAxisAlignment.start, + Expanded( + child: Row( + children: [ + Radio( + value: AmbulanceCallingPlace.FROM_HOSPITAL, + groupValue: value, + onChanged: (AmbulanceCallingPlace? newValue) { + if (newValue != null) { + context + .read() + .updateCallingPlace(newValue); + } + }, + activeColor: AppColors.primaryRedColor, + fillColor: MaterialStateProperty.all(AppColors.primaryRedColor), + ), + "From Hospital" + .needTranslation + .toText14(color: AppColors.textColor, weight: FontWeight.w500) + ], + ).onPress(() { + context + .read() + .updateCallingPlace(AmbulanceCallingPlace.FROM_HOSPITAL); + }), + ), + ], + ), + Visibility( + visible: value == AmbulanceCallingPlace.TO_HOSPITAL, + child: Selector( + selector: (context, viewModel) => viewModel.ambulanceDirection, + builder: (context, directionValue, _) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 16.h), + "Select Way" + .needTranslation + .toText16(color: AppColors.textColor, weight: FontWeight.w600), + SizedBox(height: 12.h), + Row( + mainAxisAlignment: MainAxisAlignment.start, children: [ - "Select Way" - .needTranslation - .toText16(color: AppColors.textColor, weight: FontWeight.w600), - RadioGroup( - groupValue: directionValue, - onChanged: (value) { + Expanded( + child: Row( + children: [ + Radio( + value: AmbulanceDirection.ONE_WAY, + groupValue: directionValue, + onChanged: (AmbulanceDirection? newValue) { + if (newValue != null) { + context + .read() + .updateDirection(newValue); + } + }, + activeColor: AppColors.primaryRedColor, + fillColor: MaterialStateProperty.all(AppColors.primaryRedColor), + ), + "One Way" + .needTranslation + .toText12(color: AppColors.textColor, fontWeight: FontWeight.w500) + ], + ).onPress(() { context .read() - .updateDirection(value); - }, + .updateDirection(AmbulanceDirection.ONE_WAY); + }), + ), + Expanded( child: Row( - mainAxisAlignment: MainAxisAlignment.start, - spacing: 24.h, children: [ - Row( - children: [ - Radio( - value: AmbulanceDirection.ONE_WAY, - activeColor: AppColors.primaryRedColor, - - fillColor: MaterialStateProperty.all(AppColors.primaryRedColor), - ), - "One Way" - .needTranslation - .toText12(color: AppColors.textColor, fontWeight: FontWeight.w500) - ], - ).onPress((){ - context - .read() - .updateDirection(AmbulanceDirection.ONE_WAY); - }), - Row( - children: [ - Radio( - value: AmbulanceDirection.TWO_WAY, - // activeColor: AppColors.primaryRedColor, - - fillColor: MaterialStateProperty.all(AppColors.primaryRedColor), - ), - "Two Way" - .needTranslation - .toText14(color: AppColors.textColor, weight: FontWeight.w500) - ], - ).onPress((){ - context - .read() - .updateDirection(AmbulanceDirection.TWO_WAY); - }), + Radio( + value: AmbulanceDirection.TWO_WAY, + groupValue: directionValue, + onChanged: (AmbulanceDirection? newValue) { + if (newValue != null) { + context + .read() + .updateDirection(newValue); + } + }, + activeColor: AppColors.primaryRedColor, + fillColor: MaterialStateProperty.all(AppColors.primaryRedColor), + ), + "Two Way" + .needTranslation + .toText14(color: AppColors.textColor, weight: FontWeight.w500) ], - ), + ).onPress(() { + context + .read() + .updateDirection(AmbulanceDirection.TWO_WAY); + }), ), ], - ); - }), - ) - ], - ); - }), + ), + ], + ); + }, + ), + ) + ], + ); + }, + ), SizedBox( height: 16.h, ), @@ -170,4 +189,4 @@ class PickupLocation extends StatelessWidget { ], ); } -} +} \ No newline at end of file diff --git a/lib/routes/app_routes.dart b/lib/routes/app_routes.dart index a0ee1e5..b37ebfa 100644 --- a/lib/routes/app_routes.dart +++ b/lib/routes/app_routes.dart @@ -3,7 +3,7 @@ import 'package:hmg_patient_app_new/presentation/authentication/login.dart'; import 'package:hmg_patient_app_new/presentation/authentication/register.dart'; import 'package:hmg_patient_app_new/presentation/authentication/register_step2.dart'; import 'package:hmg_patient_app_new/presentation/comprehensive_checkup/comprehensive_checkup_page.dart'; -import 'package:hmg_patient_app_new/presentation/e_referral/e_referral_page_home.dart'; +import 'package:hmg_patient_app_new/presentation/e_referral/new_e_referral.dart'; import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart'; import 'package:hmg_patient_app_new/presentation/home_health_care/hhc_procedures_page.dart'; import 'package:hmg_patient_app_new/presentation/medical_file/medical_file_page.dart'; @@ -27,7 +27,7 @@ class AppRoutes { register: (context) => RegisterNew(), registerStepTwo: (context) => RegisterNewStep2(), medicalFilePage: (context) => MedicalFilePage(), - eReferralPage: (context) => EReferralPage(), + eReferralPage: (context) => NewReferralPage(), comprehensiveCheckupPage: (context) => ComprehensiveCheckupPage(), homeHealthCarePage: (context) => HhcProceduresPage() }; diff --git a/lib/widgets/image_picker.dart b/lib/widgets/image_picker.dart index 9444159..e25b117 100644 --- a/lib/widgets/image_picker.dart +++ b/lib/widgets/image_picker.dart @@ -147,6 +147,7 @@ void cameraImageAndroid(Function(String, File) image) async { if (base64Encode != null) { image(base64Encode, _image); } + } class _BottomSheet extends StatelessWidget { diff --git a/lib/widgets/order_tracking/request_tracking.dart b/lib/widgets/order_tracking/request_tracking.dart index c0847f9..f07c0b1 100644 --- a/lib/widgets/order_tracking/request_tracking.dart +++ b/lib/widgets/order_tracking/request_tracking.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/order_tracking/order_tracking_state.dart'; +import 'package:hmg_patient_app_new/widgets/stepper/stepper_widget.dart'; class OrderTrackingWidget extends StatelessWidget { final double? height; @@ -35,9 +36,9 @@ class OrderTrackingWidget extends StatelessWidget { return Row( children: List.generate(4, (index) { if (index == 0) { - return oneProgressBar(widthOfOneState, AppColors.primaryRedColor, true); + return StepperWidget(widthOfOneState, AppColors.primaryRedColor, true, height); } else { - return oneProgressBar(widthOfOneState, AppColors.greyLightColor, false); + return StepperWidget(widthOfOneState, AppColors.greyLightColor, false, height); } })); } @@ -46,9 +47,9 @@ class OrderTrackingWidget extends StatelessWidget { return Row( children: List.generate(4, (index){ if(index == 0) { - return oneProgressBar(widthOfOneState, AppColors.primaryRedColor, true); + return StepperWidget(widthOfOneState, AppColors.primaryRedColor, true, height); } else { - return oneProgressBar(widthOfOneState, AppColors.greyLightColor, false); + return StepperWidget(widthOfOneState, AppColors.greyLightColor, false, height); } }) ); @@ -57,9 +58,9 @@ class OrderTrackingWidget extends StatelessWidget { return Row( children: List.generate(4, (index){ if(index == 1 || index == 0) { - return oneProgressBar(widthOfOneState, AppColors.primaryRedColor, index == 1 ); + return StepperWidget(widthOfOneState, AppColors.primaryRedColor, index == 1, height ); } else { - return oneProgressBar(widthOfOneState, AppColors.greyLightColor, false); + return StepperWidget(widthOfOneState, AppColors.greyLightColor, false, height); } }) ); @@ -67,9 +68,9 @@ class OrderTrackingWidget extends StatelessWidget { return Row( children: List.generate(4, (index){ if(index == 2 || index == 1 || index == 0) { - return oneProgressBar(widthOfOneState, AppColors.primaryRedColor, index == 2); + return StepperWidget(widthOfOneState, AppColors.primaryRedColor, index == 2, height); } else { - return oneProgressBar(widthOfOneState, AppColors.greyLightColor, false); + return StepperWidget(widthOfOneState, AppColors.greyLightColor, false, height); } }) ); @@ -77,7 +78,7 @@ class OrderTrackingWidget extends StatelessWidget { return Row( children: List.generate(4, (index){ // if(index == 3) { - return oneProgressBar(widthOfOneState, AppColors.successLightColor, index == 3); + return StepperWidget(widthOfOneState, AppColors.successLightColor, index == 3, height); // } else { // return oneProgressBar(widthOfOneState, AppColors.greyLightColor, false); // } @@ -87,7 +88,7 @@ class OrderTrackingWidget extends StatelessWidget { return Row( children: List.generate(4, (index){ // if(index == 3) { - return oneProgressBar(widthOfOneState, AppColors.errorColor, index == 3); + return StepperWidget(widthOfOneState, AppColors.errorColor, index == 3, height); // } else { // return oneProgressBar(widthOfOneState, AppColors.greyLightColor, false); // } @@ -97,7 +98,7 @@ class OrderTrackingWidget extends StatelessWidget { return Row( children: List.generate(4, (index){ // if(index == 3) { - return oneProgressBar(widthOfOneState, AppColors.errorColor, index == 3); + return StepperWidget(widthOfOneState, AppColors.errorColor, index == 3, height); // } else { // return oneProgressBar(widthOfOneState, AppColors.greyLightColor, false); // } @@ -106,58 +107,6 @@ class OrderTrackingWidget extends StatelessWidget { } } - Widget oneProgressBar(double width, Color color, bool hasThumb) { - return Row( - children: [ - AnimatedSize( - duration: const Duration(seconds:1), - child: SizedBox( - height: 28.h, - width: width, - child: Stack( - clipBehavior: Clip.none, - children: [ - SizedBox( - height: 28.h, - child: Container( - width: width, - height: height, - decoration: BoxDecoration( - color: color, - shape: BoxShape.rectangle, - borderRadius: BorderRadius.circular(30.h) - ), - ), - ), - Visibility( - visible: hasThumb, - child: Positioned( - top: -6.h, // move thumb above bar center - left: width - 22.h, // move to right end - child: thumb(color), - ), - ) - ], - ), - ), - ), - SizedBox(width: 8.h) - ], - ); - } - - Widget thumb(Color color) { - return Container( - width: 18.h, - height: 18.h, - padding: EdgeInsets.zero, - decoration: BoxDecoration( - color: color, - shape: BoxShape.circle, - border: Border.all(color: Colors.white, width: 2.h) - ), - ); - } } \ No newline at end of file diff --git a/lib/widgets/stepper/stepper_widget.dart b/lib/widgets/stepper/stepper_widget.dart new file mode 100644 index 0000000..5d901ac --- /dev/null +++ b/lib/widgets/stepper/stepper_widget.dart @@ -0,0 +1,71 @@ +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_export.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; + +class StepperWidget extends StatelessWidget { + + double width = 80.w; + Color activeColor = AppColors.primaryRedColor; + bool hasThumb = true; + double? height = 4.h; + StepperWidget( this.width, this.activeColor, this.hasThumb, this.height, {super.key}); + + @override + Widget build(BuildContext context) { + return oneProgressBar(width, activeColor, hasThumb); + } + + Widget oneProgressBar(double width, Color color, bool hasThumb) { + return Row( + children: [ + AnimatedSize( + duration: const Duration(seconds: 1), + child: SizedBox( + height: 28.h, + width: width, + child: Stack( + clipBehavior: Clip.none, + children: [ + SizedBox( + height: height, + child: Container( + width: width, + height: height, + decoration: BoxDecoration( + color: color, + shape: BoxShape.rectangle, + borderRadius: BorderRadius.circular(30.h) + ), + ), + ), + + Visibility( + visible: hasThumb, + child: Positioned( + top: -6.h, // move thumb above bar center + left: width - 22.h, // move to right end + child: thumb(color), + ), + ) + ], + ), + ), + ), + SizedBox(width: 8.h) + ], + ); + } + + Widget thumb(Color color) { + return Container( + width: 18.h, + height: 18.h, + padding: EdgeInsets.zero, + decoration: BoxDecoration( + color: color, + shape: BoxShape.circle, + border: Border.all(color: Colors.white, width: 2.h) + ), + ); + } +} \ No newline at end of file