diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index 1146736..417519f 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -14,8 +14,8 @@ var PACKAGES_ORDERS = '/api/orders'; var PACKAGES_ORDER_HISTORY = '/api/orders/items'; var PACKAGES_TAMARA_OPT = '/api/orders/paymentoptions/tamara'; // var BASE_URL = 'http://10.50.100.198:2018/'; -// var BASE_URL = 'https://uat.hmgwebservices.com/'; -var BASE_URL = 'https://hmgwebservices.com/'; +var BASE_URL = 'https://uat.hmgwebservices.com/'; +// var BASE_URL = 'https://hmgwebservices.com/'; // var BASE_URL = 'http://10.201.204.103/'; // var BASE_URL = 'https://orash.cloudsolutions.com.sa/'; // var BASE_URL = 'https://vidauat.cloudsolutions.com.sa/'; @@ -178,8 +178,12 @@ var DELETE_CHILD_REQUEST = 'Services/Community.svc/REST/DeleteBaby'; var GET_TABLE_REQUEST = 'Services/Community.svc/REST/CreateVaccinationTable'; ///BloodDenote +/// +/// use get all cities from the e-referral, already calling this api there don't use multiple same api calls var GET_CITIES_REQUEST = 'Services/Lists.svc/REST/GetAllCities'; - +/// +/// +/// ///BloodDetails var GET_BLOOD_REQUEST = 'services/PatientVarification.svc/REST/BloodDonation_GetBloodGroupDetails'; @@ -440,12 +444,7 @@ var H2O_UPDATE_USER_DETAIL = "Services/H2ORemainder.svc/REST/H2O_UpdateUserDetai var H2O_UNDO_USER_ACTIVITY = "Services/H2ORemainder.svc/REST/H2o_UndoUserActivity"; //E_Referral Services -var GET_ALL_RELATIONSHIP_TYPES = "Services/Patients.svc/REST/GetAllRelationshipTypes"; -var SEND_ACTIVATION_CODE_FOR_E_REFERRAL = 'Services/Authentication.svc/REST/SendActivationCodeForEReferral'; -var CHECK_ACTIVATION_CODE_FOR_E_REFERRAL = 'Services/Authentication.svc/REST/CheckActivationCodeForEReferral'; -var GET_ALL_CITIES = 'services/Lists.svc/rest/GetAllCities'; -var CREATE_E_REFERRAL = "Services/Patients.svc/REST/CreateEReferral"; -var GET_E_REFERRALS = "Services/Patients.svc/REST/GetEReferrals"; + // Encillary Orders @@ -851,6 +850,17 @@ class ApiConsts { static final String updateHHCOrder = 'api/hhc/update'; static final String addHHCOrder = 'api/HHC/add'; + + //E-REFERRAL SERVICES + + static final getAllRelationshipTypes = "Services/Patients.svc/REST/GetAllRelationshipTypes"; + static final sendActivationCodeForEReferral = 'Services/Authentication.svc/REST/SendActivationCodeForEReferral'; + static final checkActivationCodeForEReferral = 'Services/Authentication.svc/REST/CheckActivationCodeForEReferral'; + static final getAllCities = 'services/Lists.svc/rest/GetAllCities'; + static final createEReferral = "Services/Patients.svc/REST/CreateEReferral"; + static final getEReferrals = "Services/Patients.svc/REST/GetEReferrals"; + + // ************ static values for Api **************** static final double appVersionID = 50.3; static final int appChannelId = 3; diff --git a/lib/core/dependencies.dart b/lib/core/dependencies.dart index 6c45247..30b4ab7 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/core/utils/utils.dart b/lib/core/utils/utils.dart index e3b108f..e2c11d4 100644 --- a/lib/core/utils/utils.dart +++ b/lib/core/utils/utils.dart @@ -8,6 +8,7 @@ import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:crypto/crypto.dart' as crypto; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:google_api_availability/google_api_availability.dart'; @@ -29,6 +30,7 @@ import 'package:hmg_patient_app_new/widgets/loading_dialog.dart'; import 'package:lottie/lottie.dart'; import 'package:path_provider/path_provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:url_launcher/url_launcher.dart'; class Utils { static AppState appState = getIt.get(); @@ -865,4 +867,8 @@ class Utils { } return isHavePrivilege; } + static void openWebView({ required String url}) { + Uri uri = Uri.parse(url); + launchUrl(uri, mode: LaunchMode.inAppBrowserView); + } } diff --git a/lib/core/utils/validation_utils.dart b/lib/core/utils/validation_utils.dart index 7e38300..abba447 100644 --- a/lib/core/utils/validation_utils.dart +++ b/lib/core/utils/validation_utils.dart @@ -101,8 +101,7 @@ class ValidationUtils { return regex.hasMatch(id); } - static bool validateUaeRegistration( - {String? name, GenderTypeEnum? gender, NationalityCountries? country, MaritalStatusTypeEnum? maritalStatus, required Function() onOkPress}) { + static bool validateUaeRegistration({String? name, GenderTypeEnum? gender, NationalityCountries? country, MaritalStatusTypeEnum? maritalStatus, required Function() onOkPress}) { if (name == null || name.isEmpty) { _dialogService.showExceptionBottomSheet(message: LocaleKeys.pleaseEnterAValidName.tr(), onOkPressed: onOkPress); return false; @@ -141,8 +140,7 @@ class ValidationUtils { return true; } - static bool isValidatedIdAndPhoneWithCountryValidation( - {String? nationalId, String? phoneNumber, required Function() onOkPress, CountryEnum? selectedCountry}) { + static bool isValidatedIdAndPhoneWithCountryValidation({String? nationalId, String? phoneNumber, required Function() onOkPress, CountryEnum? selectedCountry}) { bool isCorrectID = true; if (nationalId == null || nationalId.isEmpty) { _dialogService.showExceptionBottomSheet(message: LocaleKeys.pleaseEnterAnationalID.tr(), onOkPressed: onOkPress); @@ -171,4 +169,10 @@ class ValidationUtils { } return isCorrectID; } -} + + static bool isNullOrEmpty(String? value) { + return value == null || value + .trim() + .isEmpty; + } +} \ No newline at end of file diff --git a/lib/features/hmg_services/hmg_services_repo.dart b/lib/features/hmg_services/hmg_services_repo.dart index 254d309..4103569 100644 --- a/lib/features/hmg_services/hmg_services_repo.dart +++ b/lib/features/hmg_services/hmg_services_repo.dart @@ -5,12 +5,21 @@ import 'package:hmg_patient_app_new/core/api/api_client.dart'; import 'package:hmg_patient_app_new/core/api_consts.dart'; import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart'; 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'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart'; import 'package:hmg_patient_app_new/services/logger_service.dart'; +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(); @@ -38,6 +47,21 @@ abstract class HmgServicesRepo { required int orderServiceID, required List services, }); + + Future>>> getRelationshipTypes(); + + Future>>> getAllCities(); + + Future>> sendEReferralActivationCode(SendActivationCodeForEReferralRequestModel requestModel); + + Future>> checkEReferralActivationCode(CheckActivationCodeForEReferralRequestModel requestModel); + + Future>> createEReferral(CreateEReferralRequestModel requestModel); + + + Future>>> searchEReferral(SearchEReferralRequestModel requestModel); + + } class HmgServicesRepoImp implements HmgServicesRepo { @@ -519,4 +543,278 @@ class HmgServicesRepoImp implements HmgServicesRepo { return Left(UnknownFailure(e.toString())); } } + + + /*e-referral functions*/ + + + @override + Future>>> getRelationshipTypes() async { + Map requestBody = {}; + + try { + GenericApiModel>? apiResponse; + Failure? failure; + + await apiClient.post( + ApiConsts.getAllRelationshipTypes, + body: requestBody, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + loggerService.logError("EReferral Services API Failed: $error, Status: $statusCode"); + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + List relationshipList = []; + + if (response['List_EReferralResult'] != null && response['List_EReferralResult'] is List) { + final servicesList = response['List_EReferralResult'] as List; + + for (var serviceJson in servicesList) { + if (serviceJson is Map) { + relationshipList.add(GetAllRelationshipTypeResponseModel.fromJson(serviceJson)); + } + } + } + + apiResponse = GenericApiModel>( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: errorMessage, + data: relationshipList, + ); + } 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 getAllCmcServices: ${e.toString()}"); + return Left(UnknownFailure(e.toString())); + } + } + + @override + Future>>> getAllCities() async { + Map requestBody = {}; + + try { + GenericApiModel>? apiResponse; + Failure? failure; + + await apiClient.post( + ApiConsts.getAllCities, + body: requestBody, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + loggerService.logError("EReferral Services API Failed: $error, Status: $statusCode"); + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + List citiesList = []; + + if (response['ListCities'] != null && response['ListCities'] is List) { + final servicesList = response['ListCities'] as List; + + for (var serviceJson in servicesList) { + if (serviceJson is Map) { + citiesList.add(GetAllCitiesResponseModel.fromJson(serviceJson)); + } + } + } + + apiResponse = GenericApiModel>( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: errorMessage, + data: citiesList, + ); + } 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 getAllCmcServices: ${e.toString()}"); + return Left(UnknownFailure(e.toString())); + } + } + + + @override + Future>> sendEReferralActivationCode(SendActivationCodeForEReferralRequestModel requestModel) async { + GenericApiModel? apiResponse; + try { + + Failure? failure; + await apiClient.post( + ApiConsts.sendActivationCodeForEReferral, + body: requestModel.toJson(), + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + loggerService.logError("EReferral Services API Failed: $error, Status: $statusCode"); + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + + + + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: errorMessage, + data: response['LogInTokenID'] as String, + ); + } 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 getAllCmcServices: ${e.toString()}"); + return Left(UnknownFailure(e.toString())); + } + } + + @override + Future>> createEReferral(CreateEReferralRequestModel requestModel) async { + GenericApiModel? apiResponse; + try { + Failure? failure; + await apiClient.post( + ApiConsts.createEReferral, + body: requestModel.toJson(), + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + loggerService.logError("EReferral Services API Failed: $error, Status: $statusCode"); + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: errorMessage, + data: response['ReferralNumber'].toString(), + ); + } 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 getAllCmcServices: ${e.toString()}"); + return Left(UnknownFailure(e.toString())); + } + } + + + @override + Future>> checkEReferralActivationCode(CheckActivationCodeForEReferralRequestModel requestModel) async { + GenericApiModel? apiResponse; + try { + Failure? failure; + await apiClient.post( + ApiConsts.checkActivationCodeForEReferral, + body: requestModel.toJson(), + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + loggerService.logError("EReferral Services API Failed: $error, Status: $statusCode"); + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: errorMessage, + data: response['IsAuthenticated'].toString(), + ); + } 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 getAllCmcServices: ${e.toString()}"); + 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 24b7fff..81cd060 100644 --- a/lib/features/hmg_services/hmg_services_view_model.dart +++ b/lib/features/hmg_services/hmg_services_view_model.dart @@ -1,19 +1,31 @@ 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'; 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; @@ -41,6 +53,12 @@ class HmgServicesViewModel extends ChangeNotifier { // HHC order creation state (no hospital selection needed for home healthcare) GetCMCServicesResponseModel? selectedServiceForHhcOrder; + + List relationTypes =[]; + List getAllCitiesList =[]; + List searchReferralList =[]; + + Future getOrdersList() async {} // HHC multiple services selection List selectedHhcServices = []; @@ -512,4 +530,262 @@ class HmgServicesViewModel extends ChangeNotifier { selectedHhcServices.clear(); notifyListeners(); } + + /*e-referral functions*/ + + Future getRelationshipType({ + Function(dynamic)? onSuccess, + Function(String)? onError, + }) async { + notifyListeners(); + final result = await hmgServicesRepo.getRelationshipTypes(); + + result.fold( + (failure) async { + + notifyListeners(); + await errorHandlerService.handleError(failure: failure); + if (onError != null) { + onError(failure.toString()); + } + }, + (apiResponse) { + + if (apiResponse.messageStatus == 1) { + relationTypes = apiResponse.data ?? []; + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } else { + notifyListeners(); + if (onError != null) { + onError(apiResponse.errorMessage ?? 'Unknown error'); + } + } + }, + ); + } + + + Future getAllCities({ + Function(dynamic)? onSuccess, + Function(String)? onError, + }) async { + notifyListeners(); + final result = await hmgServicesRepo.getAllCities(); + + result.fold( + (failure) async { + + notifyListeners(); + await errorHandlerService.handleError(failure: failure); + if (onError != null) { + onError(failure.toString()); + } + }, + (apiResponse) { + + if (apiResponse.messageStatus == 1) { + getAllCitiesList = apiResponse.data ?? []; + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } else { + notifyListeners(); + if (onError != null) { + onError(apiResponse.errorMessage ?? 'Unknown error'); + } + } + }, + ); + } + + Future eReferralSendActivationCode({ + required SendActivationCodeForEReferralRequestModel requestModel, + Function(GenericApiModel)? onSuccess, + Function(String)? onError, + }) async { + + notifyListeners(); + + final result = await hmgServicesRepo.sendEReferralActivationCode(requestModel); + + result.fold( + (failure) async { + notifyListeners(); + await errorHandlerService.handleError(failure: failure); + if (onError != null) { + onError(failure.toString()); + } + }, + (apiResponse) { + if (apiResponse.messageStatus == 1) { + + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } else { + notifyListeners(); + if (onError != null) { + onError(apiResponse.errorMessage ?? 'Unknown error'); + } + } + }, + ); + } + + Future checkEReferralActivationCode({ + required CheckActivationCodeForEReferralRequestModel requestModel, + Function(GenericApiModel)? onSuccess, + Function(String)? onError, + }) async { + + notifyListeners(); + + final result = await hmgServicesRepo.checkEReferralActivationCode(requestModel); + + result.fold( + (failure) async { + notifyListeners(); + await errorHandlerService.handleError(failure: failure); + if (onError != null) { + onError(failure.toString()); + } + }, + (apiResponse) { + if (apiResponse.messageStatus == 1) { + + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } else { + notifyListeners(); + if (onError != null) { + onError(apiResponse.errorMessage ?? 'Unknown error'); + } + } + }, + ); + } + + + Future createEReferral({ + required CreateEReferralRequestModel requestModel, + Function(GenericApiModel)? onSuccess, + Function(String)? onError, + }) async { + + notifyListeners(); + + final result = await hmgServicesRepo.createEReferral(requestModel); + + result.fold( + (failure) async { + notifyListeners(); + await errorHandlerService.handleError(failure: failure); + if (onError != null) { + onError(failure.toString()); + } + }, + (apiResponse) { + if (apiResponse.messageStatus == 1) { + + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } else { + notifyListeners(); + if (onError != null) { + onError(apiResponse.errorMessage ?? 'Unknown error'); + } + } + }, + ); + } + + + + 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/check_activation_e_referral_req_model.dart b/lib/features/hmg_services/models/req_models/check_activation_e_referral_req_model.dart new file mode 100644 index 0000000..a356616 --- /dev/null +++ b/lib/features/hmg_services/models/req_models/check_activation_e_referral_req_model.dart @@ -0,0 +1,57 @@ +class CheckActivationCodeForEReferralRequestModel { + String? logInTokenID; + String? activationCode; + double? versionID; + int? channel; + int? languageID; + String? iPAdress; + String? generalid; + int? patientOutSA; + dynamic sessionID; + bool? isDentalAllowedBackend; + int? deviceTypeID; + + CheckActivationCodeForEReferralRequestModel( + {this.logInTokenID, + this.activationCode, + this.versionID, + this.channel, + this.languageID, + this.iPAdress, + this.generalid, + this.patientOutSA, + this.sessionID, + this.isDentalAllowedBackend, + this.deviceTypeID}); + + CheckActivationCodeForEReferralRequestModel.fromJson( + Map json) { + logInTokenID = json['LogInTokenID']; + activationCode = json['activationCode']; + 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']; + } + + Map toJson() { + final Map data = new Map(); + data['LogInTokenID'] = this.logInTokenID; + data['activationCode'] = this.activationCode; + 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; + return data; + } +} diff --git a/lib/features/hmg_services/models/req_models/create_e_referral_model.dart b/lib/features/hmg_services/models/req_models/create_e_referral_model.dart new file mode 100644 index 0000000..1b351fd --- /dev/null +++ b/lib/features/hmg_services/models/req_models/create_e_referral_model.dart @@ -0,0 +1,155 @@ +class CreateEReferralRequestModel { + bool? isInsuredPatient; + String? cityCode; + String? cityName; + String? requesterName; + String? requesterContactNo; + int? requesterRelationship; + String? otherRelationship; + String? fullName; + int? identificationNo; + String? patientMobileNumber; + int? preferredBranchCode; + String? preferredBranchName; + List? medicalReportAttachment; + dynamic insuranceCardAttachment; + double? versionID; + int? channel; + int? languageID; + String? iPAdress; + String? generalid; + int? patientOutSA; + String? sessionID; + bool? isDentalAllowedBackend; + int? deviceTypeID; + int? patientID; + String? tokenID; + int? patientTypeID; + int? patientType; + + CreateEReferralRequestModel( + {this.isInsuredPatient, + this.cityCode, + this.cityName, + this.requesterName, + this.requesterContactNo, + this.requesterRelationship, + this.otherRelationship, + this.fullName, + this.identificationNo, + this.patientMobileNumber, + this.preferredBranchCode, + this.preferredBranchName, + this.medicalReportAttachment, + this.insuranceCardAttachment, + this.versionID, + this.channel, + this.languageID, + this.iPAdress, + this.generalid, + this.patientOutSA, + this.sessionID, + this.isDentalAllowedBackend, + this.deviceTypeID, + this.patientID, + this.tokenID, + this.patientTypeID, + this.patientType}); + + CreateEReferralRequestModel.fromJson(Map json) { + isInsuredPatient = json['IsInsuredPatient']; + cityCode = json['CityCode']; + cityName = json['CityName']; + requesterName = json['RequesterName']; + requesterContactNo = json['RequesterContactNo']; + requesterRelationship = json['RequesterRelationship']; + otherRelationship = json['OtherRelationship']; + fullName = json['FullName']; + identificationNo = json['IdentificationNo']; + patientMobileNumber = json['PatientMobileNumber']; + preferredBranchCode = json['PreferredBranchCode']; + preferredBranchName = json['PreferredBranchName']; + if (json['MedicalReportAttachment'] != null) { + medicalReportAttachment = []; + json['MedicalReportAttachment'].forEach((v) { + medicalReportAttachment!.add(EReferralAttachment.fromJson(v)); + }); + } + insuranceCardAttachment = json['InsuranceCardAttachment'] != null ? EReferralAttachment.fromJson(json['InsuranceCardAttachment']) : null; + 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']; + patientID = json['PatientID']; + tokenID = json['TokenID']; + patientTypeID = json['PatientTypeID']; + patientType = json['PatientType']; + } + + Map toJson() { + final Map data = new Map(); + data['IsInsuredPatient'] = this.isInsuredPatient; + data['CityCode'] = this.cityCode; + data['CityName'] = this.cityName; + data['RequesterName'] = this.requesterName; + data['RequesterContactNo'] = this.requesterContactNo; + data['RequesterRelationship'] = this.requesterRelationship; + data['OtherRelationship'] = this.otherRelationship; + data['FullName'] = this.fullName; + data['IdentificationNo'] = this.identificationNo; + data['PatientMobileNumber'] = this.patientMobileNumber; + data['PreferredBranchCode'] = this.preferredBranchCode; + data['PreferredBranchName'] = this.preferredBranchName; + if (this.medicalReportAttachment != null) { + // FIXED: Use map() to convert each item to JSON, then convert to list + data['MedicalReportAttachment'] = this.medicalReportAttachment!.map((v) => v.toJson()).toList(); + } + if (this.insuranceCardAttachment == null) { + data['InsuranceCardAttachment'] = {}; + } else if (this.insuranceCardAttachment is EReferralAttachment) { + // FIXED: Check if it's an EReferralAttachment before calling toJson() + data['InsuranceCardAttachment'] = (this.insuranceCardAttachment as EReferralAttachment).toJson(); + } else { + // If it's something else, assign directly + data['InsuranceCardAttachment'] = this.insuranceCardAttachment; + } + 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['PatientID'] = this.patientID; + data['TokenID'] = this.tokenID; + data['PatientTypeID'] = this.patientTypeID; + data['PatientType'] = this.patientType; + return data; + } +} + +class EReferralAttachment { + String? fileName; + String? base64String; + + EReferralAttachment({this.fileName, this.base64String}); + + EReferralAttachment.fromJson(Map json) { + fileName = json['FileName']; + base64String = json['Base64String']; + } + + Map toJson() { + final Map data = new Map(); + data['FileName'] = this.fileName; + data['Base64String'] = this.base64String; + return data; + } +} \ No newline at end of file diff --git a/lib/features/hmg_services/models/req_models/create_e_referral_req_model.dart b/lib/features/hmg_services/models/req_models/create_e_referral_req_model.dart new file mode 100644 index 0000000..0672fe6 --- /dev/null +++ b/lib/features/hmg_services/models/req_models/create_e_referral_req_model.dart @@ -0,0 +1,150 @@ +class CreateEReferralRequestModel { + bool? isInsuredPatient; + String? cityCode; + String? cityName; + String? requesterName; + String? requesterContactNo; + int? requesterRelationship; + String? otherRelationship; + String? fullName; + int? identificationNo; + String? patientMobileNumber; + int? preferredBranchCode; + String? preferredBranchName; + List? medicalReportAttachment; + dynamic insuranceCardAttachment; + double? versionID; + int? channel; + int? languageID; + String? iPAdress; + String? generalid; + int? patientOutSA; + String? sessionID; + bool? isDentalAllowedBackend; + int? deviceTypeID; + int? patientID; + String? tokenID; + int? patientTypeID; + int? patientType; + + CreateEReferralRequestModel( + {this.isInsuredPatient, + this.cityCode, + this.cityName, + this.requesterName, + this.requesterContactNo, + this.requesterRelationship, + this.otherRelationship, + this.fullName, + this.identificationNo, + this.patientMobileNumber, + this.preferredBranchCode, + this.preferredBranchName, + this.medicalReportAttachment, + this.insuranceCardAttachment, + this.versionID, + this.channel, + this.languageID, + this.iPAdress, + this.generalid, + this.patientOutSA, + this.sessionID, + this.isDentalAllowedBackend, + this.deviceTypeID, + this.patientID, + this.tokenID, + this.patientTypeID, + this.patientType}); + + CreateEReferralRequestModel.fromJson(Map json) { + isInsuredPatient = json['IsInsuredPatient']; + cityCode = json['CityCode']; + cityName = json['CityName']; + requesterName = json['RequesterName']; + requesterContactNo = json['RequesterContactNo']; + requesterRelationship = json['RequesterRelationship']; + otherRelationship = json['OtherRelationship']; + fullName = json['FullName']; + identificationNo = json['IdentificationNo']; + patientMobileNumber = json['PatientMobileNumber']; + preferredBranchCode = json['PreferredBranchCode']; + preferredBranchName = json['PreferredBranchName']; + if (json['MedicalReportAttachment'] != null) { + medicalReportAttachment = []; + json['MedicalReportAttachment'].forEach((v) { + medicalReportAttachment!.add(EReferralAttachment.fromJson(v)); + }); + } + insuranceCardAttachment = json['InsuranceCardAttachment'] != null ? EReferralAttachment.fromJson(json['InsuranceCardAttachment']) : null; + 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']; + patientID = json['PatientID']; + tokenID = json['TokenID']; + patientTypeID = json['PatientTypeID']; + patientType = json['PatientType']; + } + + Map toJson() { + final Map data = new Map(); + data['IsInsuredPatient'] = this.isInsuredPatient; + data['CityCode'] = this.cityCode; + data['CityName'] = this.cityName; + data['RequesterName'] = this.requesterName; + data['RequesterContactNo'] = this.requesterContactNo; + data['RequesterRelationship'] = this.requesterRelationship; + data['OtherRelationship'] = this.otherRelationship; + data['FullName'] = this.fullName; + data['IdentificationNo'] = this.identificationNo; + data['PatientMobileNumber'] = this.patientMobileNumber; + data['PreferredBranchCode'] = this.preferredBranchCode; + data['PreferredBranchName'] = this.preferredBranchName; + if (this.medicalReportAttachment != null) { + data['MedicalReportAttachment'] = this.medicalReportAttachment!.map((v) => v.toJson()).toList(); + } + if (this.insuranceCardAttachment == null) { + data['InsuranceCardAttachment'] = {}; + } else + data['InsuranceCardAttachment'] = this.insuranceCardAttachment.toJson(); + + 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['PatientID'] = this.patientID; + data['TokenID'] = this.tokenID; + data['PatientTypeID'] = this.patientTypeID; + data['PatientType'] = this.patientType; + return data; + } +} + +class EReferralAttachment { + String? fileName; + String? base64String; + + EReferralAttachment({this.fileName, this.base64String}); + + EReferralAttachment.fromJson(Map json) { + fileName = json['FileName']; + base64String = json['Base64String']; + } + + Map toJson() { + final Map data = new Map(); + data['FileName'] = this.fileName; + data['Base64String'] = this.base64String; + return data; + } +} 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/req_models/send_activation_code_ereferral_req_model.dart b/lib/features/hmg_services/models/req_models/send_activation_code_ereferral_req_model.dart new file mode 100644 index 0000000..0380de8 --- /dev/null +++ b/lib/features/hmg_services/models/req_models/send_activation_code_ereferral_req_model.dart @@ -0,0 +1,56 @@ +class SendActivationCodeForEReferralRequestModel { + int? patientMobileNumber; + String? zipCode; + double? versionID; + int? channel; + int? languageID; + String? iPAdress; + String? generalid; + int? patientOutSA; + dynamic sessionID; + bool? isDentalAllowedBackend; + int? deviceTypeID; + + SendActivationCodeForEReferralRequestModel( + {this.patientMobileNumber, + this.zipCode, + this.versionID, + this.channel, + this.languageID, + this.iPAdress, + this.generalid, + this.patientOutSA, + this.sessionID, + this.isDentalAllowedBackend, + this.deviceTypeID}); + + SendActivationCodeForEReferralRequestModel.fromJson(Map json) { + patientMobileNumber = json['PatientMobileNumber']; + zipCode = json['ZipCode']; + 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']; + } + + Map toJson() { + final Map data = new Map(); + data['PatientMobileNumber'] = this.patientMobileNumber; + data['ZipCode'] = this.zipCode; + 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; + return data; + } +} diff --git a/lib/features/hmg_services/models/resq_models/get_all_cities_resp_model.dart b/lib/features/hmg_services/models/resq_models/get_all_cities_resp_model.dart new file mode 100644 index 0000000..efc3d58 --- /dev/null +++ b/lib/features/hmg_services/models/resq_models/get_all_cities_resp_model.dart @@ -0,0 +1,21 @@ +class GetAllCitiesResponseModel { + int? iD; + String? description; + String? descriptionN; + + GetAllCitiesResponseModel({this.iD, this.description, this.descriptionN}); + + GetAllCitiesResponseModel.fromJson(Map json) { + iD = json['ID']; + description = json['Description']; + descriptionN = json['DescriptionN']; + } + + Map toJson() { + final Map data = new Map(); + data['ID'] = this.iD; + data['Description'] = this.description; + data['DescriptionN'] = this.descriptionN; + return data; + } +} diff --git a/lib/features/hmg_services/models/resq_models/relationship_type_resp_mode.dart b/lib/features/hmg_services/models/resq_models/relationship_type_resp_mode.dart new file mode 100644 index 0000000..237017d --- /dev/null +++ b/lib/features/hmg_services/models/resq_models/relationship_type_resp_mode.dart @@ -0,0 +1,25 @@ +class GetAllRelationshipTypeResponseModel { + int? iD; + String? text; + String? textAr; + String? textEn; + + GetAllRelationshipTypeResponseModel( + {this.iD, this.text, this.textAr, this.textEn}); + + GetAllRelationshipTypeResponseModel.fromJson(Map json) { + iD = json['ID']; + text = json['Text']; + textAr = json['Text_Ar']; + textEn = json['Text_En']; + } + + Map toJson() { + final Map data = new Map(); + data['ID'] = this.iD; + data['Text'] = this.text; + data['Text_Ar'] = this.textAr; + data['Text_En'] = this.textEn; + 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 new file mode 100644 index 0000000..32964b1 --- /dev/null +++ b/lib/features/hmg_services/models/ui_models/e_referral_form_model.dart @@ -0,0 +1,48 @@ +// models/referral_models.dart +import 'package:hmg_patient_app_new/core/common_models/nationality_country_model.dart'; +import 'package:hmg_patient_app_new/core/enums.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/resq_models/get_all_cities_resp_model.dart'; +import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/relationship_type_resp_mode.dart'; +import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart'; + +class ReferralFormData { + String requesterName = ''; + String requesterPhone = ''; + String searchPhone =''; + CountryEnum countryEnum = CountryEnum.saudiArabia; + CountryEnum patientCountryEnum = CountryEnum.saudiArabia; + + GetAllRelationshipTypeResponseModel? relationship; + String otherRelationshipName = ''; + + String patientIdentification = ''; + String patientName = ''; + String patientPhone = ''; + GetAllCitiesResponseModel? patientCity; + + List medicalReportImages = []; + HospitalsModel? branch; + bool isPatientInsured = false; + List insuredPatientImages = []; +} + +class FormValidationErrors { + String? requesterName; + String? requesterPhone; + String? relationship; + String? otherRelationshipName; + + String? patientIdentification; + String? patientName; + String? patientCity; + String? patientPhone; + + + String? medicalReport; + String? branch; + String? insuredDocument; + + String? searchValue; + String? searchPhone; +} \ No newline at end of file diff --git a/lib/features/hmg_services/models/ui_models/hmg_services_component_model.dart b/lib/features/hmg_services/models/ui_models/hmg_services_component_model.dart index d5180ae..d6e2654 100644 --- a/lib/features/hmg_services/models/ui_models/hmg_services_component_model.dart +++ b/lib/features/hmg_services/models/ui_models/hmg_services_component_model.dart @@ -5,21 +5,26 @@ class HmgServicesComponentModel { String title; String subTitle; String icon; + Color? iconColor; bool isLogin; bool isLocked; Color bgColor; Color textColor; - String route; + String? route; + Function? onTap; HmgServicesComponentModel( this.action, this.title, this.subTitle, this.icon, + this.isLogin, { this.isLocked = false, this.bgColor = Colors.white, this.textColor = Colors.black, - this.route = '', + this.iconColor = Colors.white, + this.route, + this.onTap }); } diff --git a/lib/presentation/e_referral/e-referral_validator.dart b/lib/presentation/e_referral/e-referral_validator.dart new file mode 100644 index 0000000..e0a8006 --- /dev/null +++ b/lib/presentation/e_referral/e-referral_validator.dart @@ -0,0 +1,85 @@ +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/features/hmg_services/models/ui_models/e_referral_form_model.dart'; + +class ReferralValidator { + static FormValidationErrors validateStep1(ReferralFormData formData) { + final errors = FormValidationErrors(); + + if (formData.requesterName.trim().isEmpty) { + errors.requesterName = 'Referral requester name is required'.needTranslation; + } + + + + if (formData.relationship == null) { + errors.relationship = 'Please select a relationship'.needTranslation; + } + + if (formData.relationship != null && + formData.relationship?.iD == 5 && + formData.otherRelationshipName.trim().isEmpty) { + errors.otherRelationshipName = 'Other relationship name is required'.needTranslation; + } + + return errors; + } + + static FormValidationErrors validateStep2(ReferralFormData formData) { + final errors = FormValidationErrors(); + + if (formData.patientIdentification.trim().isEmpty) { + errors.patientIdentification = 'Identification number is required'.needTranslation; + } + + if (formData.patientName.trim().isEmpty) { + errors.patientName = 'Patient name is required'.needTranslation; + } + + if (formData.patientPhone == null) { + errors.patientPhone = 'Please Enter patient phone number'.needTranslation; + } + + if (formData.patientCity == null) { + errors.patientCity = 'Please select patient city'.needTranslation; + } + + return errors; + } + + static FormValidationErrors validateStep3(ReferralFormData formData) { + final errors = FormValidationErrors(); + + if (formData.medicalReportImages.isEmpty) { + errors.medicalReport = 'At least one medical report is required'.needTranslation; + } + + if (formData.branch == null) { + errors.branch = 'Please select a branch'.needTranslation; + } + + if (formData.isPatientInsured && formData.insuredPatientImages.isEmpty) { + errors.insuredDocument = 'Insurance document is required for insured patients'.needTranslation; + } + + return errors; + } + + static bool _isValidSaudiPhone(String phone) { + final regex = RegExp(r'^5\d{8}$'); + return regex.hasMatch(phone); + } + + static bool hasErrors(FormValidationErrors errors) { + return errors.requesterName != null || + errors.requesterPhone != null || + errors.relationship != null || + errors.otherRelationshipName != null || + errors.patientIdentification != null || + errors.patientName != null || + errors.patientPhone != null || + errors.patientCity != null || + errors.medicalReport != null || + errors.branch != null || + errors.insuredDocument != null; + } +} \ No newline at end of file diff --git a/lib/presentation/e_referral/e_referral_form_manager.dart b/lib/presentation/e_referral/e_referral_form_manager.dart new file mode 100644 index 0000000..f7093c5 --- /dev/null +++ b/lib/presentation/e_referral/e_referral_form_manager.dart @@ -0,0 +1,290 @@ +// managers/referral_form_manager.dart +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/common_models/nationality_country_model.dart'; +import 'package:hmg_patient_app_new/core/enums.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/resq_models/get_all_cities_resp_model.dart'; +import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/relationship_type_resp_mode.dart'; +import 'package:hmg_patient_app_new/features/hmg_services/models/ui_models/e_referral_form_model.dart'; +import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart'; + + +class ReferralFormManager extends ChangeNotifier { + final ReferralFormData _formData = ReferralFormData(); + final FormValidationErrors _errors = FormValidationErrors(); + + ReferralFormData get formData => _formData; + FormValidationErrors get errors => _errors; + + + 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'); + } + + void updateRequesterPhone(String value) { + _formData.requesterPhone = value; + _clearError('requesterPhone'); + } + + void updateCountryEnum(CountryEnum value) { + _formData.countryEnum = value; + } + void updatePatientCountryEnum(CountryEnum value) { + _formData.patientCountryEnum = value; + } + + void updateRelationship(GetAllRelationshipTypeResponseModel? value) { + _formData.relationship = value; + notifyListeners(); + _clearError('relationship'); + } + + void updateOtherRelationshipName(String value) { + _formData.otherRelationshipName = value; + _clearError('otherRelationshipName'); + } + + void updatePatientIdentification(String value) { + _formData.patientIdentification = value; + _clearError('patientIdentification'); + } + + void updatePatientName(String value) { + _formData.patientName = value; + _clearError('patientName'); + } + + void updatePatientPhone(String? value) { + _formData.patientPhone = value!; + _clearError('patientPhone'); + } + + void updatePatientCity(GetAllCitiesResponseModel? value) { + _formData.patientCity = value; + _clearError('patientCity'); + notifyListeners(); + } + + void updateBranch(HospitalsModel? value) { + _formData.branch = value; + _clearError('branch'); + } + + void updateIsPatientInsured(bool value) { + _formData.isPatientInsured = value; + if (!value) { + _formData.insuredPatientImages.clear(); + } + _clearError('insuredDocument'); + } + + void addMedicalReport(EReferralAttachment attachment) { + _formData.medicalReportImages.add(attachment); + _clearError('medicalReport'); + } + + void removeMedicalReport(int index) { + if (index >= 0 && index < _formData.medicalReportImages.length) { + _formData.medicalReportImages.removeAt(index); + } + notifyListeners(); + } + + void addInsuranceDocument(EReferralAttachment attachment) { + _formData.insuredPatientImages.add(attachment); + _clearError('insuredDocument'); + } + + void removeInsuranceDocument(int index) { + if (index >= 0 && index < _formData.insuredPatientImages.length) { + _formData.insuredPatientImages.removeAt(index); + } + + notifyListeners(); + } + + + // Error management + void setErrors(FormValidationErrors newErrors) { + _errors.requesterName = newErrors.requesterName; + _errors.requesterPhone = newErrors.requesterPhone; + _errors.relationship = newErrors.relationship; + _errors.otherRelationshipName = newErrors.otherRelationshipName; + _errors.patientIdentification = newErrors.patientIdentification; + _errors.patientName = newErrors.patientName; + _errors.patientPhone = newErrors.patientPhone; + _errors.patientCity = newErrors.patientCity; + _errors.medicalReport = newErrors.medicalReport; + _errors.branch = newErrors.branch; + _errors.insuredDocument = newErrors.insuredDocument; + notifyListeners(); // Only notify when errors change + } + + void clearAllErrors() { + _errors.requesterName = null; + _errors.requesterPhone = null; + _errors.relationship = null; + _errors.otherRelationshipName = null; + _errors.patientIdentification = null; + _errors.patientName = null; + _errors.patientPhone = null; + _errors.patientCity = null; + _errors.medicalReport = null; + _errors.branch = null; + _errors.insuredDocument = null; + notifyListeners(); + } + + void _clearError(String field) { + bool shouldNotify = false; + + switch (field) { + case 'requesterName': + if (_errors.requesterName != null) { + _errors.requesterName = null; + shouldNotify = true; + } + break; + case 'requesterPhone': + if (_errors.requesterPhone != null) { + _errors.requesterPhone = null; + shouldNotify = true; + } + break; + case 'relationship': + if (_errors.relationship != null) { + _errors.relationship = null; + shouldNotify = true; + } + break; + case 'otherRelationshipName': + if (_errors.otherRelationshipName != null) { + _errors.otherRelationshipName = null; + shouldNotify = true; + } + break; + case 'patientIdentification': + if (_errors.patientIdentification != null) { + _errors.patientIdentification = null; + shouldNotify = true; + } + break; + case 'patientName': + if (_errors.patientName != null) { + _errors.patientName = null; + shouldNotify = true; + } + break; + case 'patientPhone': + if (_errors.patientPhone != null) { + _errors.patientPhone = null; + shouldNotify = true; + } + break; + case 'patientCity': + if (_errors.patientCity != null) { + _errors.patientCity = null; + shouldNotify = true; + } + break; + case 'medicalReport': + if (_errors.medicalReport != null) { + _errors.medicalReport = null; + shouldNotify = true; + } + break; + case 'branch': + if (_errors.branch != null) { + _errors.branch = null; + shouldNotify = true; + } + break; + case 'insuredDocument': + if (_errors.insuredDocument != null) { + _errors.insuredDocument = null; + shouldNotify = true; + } + break; + } + + if (shouldNotify) { + 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 bacca47..0000000 --- a/lib/presentation/e_referral/e_referral_page_home.dart +++ /dev/null @@ -1,97 +0,0 @@ -import 'dart:ui'; - -import 'package:flutter/cupertino.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/prescriptions/prescriptions_view_model.dart'; -import 'package:hmg_patient_app_new/presentation/e_referral/new_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: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; - @override - Widget build(BuildContext context) { - return Scaffold( - backgroundColor: AppColors.bgScaffoldColor, - body: CollapsingListView( - title:"E Referral".needTranslation, - child: SingleChildScrollView( - child: Consumer(builder: (context, model, child) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox(height: 16.h), - Row( - children: [ - CustomButton( - text: "New Referral".needTranslation, - onPressed: () { - isNewReferral =true; - setState(() { - - }); - }, - backgroundColor: model.isSortByClinic ? AppColors.bgRedLightColor : AppColors.whiteColor, - borderColor: model.isSortByClinic ? AppColors.primaryRedColor : AppColors.textColor.withOpacity(0.2), - textColor: model.isSortByClinic ? AppColors.primaryRedColor : AppColors.blackColor, - fontSize: 12, - fontWeight: FontWeight.w500, - borderRadius: 10, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 40.h, - ), - SizedBox(width: 8.h), - CustomButton( - text: "Search Referral".needTranslation, - onPressed: () { - isNewReferral =false; - }, - backgroundColor: model.isSortByClinic ? AppColors.whiteColor : AppColors.bgRedLightColor, - borderColor: model.isSortByClinic ? AppColors.textColor.withOpacity(0.2) : AppColors.primaryRedColor, - textColor: model.isSortByClinic ? AppColors.blackColor : AppColors.primaryRedColor, - fontSize: 12, - fontWeight: FontWeight.w500, - borderRadius: 10, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 40.h, - ), - ], - ).paddingSymmetrical(24.h, 0.h), - SizedBox(height: 20.h), - isNewReferral ? NewEReferral() : SizedBox(), - ], - ); - }), - ), - ), - ); - - - - - } -} 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..ba31f1b --- /dev/null +++ b/lib/presentation/e_referral/e_referral_search_result.dart @@ -0,0 +1,280 @@ +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.toText14(color: AppColors.greyTextColor), + + ], + ); + } + + 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; + } + } + + + 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 new file mode 100644 index 0000000..c736216 --- /dev/null +++ b/lib/presentation/e_referral/new_e_referral.dart @@ -0,0 +1,262 @@ +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/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 { + const NewReferralPage({ + super.key, + }); + + @override + State createState() => _NewReferralPageState(); +} + +class _NewReferralPageState extends State { + final PageController _pageController = PageController(); + final ReferralFormManager _formManager = ReferralFormManager(); // Use manager + int _currentStep = 0; + + double widthOfOneState = ((ResponsiveExtension.screenWidth) / 3) - (20.h); + + + @override + void initState() { + super.initState(); + _loadData(); + } + + void _handleNextStep() { + switch (_currentStep) { + case 0: + if (_validateCurrentStep()) { + OTPService.openOTPScreen( + context: context, + formManager: _formManager, + onSuccess: _proceedToNextStep, + ); + } + break; + case 1: + if (_validateCurrentStep()) { + _proceedToNextStep(); + } + break; + case 2: + if (_validateCurrentStep()) { + _submitReferral(); + } + break; + } + } + + bool _validateCurrentStep() { + FormValidationErrors stepErrors; + + switch (_currentStep) { + case 0: + stepErrors = ReferralValidator.validateStep1(_formManager.formData); + break; + case 1: + stepErrors = ReferralValidator.validateStep2(_formManager.formData); + break; + case 2: + stepErrors = ReferralValidator.validateStep3(_formManager.formData); + break; + default: + stepErrors = FormValidationErrors(); + } + + // Update errors through manager + _formManager.setErrors(stepErrors); + + return !ReferralValidator.hasErrors(stepErrors); + } + + void _proceedToNextStep() { + _pageController.nextPage( + duration: const Duration(milliseconds: 300), + curve: Curves.easeInOut, + ); + // setState(() { + _currentStep++; + // }); + // widget.onStepChanged(_currentStep); + } + + void _submitReferral() { + CreateEReferralRequestModel createReferralRequestModel = CreateEReferralRequestModel( + isInsuredPatient: _formManager.formData.isPatientInsured, + cityCode: _formManager.formData.patientCity!.iD!.toString(), + cityName: _formManager.formData.patientCity!.description, + requesterName: _formManager.formData.requesterName, + 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.patientCountryEnum.countryCode + _formManager.formData.patientPhone, + preferredBranchCode: _formManager.formData.branch!.iD, + medicalReportAttachment: _formManager.formData.medicalReportImages, + insuranceCardAttachment: _formManager.formData.insuredPatientImages, + preferredBranchName: _formManager.formData.branch!.desciption); + + final hmgServicesVM = context.read(); + LoaderBottomSheet.showLoader(); + + hmgServicesVM.createEReferral( + 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() { + final authVM = context.read(); + final habibWalletVM = context.read(); + final hmgServicesVM = context.read(); + + hmgServicesVM.getRelationshipType(); + authVM.loadCountriesData(); + hmgServicesVM.getAllCities(); + habibWalletVM.getProjectsList(); + } + + @override + Widget build(BuildContext context) { + 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: [ + 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), + ], + ), + 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, + ); + } +} diff --git a/lib/presentation/e_referral/new_referral.dart b/lib/presentation/e_referral/new_referral.dart deleted file mode 100644 index 4ed9b8e..0000000 --- a/lib/presentation/e_referral/new_referral.dart +++ /dev/null @@ -1,172 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; -import 'package:hmg_patient_app_new/theme/colors.dart'; - -class NewEReferral extends StatefulWidget { - NewEReferral(); - - @override - _NewEReferralState createState() => _NewEReferralState(); -} - -class _NewEReferralState extends State with TickerProviderStateMixin { - late PageController _controller; - int _currentIndex = 0; - int pageSelected = 2; - - // CreateEReferralRequestModel createEReferralRequestModel = new CreateEReferralRequestModel(); - - @override - void initState() { - super.initState(); - _controller = new PageController(); - } - - @override - void dispose() { - super.dispose(); - } - - changePageViewIndex(pageIndex) { - _controller.jumpToPage(pageIndex); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - body: Container( - height: double.infinity, - child: Column( - children: [ - Container( - width: double.infinity, - padding: EdgeInsets.only(left: 12,right: 12,top: 12), - child: Row( - children: [ - Expanded( - child: showProgress( - title: "Requester Info".needTranslation, - status: _currentIndex == 0 - ? "InProgress".needTranslation - : _currentIndex > 0 - ? "Completed".needTranslation - : "Locked".needTranslation, - color: _currentIndex == 0 ? AppColors.infoColor : AppColors.successColor, - ), - ), - Expanded( - child: showProgress( - title:"Patient Info".needTranslation, - status: _currentIndex == 1 - ? "InProgress".needTranslation - : _currentIndex > 1 - ? "Completed".needTranslation - : "Locked".needTranslation, - color: _currentIndex == 1 - ? AppColors.infoColor - : _currentIndex > 1 - ? AppColors.successColor - : AppColors.greyColor, - ), - ), - showProgress( - title: "Other Info".needTranslation, - status: _currentIndex == 2 ? "InProgress".needTranslation :"Locked".needTranslation, - color: _currentIndex == 2 - ? AppColors.infoColor - : _currentIndex > 3 - ? AppColors.successColor - : AppColors.greyColor, - isNeedBorder: false, - ), - ], - ), - ), - Expanded( - child: PageView( - physics: NeverScrollableScrollPhysics(), - controller: _controller, - onPageChanged: (index) { - setState(() { - _currentIndex = index; - }); - }, - scrollDirection: Axis.horizontal, - children: [ - // NewEReferralStepOnePage( - // changePageViewIndex: changePageViewIndex, - // createEReferralRequestModel: createEReferralRequestModel, - // ), - // NewEReferralStepTowPage( - // changePageViewIndex: changePageViewIndex, - // createEReferralRequestModel: createEReferralRequestModel, - // ), - // NewEReferralStepThreePage( - // changePageViewIndex: changePageViewIndex, - // createEReferralRequestModel: createEReferralRequestModel, - // ), - ], - ), - ), - ], - ), - ), - ); - } - - Widget showProgress({required String title, required String status, required Color color, bool isNeedBorder = true}) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Container( - width: 26, - height: 26, - // decoration: containerRadius(color, 200), - child: Icon( - Icons.done, - color: Colors.white, - size: 16, - ), - ), - if (isNeedBorder) - Expanded( - child: Padding( - padding: const EdgeInsets.all(8.0), - child:Divider(), - )), - ], - ), - // mHeight(8), - Text( - title, - style: TextStyle( - fontSize: 11, - fontWeight: FontWeight.w600, - letterSpacing: -0.44, - ), - ), - // mHeight(2), - Container( - padding: EdgeInsets.all(5), - // decoration: containerRadius(color.withOpacity(0.2), 4), - child: Text( - status, - style: TextStyle( - fontSize: 8, - fontWeight: FontWeight.w600, - letterSpacing: -0.32, - color: color, - ), - ), - ), - ], - ) - ], - ); - } -} diff --git a/lib/presentation/e_referral/search_e_referral.dart b/lib/presentation/e_referral/search_e_referral.dart new file mode 100644 index 0000000..1a243da --- /dev/null +++ b/lib/presentation/e_referral/search_e_referral.dart @@ -0,0 +1,119 @@ +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/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_form_manager.dart'; +import 'e_referral_search_result.dart'; + +class SearchEReferralPage extends StatefulWidget { + + const SearchEReferralPage({ + super.key, + }); + + @override + State createState() => _SearchEReferralPageState(); +} + +class _SearchEReferralPageState extends State { + final ReferralFormManager _formManager = ReferralFormManager(); + late HmgServicesViewModel hmgServicesVM; + @override + void initState() { + super.initState(); + _loadData(); + } + + void _handleSearch() { + _formManager.validateSearchForm(); + if (!_formManager.isSearchFormInValid) { + _searchReferral(); + } + } + + void _searchReferral() { + SearchEReferralRequestModel searchEReferralReq; + + if (_formManager.searchCriteria == 0) { + searchEReferralReq = SearchEReferralRequestModel( + identificationNo: _formManager.searchValue, + patientMobileNumber: _formManager.formData.countryEnum.countryCode + _formManager.searchPhone!, + referralNumber: 0, + ); + } else { + + 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 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()], + ), + ); + } + +} diff --git a/lib/presentation/e_referral/widget/e-referral_otp.dart b/lib/presentation/e_referral/widget/e-referral_otp.dart new file mode 100644 index 0000000..390fc71 --- /dev/null +++ b/lib/presentation/e_referral/widget/e-referral_otp.dart @@ -0,0 +1,102 @@ +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/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'; +import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/send_activation_code_ereferral_req_model.dart'; + +class OTPService { + static void openOTPScreen({ + required BuildContext context, + required ReferralFormManager formManager, + required Function onSuccess, + }) { + _showOTPVerificationSheet(context: context, formManager: formManager, onSuccess: onSuccess); + } + + static void _showOTPVerificationSheet({ + required BuildContext context, + required ReferralFormManager formManager, + required Function onSuccess, + }) { + 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, + ), + ), + ], + ), + )), + ); + } +} diff --git a/lib/presentation/e_referral/widget/e_referral_other_details.dart b/lib/presentation/e_referral/widget/e_referral_other_details.dart new file mode 100644 index 0000000..fd1649f --- /dev/null +++ b/lib/presentation/e_referral/widget/e_referral_other_details.dart @@ -0,0 +1,319 @@ +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'; +import 'package:provider/provider.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart'; +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'; +import 'package:hmg_patient_app_new/widgets/image_picker.dart'; + + +class OtherDetailsStep extends StatefulWidget { + const OtherDetailsStep({super.key}); + + @override + State createState() => _OtherDetailsStepState(); +} + +class _OtherDetailsStepState extends State { + final TextEditingController _medicalReportController = TextEditingController(); + final TextEditingController _insuranceController = TextEditingController(); + + late ReferralFormManager _formManager; + + @override + void initState() { + super.initState(); + _formManager = context.read(); + _updateMedicalReportText(); + _updateInsuranceText(); + } + + @override + void didUpdateWidget(OtherDetailsStep oldWidget) { + super.didUpdateWidget(oldWidget); + _updateMedicalReportText(); + _updateInsuranceText(); + } + + void _updateMedicalReportText() { + final hasMedicalReports = _formManager.formData.medicalReportImages.isNotEmpty; + _medicalReportController.text = hasMedicalReports + ? '${_formManager.formData.medicalReportImages.length} file(s) selected'.needTranslation + : ''; + } + + void _updateInsuranceText() { + final hasInsuranceDocs = _formManager.formData.insuredPatientImages.isNotEmpty; + _insuranceController.text = hasInsuranceDocs + ? '${_formManager.formData.insuredPatientImages.length} file(s) selected'.needTranslation + : ''; + + } + + @override + Widget build(BuildContext context) { + return Consumer( + builder: (context, formManager, child) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: ListView( + physics: const BouncingScrollPhysics(), + children: [ + const SizedBox(height: 12), + _buildSectionTitle('Other Details'.needTranslation), + const SizedBox(height: 12), + _buildMedicalReportField(formManager), + _buildBranchField(context, formManager), + _buildInsuranceCheckbox(formManager), + if (formManager.formData.isPatientInsured) _buildInsuranceField(formManager), + ], + ), + ); + }, + ); + } + + Widget _buildSectionTitle(String title) { + return Text( + title, + style: const TextStyle(fontWeight: FontWeight.w700, fontSize: 16) + ).paddingSymmetrical(4, 0); + } + + Widget _buildMedicalReportField(ReferralFormManager formManager) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + InkWell( + child: TextInputWidget( + controller: _medicalReportController, + padding: const EdgeInsets.symmetric(horizontal: 16.0), + hintText: 'Medical Report'.needTranslation, + labelText: 'Select Attachment'.needTranslation, + suffix: const Icon(Icons.attachment), + isReadOnly: true, + errorMessage: formManager.errors.medicalReport, + hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.medicalReport), + ), + onTap: () { + ImageOptions.showImageOptionsNew( + context, + false, + (String image, File file) { + _addMedicalReport(image, file, formManager); + }, + ); + }, + ), + if (formManager.formData.medicalReportImages.isNotEmpty) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), + child: Wrap( + spacing: 8.0, + children: formManager.formData.medicalReportImages.asMap().entries.map((entry) { + final index = entry.key; + return Chip( + label: Text('Medical Report ${index + 1}'.needTranslation), + deleteIcon: const Icon(Icons.close, size: 16), + onDeleted: () { + _removeMedicalReport(index, formManager); + }, + ); + }).toList(), + ), + ), + ], + ).paddingSymmetrical(0, 4); + } + + Widget _buildBranchField(BuildContext context, ReferralFormManager formManager) { + return DropdownWidget( + labelText: 'Branch', + hintText: formManager.formData.branch?.desciption ?? "Select Branch", + isEnable: false, + hasSelectionCustomIcon: true, + labelColor: Colors.black, + padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), + selectionCustomIcon: AppAssets.arrow_down, + leadingIcon: AppAssets.hospital, + dropdownItems: [], + errorMessage: formManager.errors.branch, + hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.branch), + ).paddingSymmetrical(0, 4).onPress(() { + _showBranchBottomSheet(context, formManager); + }); + } + + Widget _buildInsuranceCheckbox(ReferralFormManager formManager) { + return Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Row( + children: [ + Checkbox( + value: formManager.formData.isPatientInsured, + activeColor: Colors.red, + onChanged: (bool? newValue) { + final value = newValue ?? false; + formManager.updateIsPatientInsured(value); + if (!value) { + _updateInsuranceText(); + } + }, + ), + Padding( + padding: EdgeInsets.all(5.0), + child: + "Patient is Insured".needTranslation.toText14( + color: Colors.black, + weight: FontWeight.w600, + ), + // style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600), + //), + ), + ], + ), + ], + ).paddingSymmetrical(0, 4); + } + + Widget _buildInsuranceField(ReferralFormManager formManager) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + InkWell( + child: TextInputWidget( + controller: _insuranceController, + padding: const EdgeInsets.symmetric(horizontal: 16.0), + hintText: 'Insurance Document'.needTranslation, + labelText: 'Select Attachment'.needTranslation, + suffix: const Icon(Icons.attachment), + isReadOnly: true, + errorMessage: formManager.errors.insuredDocument, + hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.insuredDocument), + ), + onTap: () { + ImageOptions.showImageOptionsNew( + context, + false, + (String image, File file) { + _addInsuranceDocument(image, file, formManager); + }, + ); + }, + ), + if (formManager.formData.insuredPatientImages.isNotEmpty) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), + child: Wrap( + spacing: 8.0, + children: formManager.formData.insuredPatientImages.asMap().entries.map((entry) { + final index = entry.key; + return Chip( + label: Text('Insurance ${index + 1}'), + deleteIcon: const Icon(Icons.close, size: 16), + onDeleted: () { + _removeInsuranceDocument(index, formManager); + }, + ); + }).toList(), + ), + ), + ], + ); + } + + void _showBranchBottomSheet(BuildContext context, ReferralFormManager formManager) { + final habibWalletVM = context.read(); + + showCommonBottomSheetWithoutHeight( + context, + title: "Select Branch".needTranslation, + child: Consumer( + builder: (context, habibWalletVM, child) { + final hospitals = habibWalletVM.advancePaymentHospitals; + if (hospitals == null || hospitals.isEmpty) { + return const Center( + child: Padding( + padding: EdgeInsets.all(16.0), + child: Text('No branches available'), + ), + ); + } + + 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: (branch.desciption ?? 'Unknown').toText14(), + onTap: () { + formManager.updateBranch(branch); + Navigator.pop(context); + }, + ); + }, + // separatorBuilder: (context, index) => const Divider(), + itemCount: hospitals.length, + )); + }, + ), + useSafeArea: true, + isFullScreen: false, + isCloseButtonVisible: true, + ); + } + + void _addMedicalReport(String image, File file, ReferralFormManager formManager) { + final newAttachment = EReferralAttachment( + fileName: 'medical_report_${formManager.formData.medicalReportImages.length + 1}.png', + base64String: image + ); + + formManager.addMedicalReport(newAttachment); + _updateMedicalReportText(); + } + + void _removeMedicalReport(int index, ReferralFormManager formManager) { + formManager.removeMedicalReport(index); + _updateMedicalReportText(); + } + + void _addInsuranceDocument(String image, File file, ReferralFormManager formManager) { + final newAttachment = EReferralAttachment( + fileName: 'insurance_${formManager.formData.insuredPatientImages.length + 1}.png', + base64String: image + ); + + formManager.addInsuranceDocument(newAttachment); + _updateInsuranceText(); + } + + void _removeInsuranceDocument(int index, ReferralFormManager formManager) { + formManager.removeInsuranceDocument(index); + _updateInsuranceText(); + } + + @override + void dispose() { + _medicalReportController.dispose(); + _insuranceController.dispose(); + super.dispose(); + } +} \ No newline at end of file diff --git a/lib/presentation/e_referral/widget/e_referral_patient_info.dart b/lib/presentation/e_referral/widget/e_referral_patient_info.dart new file mode 100644 index 0000000..469755c --- /dev/null +++ b/lib/presentation/e_referral/widget/e_referral_patient_info.dart @@ -0,0 +1,235 @@ +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'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart'; +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 PatientInformationStep extends StatefulWidget { + const PatientInformationStep({super.key}); + + @override + State createState() => PatientInformationStepState(); +} + +class PatientInformationStepState extends State { + late TextEditingController _identificationController; + late TextEditingController _nameController; + late TextEditingController _phoneController; + + late FocusNode _identificationFocusNode; + late FocusNode _nameFocusNode; + late FocusNode _phoneFocusNode; + + late ReferralFormManager _formManager; + + @override + void initState() { + super.initState(); + _formManager = context.read(); + + _identificationController = TextEditingController(); + _nameController = TextEditingController(); + _phoneController = TextEditingController(); + + _identificationFocusNode = FocusNode(); + _nameFocusNode = FocusNode(); + _phoneFocusNode = FocusNode(); + + // Initialize controllers with current values + _identificationController.text = _formManager.formData.patientIdentification ?? ''; + _nameController.text = _formManager.formData.patientName ?? ''; + _phoneController.text = _formManager.formData.patientPhone ?? ''; + + // Auto-focus the identification field when the step loads + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted) { + _identificationFocusNode.requestFocus(); + } + }); + } + + @override + Widget build(BuildContext context) { + return Consumer( + builder: (context, formManager, child) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: ListView( + physics: const BouncingScrollPhysics(), + children: [ + const SizedBox(height: 12), + _buildSectionTitle('Patient information'.needTranslation), + const SizedBox(height: 12), + _buildIdentificationField(formManager), + _buildPatientNameField(formManager), + // _buildPatientCountryField(context, formManager), + _buildPatientPhoneField(formManager), + const SizedBox(height: 20), + _buildSectionTitle('Where the patient located'.needTranslation), + _buildPatientCityField(context, formManager), + ], + ), + ); + }, + ); + } + + Widget _buildSectionTitle(String title) { + return Text( + title, + style: const TextStyle(fontWeight: FontWeight.w700, fontSize: 16) + ).paddingSymmetrical(4, 0); + } + + Widget _buildIdentificationField(ReferralFormManager formManager) { + return Focus( + focusNode: _identificationFocusNode, + child: TextInputWidget( + controller: _identificationController, + padding: const EdgeInsets.symmetric(horizontal: 16.0), + hintText: 'Enter Identification Number*'.needTranslation, + labelText: 'Identification Number'.needTranslation, + errorMessage: formManager.errors.patientIdentification, + hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.patientIdentification), + onChange: (value) { + formManager.updatePatientIdentification(value ?? ''); + }, + onSubmitted: (value) { + _nameFocusNode.requestFocus(); + }, + ).paddingSymmetrical(0, 4), + ); + } + + Widget _buildPatientNameField(ReferralFormManager formManager) { + return Focus( + focusNode: _nameFocusNode, + child: TextInputWidget( + controller: _nameController, + padding: const EdgeInsets.symmetric(horizontal: 16.0), + hintText: 'Patient Name*'.needTranslation, + labelText: 'Name'.needTranslation, + keyboardType: TextInputType.text, + errorMessage: formManager.errors.patientName, + hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.patientName), + onChange: (value) { + formManager.updatePatientName(value ?? ''); + }, + onSubmitted: (value) { + // Optionally move to next field or keep focus + }, + ).paddingSymmetrical(0, 4), + ); + } + + Widget _buildPatientPhoneField(ReferralFormManager formManager) { + return Focus( + focusNode: _phoneFocusNode, + child: TextInputWidget( + labelText: 'Phone Number'.needTranslation, + hintText: "5xxxxxxxx", + controller: _phoneController, + padding: const EdgeInsets.all(8), + keyboardType: TextInputType.number, + onChange: (value) { + formManager.updatePatientPhone(value ?? ''); + }, + onCountryChange: (value) { + formManager.updatePatientCountryEnum(value); + }, + prefix: '966', + isBorderAllowed: false, + isAllowLeadingIcon: true, + fontSize: 13, + isCountryDropDown: true, + leadingIcon: AppAssets.smart_phone, + errorMessage: formManager.errors.patientPhone, + hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.patientPhone) + ).paddingSymmetrical(0, 8), + ); + } + + Widget _buildPatientCityField(BuildContext context, ReferralFormManager formManager) { + return DropdownWidget( + labelText: 'City', + hintText: formManager.formData.patientCity?.description ?? "Select City".needTranslation, + isEnable: false, + hasSelectionCustomIcon: true, + labelColor: Colors.black, + padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), + selectionCustomIcon: AppAssets.arrow_down, + leadingIcon: AppAssets.globe, + dropdownItems: [], + errorMessage: formManager.errors.patientCity, + hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.patientCity), + ).paddingSymmetrical(0, 4).onPress(() { + _showCityBottomSheet(context, formManager); + }); + } + + + void _showCityBottomSheet(BuildContext context, ReferralFormManager formManager) { + + showCommonBottomSheetWithoutHeight( + context, + title: "Select City".needTranslation, + child: Consumer( + builder: (context, hmgServicesVM, child) { + final cities = hmgServicesVM.getAllCitiesList; + if (cities == null || cities.isEmpty) { + return const Center( + child: Padding( + padding: EdgeInsets.all(16.0), + child: Text('No cities available'), + ), + ); + } + + 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: (city.description ?? 'Unknown').toText14(), + onTap: () { + formManager.updatePatientCity(city); + Navigator.pop(context); + }, + ); + }, + // separatorBuilder: (context, index) => const Divider(), + itemCount: cities.length, + )); + }, + ), + useSafeArea: true, + isFullScreen: false, + isCloseButtonVisible: true, + ); + } + + @override + void dispose() { + _identificationController.dispose(); + _nameController.dispose(); + _phoneController.dispose(); + _identificationFocusNode.dispose(); + _nameFocusNode.dispose(); + _phoneFocusNode.dispose(); + super.dispose(); + } +} \ No newline at end of file diff --git a/lib/presentation/e_referral/widget/e_referral_requester_form.dart b/lib/presentation/e_referral/widget/e_referral_requester_form.dart new file mode 100644 index 0000000..cc981a9 --- /dev/null +++ b/lib/presentation/e_referral/widget/e_referral_requester_form.dart @@ -0,0 +1,206 @@ +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'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart'; +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 RequesterFormStep extends StatefulWidget { + const RequesterFormStep({super.key}); + + @override + State createState() => RequesterFormStepState(); +} + +class RequesterFormStepState extends State { + late TextEditingController _nameController; + late TextEditingController _phoneController; + late TextEditingController _otherNameController; + + late FocusNode _nameFocusNode; + late FocusNode _phoneFocusNode; + late FocusNode _otherNameFocusNode; + + late ReferralFormManager _formManager; + + @override + void initState() { + super.initState(); + _formManager = context.read(); + + _nameController = TextEditingController(); + _phoneController = TextEditingController(); + _otherNameController = TextEditingController(); + + _nameFocusNode = FocusNode(); + _phoneFocusNode = FocusNode(); + _otherNameFocusNode = FocusNode(); + + // Initialize controllers with current values + _nameController.text = _formManager.formData.requesterName ?? ''; + _phoneController.text = _formManager.formData.requesterPhone ?? ''; + _otherNameController.text = _formManager.formData.otherRelationshipName ?? ''; + + // Auto-focus the name field when the step loads + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted) { + _nameFocusNode.requestFocus(); + } + }); + } + + @override + Widget build(BuildContext context) { + return Consumer( + builder: (context, formManager, child) { + return Padding( + 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), + _buildSectionTitle('Referral requester information'.needTranslation), + const SizedBox(height: 12), + _buildNameField(formManager), + // _buildPhoneField(formManager), + _buildRelationshipField(context, formManager), + if (_showOtherNameField(formManager)) _buildOtherNameField(formManager), + ], + ), + ); + }, + ); + } + + Widget _buildSectionTitle(String title) { + return Text( + title, + style: const TextStyle(fontWeight: FontWeight.w700, fontSize: 16) + ).paddingSymmetrical(4, 0); + } + + Widget _buildNameField(ReferralFormManager formManager) { + return Focus( + focusNode: _nameFocusNode, + child: TextInputWidget( + controller: _nameController, + padding: const EdgeInsets.symmetric(horizontal: 16.0), + hintText: 'Enter Referral Requester Name*'.needTranslation, + labelText: 'Requester Name'.needTranslation, + keyboardType: TextInputType.text, + errorMessage: formManager.errors.requesterName, + isAllowLeadingIcon: true, + leadingIcon: AppAssets.user_circle, + hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.requesterName), + onChange: (value) { + formManager.updateRequesterName(value ?? ''); + }, + + ).paddingSymmetrical(0, 8), + ); + } + Widget _buildRelationshipField(BuildContext context, ReferralFormManager formManager) { + return DropdownWidget( + labelText: "Relationship".needTranslation, + hintText: formManager.formData.relationship?.textEn ?? "Select Relation".needTranslation, + isEnable: false, + selectedValue: formManager.formData.relationship?.textEn ?? "Select Relation".needTranslation, + errorMessage: formManager.errors.relationship, + hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.relationship), + hasSelectionCustomIcon: false, + labelColor: Colors.black, + padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), + leadingIcon: AppAssets.user_circle, + dropdownItems: [], + ).paddingSymmetrical(0, 8).onPress(() { + _showRelationshipBottomSheet(context, formManager); + }); + } + + Widget _buildOtherNameField(ReferralFormManager formManager) { + return Focus( + focusNode: _otherNameFocusNode, + child: TextInputWidget( + controller: _otherNameController, + keyboardType: TextInputType.text, + padding: const EdgeInsets.symmetric(horizontal: 16.0), + hintText: 'Other Name*'.needTranslation, + labelText: 'Other Name'.needTranslation, + errorMessage: formManager.errors.otherRelationshipName, + onChange: (value) { + formManager.updateOtherRelationshipName(value ?? ''); + }, + ).paddingSymmetrical(0, 4), + ); + } + + bool _showOtherNameField(ReferralFormManager formManager) { + return formManager.formData.relationship != null && + formManager.formData.relationship?.iD == 5; + } + + void _showRelationshipBottomSheet(BuildContext context, ReferralFormManager formManager) { + final hmgServicesVM = context.read(); + + showCommonBottomSheetWithoutHeight( + context, + title: "Select Relation".needTranslation, + child: Consumer( + builder: (context, hmgServicesVM, child) { + if (hmgServicesVM.relationTypes.isEmpty) { + return const Center( + child: Padding( + padding: EdgeInsets.all(16.0), + child: Text('No relationships available'), + ), + ); + } + + 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:relationship.textEn?.toText14(), + onTap: () { + formManager.updateRelationship(relationship); + Navigator.pop(context); + }, + ); + }, + //separatorBuilder: (context, index) => const Divider(), + itemCount: hmgServicesVM.relationTypes.length, + )); + }, + ), + isFullScreen: false, + isCloseButtonVisible: true, + ); + } + + @override + void dispose() { + _nameController.dispose(); + _phoneController.dispose(); + _otherNameController.dispose(); + _nameFocusNode.dispose(); + _phoneFocusNode.dispose(); + _otherNameFocusNode.dispose(); + super.dispose(); + } +} \ 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 new file mode 100644 index 0000000..fa3ae19 --- /dev/null +++ b/lib/presentation/e_referral/widget/search_e_referral_form.dart @@ -0,0 +1,199 @@ +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'; +import 'package:hmg_patient_app_new/presentation/e_referral/e_referral_form_manager.dart'; +import 'package:provider/provider.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; +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 { + final VoidCallback? onFormValidated; + const SearchEReferralFormForm({super.key, this.onFormValidated}); + + @override + State createState() => SearchEReferralFormFormState(); +} + +class SearchEReferralFormFormState extends State { + late TextEditingController _searchController; + late TextEditingController _phoneController; + late FocusNode _searchFocusNode; + late FocusNode _phoneFocusNode; + + @override + void initState() { + super.initState(); + + _searchController = TextEditingController(); + _phoneController = TextEditingController(); + _searchFocusNode = FocusNode(); + _phoneFocusNode = FocusNode(); + + // Initialize controllers with current values from form manager + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted) { + final formManager = context.read(); + _searchController.text = formManager.searchValue ?? ''; + _phoneController.text = formManager.searchPhone ?? ''; + // _searchFocusNode.requestFocus(); + } + }); + } + + @override + Widget build(BuildContext context) { + return Consumer( + builder: (context, formManager, child) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 24.0), + child: ListView( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + children: [ + // const SizedBox(height: 12), + _buildSelectionField(context, formManager), + _buildNameField(formManager), + _buildPhoneField(formManager), + ], + ), + ); + }, + ); + } + + Widget _buildNameField(ReferralFormManager formManager) { + return Focus( + focusNode: _searchFocusNode, + autofocus: true, + child: TextInputWidget( + controller: _searchController, + padding: const EdgeInsets.symmetric(horizontal: 16.0), + 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) { + formManager.updateSearchValue(value ?? ''); + //_validateForm(formManager); + }, + ).paddingSymmetrical(0, 8), + ); + } + + 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.updateSearchPhone(value ?? ''); + // _validateForm(formManager); + }, + // onSubmitted: (value) { + // formManager.updateRequesterPhone(value ?? ''); + // _validateForm(formManager); + // }, + onCountryChange: (value) { + formManager.updateCountryEnum(value); + // _validateForm(formManager); + }, + prefix: '966', + isBorderAllowed: false, + isAllowLeadingIcon: true, + fontSize: 13, + isCountryDropDown: true, + leadingIcon: AppAssets.smart_phone, + errorMessage: formManager.errors.searchPhone, + hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.searchPhone), + ).paddingSymmetrical(0, 8), + ); + } + + Widget _buildSelectionField(BuildContext context, ReferralFormManager formManager) { + return DropdownWidget( + labelText: "Select the Search Criteria", + hintText: formManager.searchCriteria == 0 ? "Identification Number" : "Referral Number", + isEnable: false, + hasSelectionCustomIcon: false, + labelColor: Colors.black, + padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), + leadingIcon: AppAssets.search_icon, + dropdownItems: [], + ).paddingSymmetrical(0, 8).onPress(() { + _showCriteriaBottomSheet(context, formManager); + }); + } + + 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) { + 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, + ), + isFullScreen: false, + isCloseButtonVisible: true, + ); + } + + void _validateForm(ReferralFormManager formManager) { + // Trigger validation + formManager.validateSearchForm(); + + // Notify parent if form validation state changes + widget.onFormValidated?.call(); + } + + @override + void dispose() { + _searchController.dispose(); + _phoneController.dispose(); + _searchFocusNode.dispose(); + _phoneFocusNode.dispose(); + super.dispose(); + } +} 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/presentation/hmg_services/services_page.dart b/lib/presentation/hmg_services/services_page.dart index af576aa..fd6e976 100644 --- a/lib/presentation/hmg_services/services_page.dart +++ b/lib/presentation/hmg_services/services_page.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_export.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/models/ui_models/hmg_services_component_model.dart'; import 'package:hmg_patient_app_new/presentation/hmg_services/services_view.dart'; @@ -22,7 +23,7 @@ class ServicesPage extends StatelessWidget { route: AppRoutes.eReferralPage, ), HmgServicesComponentModel( - 12, + 5, "Comprehensive Checkup".needTranslation, "".needTranslation, AppAssets.comprehensiveCheckup, @@ -32,7 +33,7 @@ class ServicesPage extends StatelessWidget { route: AppRoutes.comprehensiveCheckupPage, ), HmgServicesComponentModel( - 12, + 3, "Home Health Care".needTranslation, "".needTranslation, AppAssets.emergency_services_icon, @@ -41,6 +42,21 @@ class ServicesPage extends StatelessWidget { textColor: AppColors.blackColor, route: AppRoutes.homeHealthCarePage, ), + HmgServicesComponentModel( + 11, + "Virtual Tour".needTranslation, + "".needTranslation, + AppAssets.my_address, + true, + bgColor: Colors.orange, + textColor: AppColors.blackColor, + route: null, + onTap:(){ + Utils.openWebView( + url: 'https://hmgwebservices.com/vt_mobile/html/index.html', + ); + }, + ) ]; @override @@ -49,7 +65,7 @@ class ServicesPage extends StatelessWidget { title: "Explore Services".needTranslation, isLeading: Navigator.canPop(context), child: Padding( - padding: EdgeInsets.all(24.h), + padding: EdgeInsets.symmetric(horizontal: 24.h), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -60,9 +76,9 @@ class ServicesPage extends StatelessWidget { child: GridView.builder( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 3, // 4 icons per row - crossAxisSpacing: 16.w, - mainAxisSpacing: 24.h, - childAspectRatio: 0.75, + crossAxisSpacing: 24.w, + mainAxisSpacing: 0.h, + childAspectRatio: 0.85, ), physics: NeverScrollableScrollPhysics(), shrinkWrap: true, diff --git a/lib/presentation/hmg_services/services_view.dart b/lib/presentation/hmg_services/services_view.dart index 225bd96..2cd923f 100644 --- a/lib/presentation/hmg_services/services_view.dart +++ b/lib/presentation/hmg_services/services_view.dart @@ -11,13 +11,19 @@ class ServiceGridViewItem extends StatelessWidget { final int index; final bool isHomePage; final bool isLocked; - - const ServiceGridViewItem(this.hmgServiceComponentModel, this.index, this.isHomePage, {super.key, this.isLocked = false}); + final Function? onTap; + const ServiceGridViewItem(this.hmgServiceComponentModel, this.index, this.isHomePage, {super.key, this.isLocked = false, this.onTap}); @override Widget build(BuildContext context) { return InkWell( - onTap: () => getIt.get().pushPageRoute(hmgServiceComponentModel.route), + onTap: () { + hmgServiceComponentModel.route != null + ? getIt.get().pushPageRoute(hmgServiceComponentModel.route!) + : hmgServiceComponentModel.onTap != null + ? hmgServiceComponentModel.onTap!() + : null; + }, child: Column( mainAxisSize: MainAxisSize.max, crossAxisAlignment: CrossAxisAlignment.start, @@ -33,6 +39,7 @@ class ServiceGridViewItem extends StatelessWidget { ), child: Utils.buildSvgWithAssets( icon: hmgServiceComponentModel.icon, + iconColor: hmgServiceComponentModel.iconColor, height: 21.h, width: 21.w, fit: BoxFit.none, diff --git a/lib/presentation/home/navigation_screen.dart b/lib/presentation/home/navigation_screen.dart index 18803cc..f8447d2 100644 --- a/lib/presentation/home/navigation_screen.dart +++ b/lib/presentation/home/navigation_screen.dart @@ -33,7 +33,7 @@ class _LandingNavigationState extends State { appState.isAuthenticated ? MedicalFilePage() : /* need add feedback page */ FeedbackPage(), BookAppointmentPage(), const ToDoPage(), - appState.isAuthenticated ? /* need add news page */ ServicesPage() : const LandingPage(), + ServicesPage(), ], ), bottomNavigationBar: BottomNavigation( 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/attachment_options.dart b/lib/widgets/attachment_options.dart index 64c2485..be443f8 100644 --- a/lib/widgets/attachment_options.dart +++ b/lib/widgets/attachment_options.dart @@ -45,7 +45,7 @@ class AttachmentOptions extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ SvgPicture.asset( - "assets/images/$icon", + "assets/images/svg/$icon", ), title.toText11(isBold: true), ], diff --git a/lib/widgets/dropdown/dropdown_widget.dart b/lib/widgets/dropdown/dropdown_widget.dart index b4062e4..5ef5e58 100644 --- a/lib/widgets/dropdown/dropdown_widget.dart +++ b/lib/widgets/dropdown/dropdown_widget.dart @@ -19,7 +19,8 @@ class DropdownWidget extends StatelessWidget { final String? selectionCustomIcon; final String? leadingIcon; final Color? labelColor; - + final String? errorMessage; + final bool? hasError; const DropdownWidget( {Key? key, required this.labelText, @@ -34,7 +35,10 @@ class DropdownWidget extends StatelessWidget { this.hasSelectionCustomIcon = false, this.selectionCustomIcon, this.leadingIcon, - this.labelColor}) + this.labelColor, + this.errorMessage, + this.hasError =false + }) : super(key: key); @override @@ -42,16 +46,18 @@ class DropdownWidget extends StatelessWidget { Widget content = Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, - children: [_buildLabelText(labelColor), _buildDropdown(context)], + children: [_buildLabelText(labelColor), _buildDropdown(context),], ); - return Container( + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [Container( padding: padding, alignment: Alignment.center, // This might need adjustment based on layout decoration: RoundedRectangleBorder().toSmoothCornerDecoration( color: Colors.white, borderRadius: isAllowRadius ? 15.h : null, - side: isBorderAllowed ? BorderSide(color: const Color(0xffefefef), width: 1) : null, + side: isBorderAllowed ? BorderSide(color: hasError! ? Colors.red: const Color(0xffefefef), width: 1) : null, ), child: Row( // Wrap with a Row @@ -62,9 +68,21 @@ class DropdownWidget extends StatelessWidget { SizedBox(width: 3.h), ], Expanded(child: content), + ], ), - ); + ), + if (hasError! && errorMessage != null) + Padding( + padding: EdgeInsets.only(top: 4.h, left: 12.h), // Adjust padding as needed + child: Text( + errorMessage!, + style: TextStyle( + color: Colors.red, + fontSize: 12.f, + ), + ), + )]); } Widget _buildLeadingIcon() { 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