From c43f96c619ff15efcb5f5ee5e20e0221b074f1ff Mon Sep 17 00:00:00 2001 From: Sultan khan Date: Tue, 18 Nov 2025 11:44:25 +0300 Subject: [PATCH 1/5] e-referral work in progress --- lib/core/api_consts.dart | 6 +- .../models/create_e_referral_model.dart | 150 +++++ .../e_referral/e_referral_page_home.dart | 113 ++-- lib/presentation/e_referral/new_referral.dart | 540 +++++++++++++----- .../widgets/pickup_location.dart | 194 ++++--- .../radiology/radiology_orders_page.dart | 1 + 6 files changed, 703 insertions(+), 301 deletions(-) create mode 100644 lib/features/hmg_services/models/create_e_referral_model.dart diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index b21d329..943ecf3 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/'; @@ -719,7 +719,7 @@ var GET_PRESCRIPTION_INSTRUCTIONS_PDF = 'Services/ChatBot_Service.svc/REST/Chatb class ApiConsts { static const maxSmallScreen = 660; - static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.uat; + static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.prod; // static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT diff --git a/lib/features/hmg_services/models/create_e_referral_model.dart b/lib/features/hmg_services/models/create_e_referral_model.dart new file mode 100644 index 0000000..0672fe6 --- /dev/null +++ b/lib/features/hmg_services/models/create_e_referral_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/presentation/e_referral/e_referral_page_home.dart b/lib/presentation/e_referral/e_referral_page_home.dart index bacca47..3eb1255 100644 --- a/lib/presentation/e_referral/e_referral_page_home.dart +++ b/lib/presentation/e_referral/e_referral_page_home.dart @@ -1,15 +1,19 @@ import 'dart:ui'; - +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/cupertino.dart'; 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/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/features/contact_us/contact_us_view_model.dart'; import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.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:hmg_patient_app_new/widgets/custom_tab_bar.dart'; import 'package:provider/provider.dart'; class EReferralPage extends StatefulWidget { @@ -19,10 +23,7 @@ class EReferralPage extends StatefulWidget { _EReferralPageState createState() => _EReferralPageState(); } -class _EReferralPageState extends State - { - - +class _EReferralPageState extends State { @override void initState() { super.initState(); @@ -32,66 +33,52 @@ class _EReferralPageState extends State void dispose() { super.dispose(); } + bool isNewReferral = true; + VoidCallback? onNextStep; @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(), - ], - ); - }), - ), - ), - ); - - - - + return Scaffold( + backgroundColor: AppColors.bgScaffoldColor, + body: CollapsingListView( + title: "E Referral".needTranslation, + child: Consumer(builder: (context, contactUsVM, child) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 16.h), + contactUsVM.isHMGLocationsListLoading + ? SizedBox.shrink() + : CustomTabBar( + activeTextColor: AppColors.primaryRedColor, + activeBackgroundColor: AppColors.primaryRedColor.withValues(alpha: .1), + tabs: [ + CustomTabBarModel(null, "New Referral".needTranslation), + CustomTabBarModel(null, "Search Referral".needTranslation), + ], + onTabChange: (index) {}, + ).paddingSymmetrical(24.h, 0.h), + SizedBox(height: 24.h), + NewReferralPage(onNextStep: (nextStep) { + WidgetsBinding.instance.addPostFrameCallback((_) { + setState(() { + onNextStep = nextStep; + }); + }); + }) + ], + ); + }), + ), + bottomNavigationBar: Padding( + padding: EdgeInsets.all(16.h), + child: CustomButton( + text: LocaleKeys.next.tr(), + onPressed:onNextStep ?? () {}, + backgroundColor: AppColors.primaryRedColor, + borderColor: AppColors.primaryRedColor, + textColor: AppColors.whiteColor, + )), + ); } } diff --git a/lib/presentation/e_referral/new_referral.dart b/lib/presentation/e_referral/new_referral.dart index 4ed9b8e..73c4f5c 100644 --- a/lib/presentation/e_referral/new_referral.dart +++ b/lib/presentation/e_referral/new_referral.dart @@ -1,172 +1,432 @@ +// dart +// File: lib/presentation/e_referral/new_referral_page.dart + +import 'dart:io'; + +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/app_export.dart'; +import 'package:hmg_patient_app_new/core/app_state.dart'; +import 'package:hmg_patient_app_new/core/common_models/nationality_country_model.dart'; +import 'package:hmg_patient_app_new/core/dependencies.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/authentication/authentication_view_model.dart'; +import 'package:hmg_patient_app_new/features/hmg_services/models/create_e_referral_model.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; +import 'package:hmg_patient_app_new/widgets/dropdown/dropdown_widget.dart'; +import 'package:hmg_patient_app_new/widgets/image_picker.dart'; +import 'package:hmg_patient_app_new/widgets/input_widget.dart'; +import 'package:provider/provider.dart'; -class NewEReferral extends StatefulWidget { - NewEReferral(); +class NewReferralPage extends StatefulWidget { + final Function(VoidCallback) onNextStep; + const NewReferralPage({super.key, required this.onNextStep}); @override - _NewEReferralState createState() => _NewEReferralState(); + State createState() => NewReferralPageState(); } -class _NewEReferralState extends State with TickerProviderStateMixin { - late PageController _controller; - int _currentIndex = 0; - int pageSelected = 2; - - // CreateEReferralRequestModel createEReferralRequestModel = new CreateEReferralRequestModel(); +class NewReferralPageState extends State { + final PageController _pageController = PageController(); + int pageIndex = 0; + int _tabIndex = 0; + bool isPatientInsured =false; + final TextEditingController _nameController = TextEditingController(); + final TextEditingController _phoneController = TextEditingController(); + String _country = 'Saudi Arabia'; + String? _relationship; + List medicalReportImages = []; + List insuredPatientImages = []; + void nextPressed() { + if (pageIndex < 2) { + _pageController.nextPage(duration: const Duration(milliseconds: 300), curve: Curves.easeInOut); + } else { + // submit logic + } + } @override - void initState() { + initState() { super.initState(); - _controller = new PageController(); + widget.onNextStep((){ + nextPressed(); + }); + } - @override - void dispose() { - super.dispose(); + Widget _progressStep({required String title, required bool active, bool showDivider = true}) { + final Color activeColor = active ? AppColors.primaryRedColor : Colors.grey.shade400; + return Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + + children: [ + CircleAvatar( + radius: 13, + backgroundColor: active ? activeColor : Colors.grey.shade300, + child: Icon(Icons.check, size: 14, color: Colors.white), + ), + if (showDivider) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: Divider(thickness: 1), + ), + + ], + ), + const SizedBox(height: 6), + Text(title, style: const TextStyle(fontSize: 11, fontWeight: FontWeight.w600)), + const SizedBox(height: 6), + Container( + padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4), + decoration: BoxDecoration( + color: active ? activeColor.withOpacity(0.15) : Colors.grey.shade100, + borderRadius: BorderRadius.circular(6), + ), + child: Text(active ? 'Active' : 'Inactive', + style: TextStyle(fontSize: 9, color: active ? activeColor : Colors.grey)), + ), + ], + ), + ); + } + + Widget _requesterForm() { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 24.0), + child: ListView( + physics: const BouncingScrollPhysics(), + children: [ + const SizedBox(height: 12), + const Text('Referral requester information', + style: TextStyle(fontWeight: FontWeight.w700, fontSize: 16)).paddingSymmetrical(4.h, 0.h), + const SizedBox(height: 12), + TextInputWidget( + controller: _nameController, + padding: const EdgeInsets.symmetric(horizontal: 16.0), + hintText: 'Enter Referral Requester Name*', labelText: 'Requester Name', + ).paddingSymmetrical(0.h, 4.h), + + Selector? countriesList, NationalityCountries? selectedCountry, bool isArabic})>( + selector: (context, authViewModel) { + final appState = getIt.get(); + return ( + countriesList: authViewModel.countriesList, + selectedCountry: authViewModel.pickedCountryByUAEUser, + isArabic: appState.isArabic(), + ); + }, + shouldRebuild: (previous, next) => previous.countriesList != next.countriesList || previous.selectedCountry != next.selectedCountry || previous.isArabic != next.isArabic, + builder: (context, data, child) { + final authVM = context.read(); + return DropdownWidget( + labelText: LocaleKeys.country.tr(), + hintText:_country, + isEnable: true, + dropdownItems: (data.countriesList ?? []).map((e) => data.isArabic ? e.nameN ?? "" : e.name ?? "").toList(), + selectedValue: data.selectedCountry != null + ? data.isArabic + ? data.selectedCountry!.nameN ?? "" + : data.selectedCountry!.name ?? "" + : "", + onChange: authVM.onUAEUserCountrySelection, + hasSelectionCustomIcon: true, + labelColor: AppColors.textColor, + padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), + selectionCustomIcon: AppAssets.arrow_down, + leadingIcon: AppAssets.globe, + ).withVerticalPadding(8); + }, + ).paddingSymmetrical(0.h, 4.h), + + TextInputWidget( + labelText: LocaleKeys.mobileNumber.tr(), + hintText: LocaleKeys.mobileNumber.tr(), + controller: null, + isEnable: true, + prefix: null, + isAllowLeadingIcon: true, + labelColor: AppColors.textColor, padding: const EdgeInsets.symmetric(horizontal: 16.0), + isReadOnly: true, + leadingIcon: AppAssets.call).paddingSymmetrical(0.h, 4.h), + + Selector? countriesList, NationalityCountries? selectedCountry, bool isArabic})>( + selector: (context, authViewModel) { + final appState = getIt.get(); + return ( + countriesList: authViewModel.countriesList, + selectedCountry: authViewModel.pickedCountryByUAEUser, + isArabic: appState.isArabic(), + ); + }, + shouldRebuild: (previous, next) => previous.countriesList != next.countriesList || previous.selectedCountry != next.selectedCountry || previous.isArabic != next.isArabic, + builder: (context, data, child) { + final authVM = context.read(); + return DropdownWidget( + labelText: "Relationship", + hintText: "Relationship*".needTranslation, + isEnable: true, + dropdownItems: (data.countriesList ?? []).map((e) => data.isArabic ? e.nameN ?? "" : e.name ?? "").toList(), + selectedValue: data.selectedCountry != null + ? data.isArabic + ? data.selectedCountry!.nameN ?? "" + : data.selectedCountry!.name ?? "" + : "", + onChange: authVM.onUAEUserCountrySelection, + hasSelectionCustomIcon: true, + labelColor: AppColors.textColor, + padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), + selectionCustomIcon: AppAssets.arrow_down, + leadingIcon: AppAssets.globe, + ).withVerticalPadding(8); + }, + ).paddingSymmetrical(0.h, 4.h), + + const SizedBox(height: 120), + ], + ), + ); } - changePageViewIndex(pageIndex) { - _controller.jumpToPage(pageIndex); + Widget _patientInformation(){ + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: ListView( + physics: const BouncingScrollPhysics(), + children: [ + const SizedBox(height: 12), + 'Patient information'.toText16(weight: FontWeight.bold).paddingSymmetrical(4.h, 0.h), + const SizedBox(height: 12), + TextInputWidget( + controller: _nameController, + padding: const EdgeInsets.symmetric(horizontal: 16.0), + hintText: 'Enter Identification Number*', labelText: 'Identification Number ', + ).paddingSymmetrical(0.h, 4.h), + + TextInputWidget( + controller: _nameController, + padding: const EdgeInsets.symmetric(horizontal: 16.0), + hintText: 'Patient Name*', labelText: 'Name', + ).paddingSymmetrical(0.h, 4.h), + + Selector? countriesList, NationalityCountries? selectedCountry, bool isArabic})>( + selector: (context, authViewModel) { + final appState = getIt.get(); + return ( + countriesList: authViewModel.countriesList, + selectedCountry: authViewModel.pickedCountryByUAEUser, + isArabic: appState.isArabic(), + ); + }, + shouldRebuild: (previous, next) => previous.countriesList != next.countriesList || previous.selectedCountry != next.selectedCountry || previous.isArabic != next.isArabic, + builder: (context, data, child) { + final authVM = context.read(); + return DropdownWidget( + labelText: LocaleKeys.country.tr(), + hintText:_country, + isEnable: true, + dropdownItems: (data.countriesList ?? []).map((e) => data.isArabic ? e.nameN ?? "" : e.name ?? "").toList(), + selectedValue: data.selectedCountry != null + ? data.isArabic + ? data.selectedCountry!.nameN ?? "" + : data.selectedCountry!.name ?? "" + : "", + onChange: authVM.onUAEUserCountrySelection, + hasSelectionCustomIcon: true, + labelColor: AppColors.textColor, + padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), + selectionCustomIcon: AppAssets.arrow_down, + leadingIcon: AppAssets.globe, + ).withVerticalPadding(8); + }, + ).paddingSymmetrical(0.h, 4.h), + + + 'Where the patient located'.needTranslation.toText16(weight: FontWeight.bold).paddingSymmetrical(4.h, 0.h), + + Selector? countriesList, NationalityCountries? selectedCountry, bool isArabic})>( + selector: (context, authViewModel) { + final appState = getIt.get(); + return ( + countriesList: authViewModel.countriesList, + selectedCountry: authViewModel.pickedCountryByUAEUser, + isArabic: appState.isArabic(), + ); + }, + shouldRebuild: (previous, next) => previous.countriesList != next.countriesList || previous.selectedCountry != next.selectedCountry || previous.isArabic != next.isArabic, + builder: (context, data, child) { + final authVM = context.read(); + return DropdownWidget( + labelText: LocaleKeys.country.tr(), + hintText:_country, + isEnable: true, + dropdownItems: (data.countriesList ?? []).map((e) => data.isArabic ? e.nameN ?? "" : e.name ?? "").toList(), + selectedValue: data.selectedCountry != null + ? data.isArabic + ? data.selectedCountry!.nameN ?? "" + : data.selectedCountry!.name ?? "" + : "", + onChange: authVM.onUAEUserCountrySelection, + hasSelectionCustomIcon: true, + labelColor: AppColors.textColor, + padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), + selectionCustomIcon: AppAssets.arrow_down, + leadingIcon: AppAssets.globe, + ).withVerticalPadding(8); + }, + ).paddingSymmetrical(0.h, 4.h), + + ])); } - @override - Widget build(BuildContext context) { - return Scaffold( - body: Container( - height: double.infinity, + Widget _otherDetails() { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: ListView( + physics: const BouncingScrollPhysics(), + children: [ + const SizedBox(height: 12), + 'Other Details'.toText16(weight: FontWeight.bold).paddingSymmetrical(4.h, 0.h), + const SizedBox(height: 12), + + InkWell(child: TextInputWidget( + controller: _nameController, + padding: const EdgeInsets.symmetric(horizontal: 16.0), + hintText: 'Medical Report', labelText: 'Select Attachment', + suffix: Icon(Icons.attachment), + isReadOnly: true, + + ), + onTap: (){ + ImageOptions.showImageOptionsNew( + context, + true, + (String image, File file) { + setState(() { + EReferralAttachment eReferralAttachment = new EReferralAttachment(fileName: 'image ${medicalReportImages.length + 1}.png', base64String: image); + medicalReportImages.add(eReferralAttachment); + }); + }, + ); + }, + ).paddingSymmetrical(0.h, 4.h), + + Selector? countriesList, NationalityCountries? selectedCountry, bool isArabic})>( + selector: (context, authViewModel) { + final appState = getIt.get(); + return ( + countriesList: authViewModel.countriesList, + selectedCountry: authViewModel.pickedCountryByUAEUser, + isArabic: appState.isArabic(), + ); + }, + shouldRebuild: (previous, next) => previous.countriesList != next.countriesList || previous.selectedCountry != next.selectedCountry || previous.isArabic != next.isArabic, + builder: (context, data, child) { + final authVM = context.read(); + return DropdownWidget( + labelText: LocaleKeys.branch.tr(), + hintText:_country, + isEnable: true, + dropdownItems: (data.countriesList ?? []).map((e) => data.isArabic ? e.nameN ?? "" : e.name ?? "").toList(), + selectedValue: data.selectedCountry != null + ? data.isArabic + ? data.selectedCountry!.nameN ?? "" + : data.selectedCountry!.name ?? "" + : "", + onChange: authVM.onUAEUserCountrySelection, + hasSelectionCustomIcon: true, + labelColor: AppColors.textColor, + padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), + selectionCustomIcon: AppAssets.arrow_down, + leadingIcon: AppAssets.hospital, + ).withVerticalPadding(8); + }, + ).paddingSymmetrical(0.h, 4.h), + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Row( + children: [ + Checkbox( + value: isPatientInsured, + activeColor: AppColors.primaryRedColor, + onChanged: (bool? newValue) { + setState(() { + isPatientInsured = newValue!; + }); + }), + Padding( + padding: const EdgeInsets.all(5.0), + child: Text( + "Patient is Insured".needTranslation, + style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600), + ), + ), + ], + ), + ], + ).paddingSymmetrical(0.h, 4.h), + + isPatientInsured? InkWell(child: TextInputWidget( + controller: _nameController, + padding: const EdgeInsets.symmetric(horizontal: 16.0), + hintText: 'Insurance Document', labelText: 'Select Attachment', + suffix: Icon(Icons.attachment), + isReadOnly: true, + + )) : SizedBox(), + ])); + } + @override + Widget build(BuildContext context) { + final bool step0Active = pageIndex == 0; + final bool step1Active = pageIndex == 1; + final bool step2Active = pageIndex == 2; + + return SizedBox( + height: MediaQuery + .of(context) + .size + .height, // constrain height child: Column( children: [ - Container( - width: double.infinity, - padding: EdgeInsets.only(left: 12,right: 12,top: 12), + const SizedBox(height: 8), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 24.0), child: Row( + mainAxisAlignment: MainAxisAlignment.center, 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, - ), + _progressStep(title: 'Requester Info', active: step0Active), + _progressStep(title: 'Patient Information', active: step1Active), + _progressStep( + title: 'Other details', active: step2Active, showDivider: 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, - // ), + controller: _pageController, + physics: const NeverScrollableScrollPhysics(), + onPageChanged: (i) => setState(() => pageIndex = i), + children: [ + _requesterForm(), + _patientInformation(), + // const Center(child: Text('Patient Info - step 2 (placeholder)')), + _otherDetails(), ], ), ), - ], - ), - ), - ); - } - 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/emergency_services/call_ambulance/widgets/pickup_location.dart b/lib/presentation/emergency_services/call_ambulance/widgets/pickup_location.dart index 5d073d4..2363644 100644 --- a/lib/presentation/emergency_services/call_ambulance/widgets/pickup_location.dart +++ b/lib/presentation/emergency_services/call_ambulance/widgets/pickup_location.dart @@ -39,54 +39,56 @@ class PickupLocation extends StatelessWidget { 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 - .toText12(color: AppColors.textColor) - ], - ).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 - .toText12(color: AppColors.textColor) - ], - ).onPress((){ - context - .read() - .updateCallingPlace(AmbulanceCallingPlace.FROM_HOSPITAL); - }), - ], - ), + // Use a plain Row with Radios and provide required groupValue and onChanged + Row( + mainAxisAlignment: MainAxisAlignment.start, + spacing: 24.h, + children: [ + Row( + children: [ + Radio( + value: AmbulanceCallingPlace.TO_HOSPITAL, + groupValue: value, + onChanged: (AmbulanceCallingPlace? v) { + if (v != null) { + context.read().updateCallingPlace(v); + } + }, + activeColor: AppColors.primaryRedColor, + fillColor: MaterialStateProperty.all(AppColors.primaryRedColor), + ), + "To Hospital" + .needTranslation + .toText12(color: AppColors.textColor) + ], + ).onPress(() { + context + .read() + .updateCallingPlace(AmbulanceCallingPlace.TO_HOSPITAL); + }), + Row( + children: [ + Radio( + value: AmbulanceCallingPlace.FROM_HOSPITAL, + groupValue: value, + onChanged: (AmbulanceCallingPlace? v) { + if (v != null) { + context.read().updateCallingPlace(v); + } + }, + activeColor: AppColors.primaryRedColor, + fillColor: MaterialStateProperty.all(AppColors.primaryRedColor), + ), + "From Hospital" + .needTranslation + .toText12(color: AppColors.textColor) + ], + ).onPress(() { + context + .read() + .updateCallingPlace(AmbulanceCallingPlace.FROM_HOSPITAL); + }), + ], ), Visibility( visible: value == AmbulanceCallingPlace.TO_HOSPITAL, @@ -100,53 +102,55 @@ class PickupLocation extends StatelessWidget { "Select Way" .needTranslation .toText14(color: AppColors.textColor, weight: FontWeight.w600), - RadioGroup( - groupValue: directionValue, - onChanged: (value) { - context - .read() - .updateDirection(value); - }, - 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) - ], - ).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 - .toText12(color: AppColors.textColor) - ], - ).onPress((){ - context - .read() - .updateDirection(AmbulanceDirection.TWO_WAY); - }), - ], - ), + // Use a Row and assign groupValue/onChanged to each Radio + Row( + mainAxisAlignment: MainAxisAlignment.start, + spacing: 24.h, + children: [ + Row( + children: [ + Radio( + value: AmbulanceDirection.ONE_WAY, + groupValue: directionValue, + onChanged: (AmbulanceDirection? v) { + if (v != null) { + context.read().updateDirection(v); + } + }, + activeColor: AppColors.primaryRedColor, + fillColor: MaterialStateProperty.all(AppColors.primaryRedColor), + ), + "One Way" + .needTranslation + .toText12(color: AppColors.textColor) + ], + ).onPress(() { + context + .read() + .updateDirection(AmbulanceDirection.ONE_WAY); + }), + Row( + children: [ + Radio( + value: AmbulanceDirection.TWO_WAY, + groupValue: directionValue, + onChanged: (AmbulanceDirection? v) { + if (v != null) { + context.read().updateDirection(v); + } + }, + fillColor: MaterialStateProperty.all(AppColors.primaryRedColor), + ), + "Two Way" + .needTranslation + .toText12(color: AppColors.textColor) + ], + ).onPress(() { + context + .read() + .updateDirection(AmbulanceDirection.TWO_WAY); + }), + ], ), ], ); diff --git a/lib/presentation/radiology/radiology_orders_page.dart b/lib/presentation/radiology/radiology_orders_page.dart index 1d7dfe2..1c3cad4 100644 --- a/lib/presentation/radiology/radiology_orders_page.dart +++ b/lib/presentation/radiology/radiology_orders_page.dart @@ -10,6 +10,7 @@ import 'package:hmg_patient_app_new/core/utils/size_utils.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/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart'; import 'package:hmg_patient_app_new/presentation/radiology/search_radiology.dart'; From d8a9d8443a732213643808c7c258dbb8a85589ec Mon Sep 17 00:00:00 2001 From: Sultan khan Date: Sun, 23 Nov 2025 11:56:53 +0300 Subject: [PATCH 2/5] e-referral work done search-referral in progress. --- lib/core/api_consts.dart | 24 +- lib/core/utils/validation_utils.dart | 14 +- .../hmg_services/hmg_services_repo.dart | 238 ++++++++++ .../hmg_services/hmg_services_view_model.dart | 189 ++++++++ ...check_activation_e_referral_req_model.dart | 57 +++ .../req_models/create_e_referral_model.dart | 155 +++++++ .../create_e_referral_req_model.dart} | 0 ...d_activation_code_ereferral_req_model.dart | 56 +++ .../get_all_cities_resp_model.dart | 21 + .../relationship_type_resp_mode.dart | 25 + .../ui_models/e_referral_form_model.dart | 42 ++ .../e_referral/e-referral_validator.dart | 91 ++++ .../e_referral/e_referral_form_manager.dart | 206 +++++++++ .../e_referral/e_referral_page_home.dart | 55 ++- .../e_referral/new_e_referral.dart | 183 ++++++++ lib/presentation/e_referral/new_referral.dart | 432 ------------------ .../e_referral/search_e_referral.dart | 98 ++++ .../e_referral/widget/e-referral_otp.dart | 105 +++++ .../widget/e_referral_other_details.dart | 308 +++++++++++++ .../widget/e_referral_patient_info.dart | 289 ++++++++++++ .../widget/e_referral_requester_form.dart | 228 +++++++++ .../e_referral/widget/e_referral_stepper.dart | 72 +++ .../widget/search_e_referral_form.dart | 184 ++++++++ lib/widgets/attachment_options.dart | 2 +- lib/widgets/dropdown/dropdown_widget.dart | 30 +- 25 files changed, 2635 insertions(+), 469 deletions(-) create mode 100644 lib/features/hmg_services/models/req_models/check_activation_e_referral_req_model.dart create mode 100644 lib/features/hmg_services/models/req_models/create_e_referral_model.dart rename lib/features/hmg_services/models/{create_e_referral_model.dart => req_models/create_e_referral_req_model.dart} (100%) create mode 100644 lib/features/hmg_services/models/req_models/send_activation_code_ereferral_req_model.dart create mode 100644 lib/features/hmg_services/models/resq_models/get_all_cities_resp_model.dart create mode 100644 lib/features/hmg_services/models/resq_models/relationship_type_resp_mode.dart create mode 100644 lib/features/hmg_services/models/ui_models/e_referral_form_model.dart create mode 100644 lib/presentation/e_referral/e-referral_validator.dart create mode 100644 lib/presentation/e_referral/e_referral_form_manager.dart create mode 100644 lib/presentation/e_referral/new_e_referral.dart delete mode 100644 lib/presentation/e_referral/new_referral.dart create mode 100644 lib/presentation/e_referral/search_e_referral.dart create mode 100644 lib/presentation/e_referral/widget/e-referral_otp.dart create mode 100644 lib/presentation/e_referral/widget/e_referral_other_details.dart create mode 100644 lib/presentation/e_referral/widget/e_referral_patient_info.dart create mode 100644 lib/presentation/e_referral/widget/e_referral_requester_form.dart create mode 100644 lib/presentation/e_referral/widget/e_referral_stepper.dart create mode 100644 lib/presentation/e_referral/widget/search_e_referral_form.dart diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index a9e95ae..4cedda1 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -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 @@ -849,6 +848,17 @@ class ApiConsts { var GET_ALL_CMC_ORDERS_RC = 'api/cmc/list'; var UPDATE_CMC_ORDER_RC = 'api/cmc/update'; + + //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 = 20.0; static final int appChannelId = 3; 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 39a6142..1115f18 100644 --- a/lib/features/hmg_services/hmg_services_repo.dart +++ b/lib/features/hmg_services/hmg_services_repo.dart @@ -5,10 +5,17 @@ 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/order_update_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/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'; abstract class HmgServicesRepo { Future>>> getAllCmcOrders(); @@ -16,6 +23,17 @@ abstract class HmgServicesRepo { Future>> updateCmcPresOrder(OrderUpdateRequestModel requestModel); Future>>> getAllCmcServices({required int patientID}); + + Future>>> getRelationshipTypes(); + + Future>>> getAllCities(); + + Future>> sendEReferralActivationCode(SendActivationCodeForEReferralRequestModel requestModel); + + Future>> checkEReferralActivationCode(CheckActivationCodeForEReferralRequestModel requestModel); + + Future>> createEReferral(CreateEReferralRequestModel requestModel); + } class HmgServicesRepoImp implements HmgServicesRepo { @@ -175,4 +193,224 @@ 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())); + } + } } diff --git a/lib/features/hmg_services/hmg_services_view_model.dart b/lib/features/hmg_services/hmg_services_view_model.dart index 84e3bb4..5dd5482 100644 --- a/lib/features/hmg_services/hmg_services_view_model.dart +++ b/lib/features/hmg_services/hmg_services_view_model.dart @@ -1,10 +1,17 @@ import 'package:flutter/material.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_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/order_update_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/services/error_handler_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 ErrorHandlerService errorHandlerService; @@ -18,6 +25,10 @@ class HmgServicesViewModel extends ChangeNotifier { List cmcOrdersList = []; List cmcServicesList = []; + + List relationTypes =[]; + List getAllCitiesList =[]; + Future getOrdersList() async { cmcOrdersList.clear(); isCmcOrdersLoading = true; @@ -134,4 +145,182 @@ class HmgServicesViewModel extends ChangeNotifier { return success; } + + /*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'); + } + } + }, + ); + } + } 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/create_e_referral_model.dart b/lib/features/hmg_services/models/req_models/create_e_referral_req_model.dart similarity index 100% rename from lib/features/hmg_services/models/create_e_referral_model.dart rename to lib/features/hmg_services/models/req_models/create_e_referral_req_model.dart 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/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..fded059 --- /dev/null +++ b/lib/features/hmg_services/models/ui_models/e_referral_form_model.dart @@ -0,0 +1,42 @@ +// 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 = ''; + CountryEnum countryEnum = 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; +} \ No newline at end of file diff --git a/lib/presentation/e_referral/e-referral_validator.dart b/lib/presentation/e_referral/e-referral_validator.dart new file mode 100644 index 0000000..78efb49 --- /dev/null +++ b/lib/presentation/e_referral/e-referral_validator.dart @@ -0,0 +1,91 @@ +// utils/referral_validator.dart +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'; + } + + if (formData.requesterPhone.trim().isEmpty) { + errors.requesterPhone = 'Phone number is required'; + } else if (formData.countryEnum.countryCode == '966' && + !_isValidSaudiPhone(formData.requesterPhone)) { + errors.requesterPhone = 'Please enter a valid Saudi phone number (5xxxxxxxx)'; + } + + if (formData.relationship == null) { + errors.relationship = 'Please select a relationship'; + } + + if (formData.relationship != null && + formData.relationship?.iD == 5 && + formData.otherRelationshipName.trim().isEmpty) { + errors.otherRelationshipName = 'Other relationship name is required'; + } + + return errors; + } + + static FormValidationErrors validateStep2(ReferralFormData formData) { + final errors = FormValidationErrors(); + + if (formData.patientIdentification.trim().isEmpty) { + errors.patientIdentification = 'Identification number is required'; + } + + if (formData.patientName.trim().isEmpty) { + errors.patientName = 'Patient name is required'; + } + + if (formData.patientPhone == null) { + errors.patientPhone = 'Please Enter patient phone number'; + } + + if (formData.patientCity == null) { + errors.patientCity = 'Please select patient city'; + } + + return errors; + } + + static FormValidationErrors validateStep3(ReferralFormData formData) { + final errors = FormValidationErrors(); + + if (formData.medicalReportImages.isEmpty) { + errors.medicalReport = 'At least one medical report is required'; + } + + if (formData.branch == null) { + errors.branch = 'Please select a branch'; + } + + if (formData.isPatientInsured && formData.insuredPatientImages.isEmpty) { + errors.insuredDocument = 'Insurance document is required for insured patients'; + } + + 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..a69a2fe --- /dev/null +++ b/lib/presentation/e_referral/e_referral_form_manager.dart @@ -0,0 +1,206 @@ +// 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; + + // Field-specific update methods that don't notify listeners immediately + 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 updateRelationship(GetAllRelationshipTypeResponseModel? value) { + _formData.relationship = value; + _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'); + } + + 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); + } + } + + 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); + } + } + + // 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(); + } + } +} \ 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 index 3eb1255..d41defb 100644 --- a/lib/presentation/e_referral/e_referral_page_home.dart +++ b/lib/presentation/e_referral/e_referral_page_home.dart @@ -1,15 +1,13 @@ import 'dart:ui'; import 'package:easy_localization/easy_localization.dart'; -import 'package:flutter/cupertino.dart'; 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/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/contact_us/contact_us_view_model.dart'; -import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; -import 'package:hmg_patient_app_new/presentation/e_referral/new_referral.dart'; +import 'package:hmg_patient_app_new/presentation/e_referral/new_e_referral.dart'; +import 'package:hmg_patient_app_new/presentation/e_referral/search_e_referral.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; @@ -35,7 +33,9 @@ class _EReferralPageState extends State { } bool isNewReferral = true; - VoidCallback? onNextStep; + VoidCallback? onNextStep; + int _currentPageIndex = 0; + int activeTabIndex = 0; @override Widget build(BuildContext context) { return Scaffold( @@ -47,34 +47,53 @@ class _EReferralPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: 16.h), - contactUsVM.isHMGLocationsListLoading - ? SizedBox.shrink() - : CustomTabBar( + CustomTabBar( activeTextColor: AppColors.primaryRedColor, activeBackgroundColor: AppColors.primaryRedColor.withValues(alpha: .1), tabs: [ CustomTabBarModel(null, "New Referral".needTranslation), CustomTabBarModel(null, "Search Referral".needTranslation), ], - onTabChange: (index) {}, + onTabChange: (index) { + activeTabIndex =index; + setState(() { + + }); + }, ).paddingSymmetrical(24.h, 0.h), SizedBox(height: 24.h), - NewReferralPage(onNextStep: (nextStep) { - WidgetsBinding.instance.addPostFrameCallback((_) { + activeTabIndex ==0 ? NewReferralPage( + onNextStep: (nextStep) { + WidgetsBinding.instance.addPostFrameCallback((_) { + setState(() { + onNextStep = nextStep; + }); + }); + }, + onStepChanged: (int value) { setState(() { - onNextStep = nextStep; - }); - }); - }) + _currentPageIndex = value; + }); + }, + ) : + SearchEReferralPage( + onNextStep: (onNextStep) { + WidgetsBinding.instance.addPostFrameCallback((_) { + setState(() { + onNextStep = onNextStep; + }); + }); + }, + ) ], ); }), ), bottomNavigationBar: Padding( - padding: EdgeInsets.all(16.h), + padding: EdgeInsets.all(20.h), child: CustomButton( - text: LocaleKeys.next.tr(), - onPressed:onNextStep ?? () {}, + text:activeTabIndex == 0 ? _currentPageIndex >= 2 ? LocaleKeys.submit.tr() : LocaleKeys.next.tr() : LocaleKeys.search.tr(), + onPressed: onNextStep ?? () {}, backgroundColor: AppColors.primaryRedColor, borderColor: AppColors.primaryRedColor, textColor: AppColors.whiteColor, diff --git a/lib/presentation/e_referral/new_e_referral.dart b/lib/presentation/e_referral/new_e_referral.dart new file mode 100644 index 0000000..ca96153 --- /dev/null +++ b/lib/presentation/e_referral/new_e_referral.dart @@ -0,0 +1,183 @@ + +import 'package:flutter/material.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/presentation/e_referral/widget/e-referral_otp.dart'; +import 'package:hmg_patient_app_new/presentation/e_referral/widget/e_referral_other_details.dart'; +import 'package:hmg_patient_app_new/presentation/e_referral/widget/e_referral_patient_info.dart'; +import 'package:hmg_patient_app_new/presentation/e_referral/widget/e_referral_requester_form.dart'; +import 'package:hmg_patient_app_new/presentation/e_referral/widget/e_referral_stepper.dart'; +import 'package:provider/provider.dart'; +import 'e-referral_validator.dart'; +import 'e_referral_form_manager.dart'; + +class NewReferralPage extends StatefulWidget { + final Function(VoidCallback) onNextStep; + final Function(int) onStepChanged; + + const NewReferralPage({ + super.key, + required this.onNextStep, + required this.onStepChanged, + }); + + @override + State createState() => _NewReferralPageState(); +} + +class _NewReferralPageState extends State { + final PageController _pageController = PageController(); + final ReferralFormManager _formManager = ReferralFormManager(); // Use manager + int _currentStep = 0; + + final List _steps = [ + 'Requester Info', + 'Patient Information', + 'Other details' + ]; + + @override + void initState() { + super.initState(); + _loadData(); + widget.onNextStep(_handleNextStep); + } + + void _handleNextStep() { + switch (_currentStep) { + case 0: + if (_validateCurrentStep()) { + OTPService.openOTPScreen( + context: context, + phoneNumber: _formManager.formData.requesterPhone ?? '', + countryEnum: _formManager.formData.countryEnum, + 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 createReferrralRequestModel = + CreateEReferralRequestModel( + isInsuredPatient: _formManager.formData.isPatientInsured, + cityCode: _formManager.formData.patientCity!.iD!.toString(), + cityName: _formManager.formData.patientCity!.description, + requesterName: _formManager.formData.requesterName, + requesterContactNo: _formManager.formData.requesterPhone, + requesterRelationship: _formManager.formData.relationship?.iD, + otherRelationship: _formManager.formData.relationship!.iD.toString(), + fullName: _formManager.formData.patientName, + identificationNo: int.tryParse(_formManager.formData!.patientIdentification ?? '0'), + patientMobileNumber: _formManager.formData.patientPhone, + preferredBranchCode: _formManager.formData.branch!.iD, + medicalReportAttachment: _formManager.formData.medicalReportImages, + insuranceCardAttachment: _formManager.formData.insuredPatientImages, + preferredBranchName: _formManager.formData.branch!.desciption + ); + + final hmgServicesVM = context.read(); + hmgServicesVM.createEReferral( + requestModel: createReferrralRequestModel, + onSuccess: (response) { + print("E-Referral submitted successfully"); + }, + onError: (errorMessage) { + // Handle error (e.g., show error message) + print(errorMessage); + }, + ); + + } + + 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 ChangeNotifierProvider.value( + value: _formManager, + child: SizedBox( + height: MediaQuery.of(context).size.height, + child: Column( + children: [ + const SizedBox(height: 8), + ProgressStepperWidget( + currentStep: _currentStep, + steps: _steps, + ), + Expanded( + child: PageView( + controller: _pageController, + physics: const NeverScrollableScrollPhysics(), + onPageChanged: (index) => setState(() => _currentStep = index), + children: [ + RequesterFormStep(), + PatientInformationStep(), + OtherDetailsStep(), + ], + ), + ), + ], + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/presentation/e_referral/new_referral.dart b/lib/presentation/e_referral/new_referral.dart deleted file mode 100644 index 73c4f5c..0000000 --- a/lib/presentation/e_referral/new_referral.dart +++ /dev/null @@ -1,432 +0,0 @@ -// dart -// File: lib/presentation/e_referral/new_referral_page.dart - -import 'dart:io'; - -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/app_export.dart'; -import 'package:hmg_patient_app_new/core/app_state.dart'; -import 'package:hmg_patient_app_new/core/common_models/nationality_country_model.dart'; -import 'package:hmg_patient_app_new/core/dependencies.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/authentication/authentication_view_model.dart'; -import 'package:hmg_patient_app_new/features/hmg_services/models/create_e_referral_model.dart'; -import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; -import 'package:hmg_patient_app_new/theme/colors.dart'; -import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; -import 'package:hmg_patient_app_new/widgets/dropdown/dropdown_widget.dart'; -import 'package:hmg_patient_app_new/widgets/image_picker.dart'; -import 'package:hmg_patient_app_new/widgets/input_widget.dart'; -import 'package:provider/provider.dart'; - -class NewReferralPage extends StatefulWidget { - final Function(VoidCallback) onNextStep; - const NewReferralPage({super.key, required this.onNextStep}); - - @override - State createState() => NewReferralPageState(); -} - -class NewReferralPageState extends State { - final PageController _pageController = PageController(); - int pageIndex = 0; - int _tabIndex = 0; - bool isPatientInsured =false; - final TextEditingController _nameController = TextEditingController(); - final TextEditingController _phoneController = TextEditingController(); - String _country = 'Saudi Arabia'; - String? _relationship; - List medicalReportImages = []; - List insuredPatientImages = []; - void nextPressed() { - if (pageIndex < 2) { - _pageController.nextPage(duration: const Duration(milliseconds: 300), curve: Curves.easeInOut); - } else { - // submit logic - } - } - - @override - initState() { - super.initState(); - widget.onNextStep((){ - nextPressed(); - }); - - } - - Widget _progressStep({required String title, required bool active, bool showDivider = true}) { - final Color activeColor = active ? AppColors.primaryRedColor : Colors.grey.shade400; - return Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - - children: [ - CircleAvatar( - radius: 13, - backgroundColor: active ? activeColor : Colors.grey.shade300, - child: Icon(Icons.check, size: 14, color: Colors.white), - ), - if (showDivider) - Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: Divider(thickness: 1), - ), - - ], - ), - const SizedBox(height: 6), - Text(title, style: const TextStyle(fontSize: 11, fontWeight: FontWeight.w600)), - const SizedBox(height: 6), - Container( - padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4), - decoration: BoxDecoration( - color: active ? activeColor.withOpacity(0.15) : Colors.grey.shade100, - borderRadius: BorderRadius.circular(6), - ), - child: Text(active ? 'Active' : 'Inactive', - style: TextStyle(fontSize: 9, color: active ? activeColor : Colors.grey)), - ), - ], - ), - ); - } - - Widget _requesterForm() { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 24.0), - child: ListView( - physics: const BouncingScrollPhysics(), - children: [ - const SizedBox(height: 12), - const Text('Referral requester information', - style: TextStyle(fontWeight: FontWeight.w700, fontSize: 16)).paddingSymmetrical(4.h, 0.h), - const SizedBox(height: 12), - TextInputWidget( - controller: _nameController, - padding: const EdgeInsets.symmetric(horizontal: 16.0), - hintText: 'Enter Referral Requester Name*', labelText: 'Requester Name', - ).paddingSymmetrical(0.h, 4.h), - - Selector? countriesList, NationalityCountries? selectedCountry, bool isArabic})>( - selector: (context, authViewModel) { - final appState = getIt.get(); - return ( - countriesList: authViewModel.countriesList, - selectedCountry: authViewModel.pickedCountryByUAEUser, - isArabic: appState.isArabic(), - ); - }, - shouldRebuild: (previous, next) => previous.countriesList != next.countriesList || previous.selectedCountry != next.selectedCountry || previous.isArabic != next.isArabic, - builder: (context, data, child) { - final authVM = context.read(); - return DropdownWidget( - labelText: LocaleKeys.country.tr(), - hintText:_country, - isEnable: true, - dropdownItems: (data.countriesList ?? []).map((e) => data.isArabic ? e.nameN ?? "" : e.name ?? "").toList(), - selectedValue: data.selectedCountry != null - ? data.isArabic - ? data.selectedCountry!.nameN ?? "" - : data.selectedCountry!.name ?? "" - : "", - onChange: authVM.onUAEUserCountrySelection, - hasSelectionCustomIcon: true, - labelColor: AppColors.textColor, - padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), - selectionCustomIcon: AppAssets.arrow_down, - leadingIcon: AppAssets.globe, - ).withVerticalPadding(8); - }, - ).paddingSymmetrical(0.h, 4.h), - - TextInputWidget( - labelText: LocaleKeys.mobileNumber.tr(), - hintText: LocaleKeys.mobileNumber.tr(), - controller: null, - isEnable: true, - prefix: null, - isAllowLeadingIcon: true, - labelColor: AppColors.textColor, padding: const EdgeInsets.symmetric(horizontal: 16.0), - isReadOnly: true, - leadingIcon: AppAssets.call).paddingSymmetrical(0.h, 4.h), - - Selector? countriesList, NationalityCountries? selectedCountry, bool isArabic})>( - selector: (context, authViewModel) { - final appState = getIt.get(); - return ( - countriesList: authViewModel.countriesList, - selectedCountry: authViewModel.pickedCountryByUAEUser, - isArabic: appState.isArabic(), - ); - }, - shouldRebuild: (previous, next) => previous.countriesList != next.countriesList || previous.selectedCountry != next.selectedCountry || previous.isArabic != next.isArabic, - builder: (context, data, child) { - final authVM = context.read(); - return DropdownWidget( - labelText: "Relationship", - hintText: "Relationship*".needTranslation, - isEnable: true, - dropdownItems: (data.countriesList ?? []).map((e) => data.isArabic ? e.nameN ?? "" : e.name ?? "").toList(), - selectedValue: data.selectedCountry != null - ? data.isArabic - ? data.selectedCountry!.nameN ?? "" - : data.selectedCountry!.name ?? "" - : "", - onChange: authVM.onUAEUserCountrySelection, - hasSelectionCustomIcon: true, - labelColor: AppColors.textColor, - padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), - selectionCustomIcon: AppAssets.arrow_down, - leadingIcon: AppAssets.globe, - ).withVerticalPadding(8); - }, - ).paddingSymmetrical(0.h, 4.h), - - const SizedBox(height: 120), - ], - ), - ); - } - - Widget _patientInformation(){ - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: ListView( - physics: const BouncingScrollPhysics(), - children: [ - const SizedBox(height: 12), - 'Patient information'.toText16(weight: FontWeight.bold).paddingSymmetrical(4.h, 0.h), - const SizedBox(height: 12), - TextInputWidget( - controller: _nameController, - padding: const EdgeInsets.symmetric(horizontal: 16.0), - hintText: 'Enter Identification Number*', labelText: 'Identification Number ', - ).paddingSymmetrical(0.h, 4.h), - - TextInputWidget( - controller: _nameController, - padding: const EdgeInsets.symmetric(horizontal: 16.0), - hintText: 'Patient Name*', labelText: 'Name', - ).paddingSymmetrical(0.h, 4.h), - - Selector? countriesList, NationalityCountries? selectedCountry, bool isArabic})>( - selector: (context, authViewModel) { - final appState = getIt.get(); - return ( - countriesList: authViewModel.countriesList, - selectedCountry: authViewModel.pickedCountryByUAEUser, - isArabic: appState.isArabic(), - ); - }, - shouldRebuild: (previous, next) => previous.countriesList != next.countriesList || previous.selectedCountry != next.selectedCountry || previous.isArabic != next.isArabic, - builder: (context, data, child) { - final authVM = context.read(); - return DropdownWidget( - labelText: LocaleKeys.country.tr(), - hintText:_country, - isEnable: true, - dropdownItems: (data.countriesList ?? []).map((e) => data.isArabic ? e.nameN ?? "" : e.name ?? "").toList(), - selectedValue: data.selectedCountry != null - ? data.isArabic - ? data.selectedCountry!.nameN ?? "" - : data.selectedCountry!.name ?? "" - : "", - onChange: authVM.onUAEUserCountrySelection, - hasSelectionCustomIcon: true, - labelColor: AppColors.textColor, - padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), - selectionCustomIcon: AppAssets.arrow_down, - leadingIcon: AppAssets.globe, - ).withVerticalPadding(8); - }, - ).paddingSymmetrical(0.h, 4.h), - - - 'Where the patient located'.needTranslation.toText16(weight: FontWeight.bold).paddingSymmetrical(4.h, 0.h), - - Selector? countriesList, NationalityCountries? selectedCountry, bool isArabic})>( - selector: (context, authViewModel) { - final appState = getIt.get(); - return ( - countriesList: authViewModel.countriesList, - selectedCountry: authViewModel.pickedCountryByUAEUser, - isArabic: appState.isArabic(), - ); - }, - shouldRebuild: (previous, next) => previous.countriesList != next.countriesList || previous.selectedCountry != next.selectedCountry || previous.isArabic != next.isArabic, - builder: (context, data, child) { - final authVM = context.read(); - return DropdownWidget( - labelText: LocaleKeys.country.tr(), - hintText:_country, - isEnable: true, - dropdownItems: (data.countriesList ?? []).map((e) => data.isArabic ? e.nameN ?? "" : e.name ?? "").toList(), - selectedValue: data.selectedCountry != null - ? data.isArabic - ? data.selectedCountry!.nameN ?? "" - : data.selectedCountry!.name ?? "" - : "", - onChange: authVM.onUAEUserCountrySelection, - hasSelectionCustomIcon: true, - labelColor: AppColors.textColor, - padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), - selectionCustomIcon: AppAssets.arrow_down, - leadingIcon: AppAssets.globe, - ).withVerticalPadding(8); - }, - ).paddingSymmetrical(0.h, 4.h), - - ])); - } - - Widget _otherDetails() { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: ListView( - physics: const BouncingScrollPhysics(), - children: [ - const SizedBox(height: 12), - 'Other Details'.toText16(weight: FontWeight.bold).paddingSymmetrical(4.h, 0.h), - const SizedBox(height: 12), - - InkWell(child: TextInputWidget( - controller: _nameController, - padding: const EdgeInsets.symmetric(horizontal: 16.0), - hintText: 'Medical Report', labelText: 'Select Attachment', - suffix: Icon(Icons.attachment), - isReadOnly: true, - - ), - onTap: (){ - ImageOptions.showImageOptionsNew( - context, - true, - (String image, File file) { - setState(() { - EReferralAttachment eReferralAttachment = new EReferralAttachment(fileName: 'image ${medicalReportImages.length + 1}.png', base64String: image); - medicalReportImages.add(eReferralAttachment); - }); - }, - ); - }, - ).paddingSymmetrical(0.h, 4.h), - - Selector? countriesList, NationalityCountries? selectedCountry, bool isArabic})>( - selector: (context, authViewModel) { - final appState = getIt.get(); - return ( - countriesList: authViewModel.countriesList, - selectedCountry: authViewModel.pickedCountryByUAEUser, - isArabic: appState.isArabic(), - ); - }, - shouldRebuild: (previous, next) => previous.countriesList != next.countriesList || previous.selectedCountry != next.selectedCountry || previous.isArabic != next.isArabic, - builder: (context, data, child) { - final authVM = context.read(); - return DropdownWidget( - labelText: LocaleKeys.branch.tr(), - hintText:_country, - isEnable: true, - dropdownItems: (data.countriesList ?? []).map((e) => data.isArabic ? e.nameN ?? "" : e.name ?? "").toList(), - selectedValue: data.selectedCountry != null - ? data.isArabic - ? data.selectedCountry!.nameN ?? "" - : data.selectedCountry!.name ?? "" - : "", - onChange: authVM.onUAEUserCountrySelection, - hasSelectionCustomIcon: true, - labelColor: AppColors.textColor, - padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), - selectionCustomIcon: AppAssets.arrow_down, - leadingIcon: AppAssets.hospital, - ).withVerticalPadding(8); - }, - ).paddingSymmetrical(0.h, 4.h), - Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Row( - children: [ - Checkbox( - value: isPatientInsured, - activeColor: AppColors.primaryRedColor, - onChanged: (bool? newValue) { - setState(() { - isPatientInsured = newValue!; - }); - }), - Padding( - padding: const EdgeInsets.all(5.0), - child: Text( - "Patient is Insured".needTranslation, - style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600), - ), - ), - ], - ), - ], - ).paddingSymmetrical(0.h, 4.h), - - isPatientInsured? InkWell(child: TextInputWidget( - controller: _nameController, - padding: const EdgeInsets.symmetric(horizontal: 16.0), - hintText: 'Insurance Document', labelText: 'Select Attachment', - suffix: Icon(Icons.attachment), - isReadOnly: true, - - )) : SizedBox(), - ])); - } - @override - Widget build(BuildContext context) { - final bool step0Active = pageIndex == 0; - final bool step1Active = pageIndex == 1; - final bool step2Active = pageIndex == 2; - - return SizedBox( - height: MediaQuery - .of(context) - .size - .height, // constrain height - child: Column( - children: [ - const SizedBox(height: 8), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 24.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - _progressStep(title: 'Requester Info', active: step0Active), - _progressStep(title: 'Patient Information', active: step1Active), - _progressStep( - title: 'Other details', active: step2Active, showDivider: false), - - ], - ), - ), - Expanded( - child: PageView( - controller: _pageController, - physics: const NeverScrollableScrollPhysics(), - onPageChanged: (i) => setState(() => pageIndex = i), - children: [ - _requesterForm(), - _patientInformation(), - // const Center(child: Text('Patient Info - step 2 (placeholder)')), - _otherDetails(), - ], - ), - ), - - - ], - ), - ); - } - } 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..fabf115 --- /dev/null +++ b/lib/presentation/e_referral/search_e_referral.dart @@ -0,0 +1,98 @@ + +import 'package:flutter/material.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/presentation/e_referral/widget/search_e_referral_form.dart'; +import 'package:provider/provider.dart'; +import 'e-referral_validator.dart'; +import 'e_referral_form_manager.dart'; + +class SearchEReferralPage extends StatefulWidget { + final Function(VoidCallback) onNextStep; + + + const SearchEReferralPage({ + super.key, + required this.onNextStep, + }); + + @override + State createState() => _SearchEReferralPageState(); +} + +class _SearchEReferralPageState extends State { + final PageController _pageController = PageController(); + final ReferralFormManager _formManager = ReferralFormManager(); // Use manager + int _currentStep = 0; + + + + @override + void initState() { + super.initState(); + _loadData(); + widget.onNextStep(_handleNextStep); + } + + void _handleNextStep() { + _searchReferral(); + } + + void _searchReferral() { + + CreateEReferralRequestModel createReferrralRequestModel = + CreateEReferralRequestModel( + isInsuredPatient: _formManager.formData.isPatientInsured, + cityCode: _formManager.formData.patientCity!.iD!.toString(), + cityName: _formManager.formData.patientCity!.description, + requesterName: _formManager.formData.requesterName, + requesterContactNo: _formManager.formData.requesterPhone, + requesterRelationship: _formManager.formData.relationship?.iD, + otherRelationship: _formManager.formData.relationship!.iD.toString(), + fullName: _formManager.formData.patientName, + identificationNo: int.tryParse(_formManager.formData!.patientIdentification ?? '0'), + patientMobileNumber: _formManager.formData.patientPhone, + preferredBranchCode: _formManager.formData.branch!.iD, + medicalReportAttachment: _formManager.formData.medicalReportImages, + insuranceCardAttachment: _formManager.formData.insuredPatientImages, + preferredBranchName: _formManager.formData.branch!.desciption + ); + + final hmgServicesVM = context.read(); + hmgServicesVM.createEReferral( + requestModel: createReferrralRequestModel, + onSuccess: (response) { + print("E-Referral submitted successfully"); + }, + onError: (errorMessage) { + // Handle error (e.g., show error message) + print(errorMessage); + }, + ); + + } + + void _loadData() { + + } + + @override + Widget build(BuildContext context) { + return ChangeNotifierProvider.value( + value: _formManager, + child: SizedBox( + height: MediaQuery.of(context).size.height, + child: Column( + children: [ + + Expanded( + child: + SearchEReferralFormForm(), + + ), + ], + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/presentation/e_referral/widget/e-referral_otp.dart b/lib/presentation/e_referral/widget/e-referral_otp.dart new file mode 100644 index 0000000..3fcac6c --- /dev/null +++ b/lib/presentation/e_referral/widget/e-referral_otp.dart @@ -0,0 +1,105 @@ +// services/otp_service.dart +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/enums.dart'; +import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.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/check_activation_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/widgets/common_bottom_sheet.dart'; +import 'package:hmg_patient_app_new/features/authentication/widgets/otp_verification_screen.dart'; + +class OTPService { + static void openOTPScreen({ + required BuildContext context, + required String phoneNumber, + required CountryEnum countryEnum, + required Function onSuccess, + }) { + final hmgServicesViewModel = context.read(); + + hmgServicesViewModel.eReferralSendActivationCode( + requestModel: SendActivationCodeForEReferralRequestModel( + patientMobileNumber: int.parse(phoneNumber), + zipCode: countryEnum.countryCode, + patientOutSA: countryEnum.countryCode == '966' ? 0 : 1, + ), + onSuccess: (GenericApiModel response) { + _showOTPVerificationSheet( + context: context, + phoneNumber: phoneNumber, + loginTokenID: response.data, + onSuccess: onSuccess, + ); + }, + onError: (String errorMessage) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(errorMessage)) + ); + }, + ); + } + + static void _showOTPVerificationSheet({ + required BuildContext context, + required String phoneNumber, + required String loginTokenID, + required Function onSuccess, + }) { + showCommonBottomSheet( + context, + isFullScreen: true, + title: "OTP Verification", + isCloseButtonVisible: false, + height:ResponsiveExtension.screenHeight * 0.75, + child: OTPVerificationScreen( + phoneNumber: phoneNumber, + checkActivationCode: (int code) { + _verifyOTP( + context: context, + loginTokenID: loginTokenID, + code: code, + onSuccess: onSuccess, + ); + }, + onResendOTPPressed: (String phoneNumber) { + Navigator.pop(context); + openOTPScreen( + context: context, + phoneNumber: phoneNumber, + countryEnum: CountryEnum.saudiArabia, + onSuccess: onSuccess, + ); + }, + isFormFamilyFile: false, + ), + ); + } + + static void _verifyOTP({ + required BuildContext context, + required String loginTokenID, + required int code, + required Function onSuccess, + }) { + final hmgServicesViewModel = context.read(); + + hmgServicesViewModel.checkEReferralActivationCode( + requestModel: CheckActivationCodeForEReferralRequestModel( + logInTokenID: loginTokenID, + activationCode: code.toString(), + ), + onSuccess: (GenericApiModel response) { + Navigator.pop(context); + onSuccess(); + }, + onError: (String errorMessage) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(errorMessage)) + ); + }, + ); + } +} \ No newline at end of file diff --git a/lib/presentation/e_referral/widget/e_referral_other_details.dart b/lib/presentation/e_referral/widget/e_referral_other_details.dart new file mode 100644 index 0000000..d473dfe --- /dev/null +++ b/lib/presentation/e_referral/widget/e_referral_other_details.dart @@ -0,0 +1,308 @@ +// widgets/other_details_step.dart +import 'dart:io'; + +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/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' + : ''; + } + + void _updateInsuranceText() { + final hasInsuranceDocs = _formManager.formData.insuredPatientImages.isNotEmpty; + _insuranceController.text = hasInsuranceDocs + ? '${_formManager.formData.insuredPatientImages.length} file(s) selected' + : ''; + } + + @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'), + 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', + labelText: 'Select Attachment', + suffix: const Icon(Icons.attachment), + isReadOnly: true, + errorMessage: formManager.errors.medicalReport, + hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.medicalReport), + ), + onTap: () { + ImageOptions.showImageOptionsNew( + context, + true, + (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}'), + 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(); + } + }, + ), + const Padding( + padding: EdgeInsets.all(5.0), + child: Text( + "Patient is Insured", + 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', + labelText: 'Select Attachment', + suffix: const Icon(Icons.attachment), + isReadOnly: true, + errorMessage: formManager.errors.insuredDocument, + hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.insuredDocument), + ), + onTap: () { + ImageOptions.showImageOptionsNew( + context, + true, + (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", + 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 ListView.separated( + shrinkWrap: true, + physics: const BouncingScrollPhysics(), + itemBuilder: (context, index) { + final branch = hospitals[index]; + return ListTile( + title: Text(branch.desciption ?? 'Unknown'), + 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..779804e --- /dev/null +++ b/lib/presentation/e_referral/widget/e_referral_patient_info.dart @@ -0,0 +1,289 @@ +// widgets/patient_information_step.dart +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/features/authentication/authentication_view_model.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'), + const SizedBox(height: 12), + _buildIdentificationField(formManager), + _buildPatientNameField(formManager), + // _buildPatientCountryField(context, formManager), + _buildPatientPhoneField(formManager), + const SizedBox(height: 20), + _buildSectionTitle('Where the patient located'), + _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*', + labelText: 'Identification Number', + 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*', + labelText: 'Name', + 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 _buildPatientCountryField(BuildContext context, ReferralFormManager formManager) { + // return DropdownWidget( + // labelText: 'Country', + // hintText: formManager.formData.patientCountry?.name ?? "Select Country", + // 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.patientCountry, + // hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.patientCountry), + // ).paddingSymmetrical(0, 4).onPress(() { + // _showCountryBottomSheet(context, formManager); + // }); + // } + + Widget _buildPatientPhoneField(ReferralFormManager formManager) { + return Focus( + focusNode: _phoneFocusNode, + child: TextInputWidget( + labelText: 'Phone Number', + hintText: "5xxxxxxxx", + controller: _phoneController, + padding: const EdgeInsets.all(8), + keyboardType: TextInputType.number, + onChange: (value) { + formManager.updatePatientPhone(value ?? ''); + }, + onCountryChange: (value) { + formManager.updateCountryEnum(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", + 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 _showCountryBottomSheet(BuildContext context, ReferralFormManager formManager) { + // final authVM = context.read(); + // + // showCommonBottomSheetWithoutHeight( + // context, + // title: "Select Country", + // child: Consumer( + // builder: (context, authVM, child) { + // final countries = authVM.countriesList; + // if (countries == null || countries.isEmpty) { + // return const Center( + // child: Padding( + // padding: EdgeInsets.all(16.0), + // child: Text('No countries available'), + // ), + // ); + // } + // + // return ListView.separated( + // shrinkWrap: true, + // physics: const BouncingScrollPhysics(), + // itemBuilder: (context, index) { + // final country = countries[index]; + // return ListTile( + // title: Text(country.name ?? 'Unknown'), + // onTap: () { + // formManager.updatePatientCountry(country); + // Navigator.pop(context); + // }, + // ); + // }, + // separatorBuilder: (context, index) => const Divider(), + // itemCount: countries.length, + // ); + // }, + // ), + // useSafeArea: true, + // isFullScreen: false, + // isCloseButtonVisible: true, + // ); + // } + + void _showCityBottomSheet(BuildContext context, ReferralFormManager formManager) { + final hmgServicesVM = context.read(); + + showCommonBottomSheetWithoutHeight( + context, + title: "Select City", + 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 ListView.separated( + shrinkWrap: true, + physics: const BouncingScrollPhysics(), + itemBuilder: (context, index) { + final city = cities[index]; + return ListTile( + title: Text(city.description ?? 'Unknown'), + 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..b6952a5 --- /dev/null +++ b/lib/presentation/e_referral/widget/e_referral_requester_form.dart @@ -0,0 +1,228 @@ +// widgets/requester_form_step.dart +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/features/hmg_services/models/ui_models/e_referral_form_model.dart'; +import 'package:hmg_patient_app_new/presentation/e_referral/e-referral_validator.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/authentication/authentication_view_model.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'; +// widgets/requester_form_step.dart +// widgets/requester_form_step.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), + child: ListView( + physics: const BouncingScrollPhysics(), + children: [ + const SizedBox(height: 12), + _buildSectionTitle('Referral requester information'), + 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*', + labelText: 'Requester Name', + keyboardType: TextInputType.text, + errorMessage: formManager.errors.requesterName, + hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.requesterName), + onChange: (value) { + formManager.updateRequesterName(value ?? ''); + }, + onSubmitted: (value) { + _phoneFocusNode.requestFocus(); + }, + ).paddingSymmetrical(0, 8), + ); + } + + Widget _buildPhoneField(ReferralFormManager formManager) { + return Focus( + focusNode: _phoneFocusNode, + child: TextInputWidget( + labelText: 'Phone Number', + hintText: "5xxxxxxxx", + controller: _phoneController, + padding: const EdgeInsets.all(8), + keyboardType: TextInputType.number, + onChange: (value) { + formManager.updateRequesterPhone(value ?? ''); + }, + onCountryChange: (value) { + formManager.updateCountryEnum(value); + }, + prefix: '966', + isBorderAllowed: false, + isAllowLeadingIcon: true, + fontSize: 13, + isCountryDropDown: true, + leadingIcon: AppAssets.smart_phone, + errorMessage: formManager.errors.requesterPhone, + hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.requesterPhone) + ).paddingSymmetrical(0, 8), + ); + } + + Widget _buildRelationshipField(BuildContext context, ReferralFormManager formManager) { + return DropdownWidget( + labelText: "Relationship", + hintText: formManager.formData.relationship?.textEn ?? "Select Relation", + isEnable: false, + selectedValue: formManager.formData.relationship?.textEn ?? "Select Relation", + 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, + padding: const EdgeInsets.symmetric(horizontal: 16.0), + hintText: 'Other Name*', + labelText: 'Other Name', + 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", + 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 ListView.separated( + shrinkWrap: true, + physics: const BouncingScrollPhysics(), + itemBuilder: (context, index) { + final relationship = hmgServicesVM.relationTypes[index]; + return ListTile( + title: Text(relationship.textEn ?? 'Unknown'), + 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/e_referral_stepper.dart b/lib/presentation/e_referral/widget/e_referral_stepper.dart new file mode 100644 index 0000000..a6cd771 --- /dev/null +++ b/lib/presentation/e_referral/widget/e_referral_stepper.dart @@ -0,0 +1,72 @@ +// widgets/progress_stepper_widget.dart +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; + +class ProgressStepperWidget extends StatelessWidget { + final int currentStep; + final List steps; + + const ProgressStepperWidget({ + super.key, + required this.currentStep, + required this.steps, + }); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 24.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + for (int i = 0; i < steps.length; i++) + _buildStep( + title: steps[i], + active: i == currentStep, + showDivider: i < steps.length - 1, + ), + ], + ), + ); + } + + Widget _buildStep({required String title, required bool active, bool showDivider = true}) { + final Color activeColor = active ? AppColors.primaryRedColor : Colors.grey.shade400; + + return Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + CircleAvatar( + radius: 13, + backgroundColor: active ? activeColor : Colors.grey.shade300, + child: Icon(Icons.check, size: 14, color: Colors.white), + ), + if (showDivider) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: Divider(thickness: 1), + ), + ], + ), + const SizedBox(height: 6), + Text(title, style: const TextStyle(fontSize: 11, fontWeight: FontWeight.w600)), + const SizedBox(height: 6), + Container( + padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4), + decoration: BoxDecoration( + color: active ? activeColor.withOpacity(0.15) : Colors.grey.shade100, + borderRadius: BorderRadius.circular(6), + ), + child: Text( + active ? 'Active' : 'Inactive', + style: TextStyle(fontSize: 9, color: active ? activeColor : Colors.grey) + ), + ), + ], + ), + ); + } +} \ No newline at end of file diff --git a/lib/presentation/e_referral/widget/search_e_referral_form.dart b/lib/presentation/e_referral/widget/search_e_referral_form.dart new file mode 100644 index 0000000..37d4025 --- /dev/null +++ b/lib/presentation/e_referral/widget/search_e_referral_form.dart @@ -0,0 +1,184 @@ + +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/features/hmg_services/models/ui_models/e_referral_form_model.dart'; +import 'package:hmg_patient_app_new/presentation/e_referral/e-referral_validator.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/authentication/authentication_view_model.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 SearchEReferralFormForm extends StatefulWidget { + const SearchEReferralFormForm({super.key}); + + @override + State createState() => SearchEReferralFormFormState(); +} + +class SearchEReferralFormFormState extends State { + late TextEditingController _searchController; + late TextEditingController _phoneController; + late FocusNode _searchFocusNode; + late FocusNode _phoneFocusNode; + + int criteria =0; + + List> criteriaList = [ + {0: 'Identification Number'}, + {1: 'Referral Number'}, + ]; + late ReferralFormManager _formManager; + + @override + void initState() { + super.initState(); + _formManager = context.read(); + + _searchController = TextEditingController(); + _phoneController = TextEditingController(); + _searchFocusNode = FocusNode(); + _phoneFocusNode = FocusNode(); + + // Initialize controllers with current values + _searchController.text = ''; + _phoneController.text = ''; + + // Auto-focus the name field when the step loads + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted) { + _searchFocusNode.requestFocus(); + } + }); + } + + @override + Widget build(BuildContext context) { + return Consumer( + builder: (context, formManager, child) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 24.0), + child: ListView( + physics: const BouncingScrollPhysics(), + children: [ + + const SizedBox(height: 12), + _buildSelectionField(context, formManager), + _buildNameField(formManager), + _buildPhoneField(formManager), + + ], + ), + ); + }, + ); + } + + + + Widget _buildNameField(ReferralFormManager formManager) { + return Focus( + focusNode: _searchFocusNode, + child: TextInputWidget( + controller: _searchController, + padding: const EdgeInsets.symmetric(horizontal: 16.0), + hintText:criteria ==0 ? "Enter Identification Number" : "Enter Referral Number", + labelText: criteria ==0 ? "Identification Number" : "Referral Number", + keyboardType: TextInputType.text, + // errorMessage: formManager.errors.requesterName, + hasError: !ValidationUtils.isNullOrEmpty(_searchController.text), + onChange: (value) { + + }, + onSubmitted: (value) { + + }, + ).paddingSymmetrical(0, 8), + ); + } + + Widget _buildPhoneField(ReferralFormManager formManager) { + return Focus( + focusNode: _phoneFocusNode, + child: TextInputWidget( + labelText: 'Phone Number', + hintText: "5xxxxxxxx", + controller: _phoneController, + padding: const EdgeInsets.all(8), + keyboardType: TextInputType.number, + onChange: (value) { + formManager.updateRequesterPhone(value ?? ''); + }, + onCountryChange: (value) { + formManager.updateCountryEnum(value); + }, + prefix: '966', + isBorderAllowed: false, + isAllowLeadingIcon: true, + fontSize: 13, + isCountryDropDown: true, + leadingIcon: AppAssets.smart_phone, + errorMessage: formManager.errors.requesterPhone, + hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.requesterPhone) + ).paddingSymmetrical(0, 8), + ); + } + + Widget _buildSelectionField(BuildContext context, ReferralFormManager formManager) { + return DropdownWidget( + labelText: "Select the Search Criteria", + hintText: criteria ==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); + }); + } + + + + void _showCriteriaBottomSheet(BuildContext context,) { + + + showCommonBottomSheetWithoutHeight( + context, + title: "Select Criteria", + child: ListView.separated( + shrinkWrap: true, + physics: const BouncingScrollPhysics(), + itemBuilder: (context, index) { + return ListTile( + title: Text(criteriaList[index].values.first), + onTap: () { + setState(() { + criteria = index; + }); + Navigator.pop(context); + }, + ); + }, + separatorBuilder: (context, index) => const Divider(), + itemCount: criteriaList.length, + ), + isFullScreen: false, + isCloseButtonVisible: true, + ); + } + + @override + void dispose() { + _searchController.dispose(); + _phoneController.dispose(); + _searchFocusNode.dispose(); + _phoneFocusNode.dispose(); + + super.dispose(); + } +} \ No newline at end of file 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() { From fb69723a659fe3ae46437054633fed85d3c8bd89 Mon Sep 17 00:00:00 2001 From: Sultan khan Date: Tue, 25 Nov 2025 14:32:00 +0300 Subject: [PATCH 3/5] e-referral done. --- lib/core/dependencies.dart | 2 +- .../hmg_services/hmg_services_repo.dart | 60 ++++ .../hmg_services/hmg_services_view_model.dart | 90 ++++- .../search_e_referral_req_model.dart | 60 ++++ .../search_e_referral_resp_model.dart | 161 +++++++++ .../ui_models/e_referral_form_model.dart | 6 + .../e_referral/e-referral_validator.dart | 12 +- .../e_referral/e_referral_form_manager.dart | 86 ++++- .../e_referral/e_referral_page_home.dart | 103 ------ .../e_referral/e_referral_search_result.dart | 310 ++++++++++++++++++ .../e_referral/new_e_referral.dart | 184 ++++++++--- .../e_referral/search_e_referral.dart | 134 ++++---- .../e_referral/widget/e-referral_otp.dart | 174 +++++----- .../widget/e_referral_other_details.dart | 23 +- .../widget/e_referral_patient_info.dart | 60 +--- .../widget/e_referral_requester_form.dart | 85 ++--- .../e_referral/widget/e_referral_stepper.dart | 72 ---- .../widget/search_e_referral_form.dart | 137 ++++---- .../widgets/pickup_location.dart | 249 +++++++------- lib/routes/app_routes.dart | 4 +- lib/widgets/image_picker.dart | 1 + .../order_tracking/request_tracking.dart | 75 +---- lib/widgets/stepper/stepper_widget.dart | 71 ++++ 23 files changed, 1458 insertions(+), 701 deletions(-) create mode 100644 lib/features/hmg_services/models/req_models/search_e_referral_req_model.dart create mode 100644 lib/features/hmg_services/models/resq_models/search_e_referral_resp_model.dart delete mode 100644 lib/presentation/e_referral/e_referral_page_home.dart create mode 100644 lib/presentation/e_referral/e_referral_search_result.dart delete mode 100644 lib/presentation/e_referral/widget/e_referral_stepper.dart create mode 100644 lib/widgets/stepper/stepper_widget.dart diff --git a/lib/core/dependencies.dart b/lib/core/dependencies.dart index 73a93c6..2a5c749 100644 --- a/lib/core/dependencies.dart +++ b/lib/core/dependencies.dart @@ -221,7 +221,7 @@ class AppDependencies { ); getIt.registerLazySingleton( - () => HmgServicesViewModel(bookAppointmentsRepo: getIt(), hmgServicesRepo: getIt(), errorHandlerService: getIt()), + () => HmgServicesViewModel(bookAppointmentsRepo: getIt(), hmgServicesRepo: getIt(), errorHandlerService: getIt(), navigationService: getIt()), ); // Screen-specific VMs → Factory diff --git a/lib/features/hmg_services/hmg_services_repo.dart b/lib/features/hmg_services/hmg_services_repo.dart index 6e1d533..4103569 100644 --- a/lib/features/hmg_services/hmg_services_repo.dart +++ b/lib/features/hmg_services/hmg_services_repo.dart @@ -8,6 +8,7 @@ import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/check_activation_e_referral_req_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/cmc_create_new_order_req_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/order_update_req_model.dart'; +import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/search_e_referral_req_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_all_cities_resp_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_all_orders_resp_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_services_resp_model.dart'; @@ -18,6 +19,7 @@ import 'package:provider/provider.dart'; import 'models/req_models/create_e_referral_model.dart'; import 'models/req_models/send_activation_code_ereferral_req_model.dart'; import 'models/resq_models/relationship_type_resp_mode.dart'; +import 'models/resq_models/search_e_referral_resp_model.dart'; abstract class HmgServicesRepo { Future>>> getAllComprehensiveCheckupOrders(); @@ -56,6 +58,10 @@ abstract class HmgServicesRepo { Future>> createEReferral(CreateEReferralRequestModel requestModel); + + Future>>> searchEReferral(SearchEReferralRequestModel requestModel); + + } class HmgServicesRepoImp implements HmgServicesRepo { @@ -757,4 +763,58 @@ class HmgServicesRepoImp implements HmgServicesRepo { return Left(UnknownFailure(e.toString())); } } + + + + @override + Future>>> searchEReferral(SearchEReferralRequestModel requestBody) async { + + + try { + GenericApiModel>? apiResponse; + Failure? failure; + + await apiClient.post( + ApiConsts.getEReferrals, + body: requestBody.toJson(), + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + loggerService.logError("EReferral Services API Failed: $error, Status: $statusCode"); + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + List searchReferral = []; + + if (response['List_EReferrals'] != null && response['List_EReferrals'] is List) { + final servicesList = response['List_EReferrals'] as List; + + for (var serviceJson in servicesList) { + if (serviceJson is Map) { + searchReferral.add(SearchEReferralResponseModel.fromJson(serviceJson)); + } + } + } + + apiResponse = GenericApiModel>( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: errorMessage, + data: searchReferral, + ); + } catch (e) { + loggerService.logError("Error parsing E-Referral services: ${e.toString()}"); + failure = DataParsingFailure(e.toString()); + } + }, + ); + + if (failure != null) return Left(failure!); + if (apiResponse == null) return Left(ServerFailure("Unknown error")); + return Right(apiResponse!); + } catch (e) { + log("Unknown error in Search Referral: ${e.toString()}"); + return Left(UnknownFailure(e.toString())); + } + } + } diff --git a/lib/features/hmg_services/hmg_services_view_model.dart b/lib/features/hmg_services/hmg_services_view_model.dart index 1e97a63..81cd060 100644 --- a/lib/features/hmg_services/hmg_services_view_model.dart +++ b/lib/features/hmg_services/hmg_services_view_model.dart @@ -1,16 +1,20 @@ import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart'; +import 'package:hmg_patient_app_new/core/enums.dart'; import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_repo.dart'; import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_repo.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/create_e_referral_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/cmc_create_new_order_req_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/order_update_req_model.dart'; +import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/search_e_referral_req_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/send_activation_code_ereferral_req_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_all_cities_resp_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_all_orders_resp_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_services_resp_model.dart'; +import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/search_e_referral_resp_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart'; import 'package:hmg_patient_app_new/services/error_handler_service.dart'; +import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'models/req_models/check_activation_e_referral_req_model.dart'; import 'models/resq_models/relationship_type_resp_mode.dart'; @@ -19,8 +23,9 @@ class HmgServicesViewModel extends ChangeNotifier { final HmgServicesRepo hmgServicesRepo; final BookAppointmentsRepo bookAppointmentsRepo; final ErrorHandlerService errorHandlerService; + final NavigationService navigationService; + HmgServicesViewModel({required this.bookAppointmentsRepo, required this.hmgServicesRepo, required this.errorHandlerService, required this.navigationService}); - HmgServicesViewModel({required this.bookAppointmentsRepo, required this.hmgServicesRepo, required this.errorHandlerService}); bool isCmcOrdersLoading = false; bool isCmcServicesLoading = false; @@ -51,8 +56,9 @@ class HmgServicesViewModel extends ChangeNotifier { List relationTypes =[]; List getAllCitiesList =[]; + List searchReferralList =[]; - Future getOrdersList() async { + Future getOrdersList() async {} // HHC multiple services selection List selectedHhcServices = []; @@ -702,4 +708,84 @@ class HmgServicesViewModel extends ChangeNotifier { ); } + + + Future searchEReferral({ + required SearchEReferralRequestModel requestModel, + Function(dynamic)? onSuccess, + Function(String)? onError, + }) async { + + notifyListeners(); + + final result = await hmgServicesRepo.searchEReferral(requestModel); + + result.fold( + (failure) async { + + notifyListeners(); + await errorHandlerService.handleError(failure: failure); + if (onError != null) { + onError(failure.toString()); + } + }, + (apiResponse) { + + if (apiResponse.messageStatus == 1) { + searchReferralList = apiResponse.data ?? []; + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } else { + notifyListeners(); + if (onError != null) { + onError(apiResponse.errorMessage ?? 'Unknown error'); + } + } + }, + ); + } + + Future navigateToOTPScreen( + {required OTPTypeEnum otpTypeEnum, + required String phoneNumber, + required String loginToken, + required Function onSuccess, + }) async { + + navigationService.pushToOtpScreen( + phoneNumber: phoneNumber, + isFormFamilyFile: false, + checkActivationCode: (int activationCode) async { + + checkEReferralActivationCode( + requestModel: CheckActivationCodeForEReferralRequestModel( + logInTokenID: loginToken, + activationCode: activationCode.toString(), + ), + onSuccess: (GenericApiModel response) { + onSuccess(); + }, + onError: (String errorMessage) { + print(errorMessage); + }, + ); + }, + onResendOTPPressed: (String phoneNumber) async { + // await sendActivationCode( + // otpTypeEnum: otpTypeEnum, + // phoneNumber: phoneNumberController.text, + // nationalIdOrFileNumber: nationalIdController.text, + // isForRegister: isComingFromRegister, + // isComingFromResendOTP: true, + // payload: payload, + // isFormFamilyFile: isFormFamilyFile, + // isExcludedUser: isExcludedUser, + // responseID: responseID, + // ); + }, + ); + } + } diff --git a/lib/features/hmg_services/models/req_models/search_e_referral_req_model.dart b/lib/features/hmg_services/models/req_models/search_e_referral_req_model.dart new file mode 100644 index 0000000..deb3a92 --- /dev/null +++ b/lib/features/hmg_services/models/req_models/search_e_referral_req_model.dart @@ -0,0 +1,60 @@ +class SearchEReferralRequestModel { + String? patientMobileNumber; + double? versionID; + int? channel; + int? languageID; + String? iPAdress; + String? generalid; + int? patientOutSA; + dynamic sessionID; + bool? isDentalAllowedBackend; + int? deviceTypeID; + int? referralNumber; + String? identificationNo; + + SearchEReferralRequestModel( + {this.patientMobileNumber, + this.versionID, + this.channel, + this.languageID, + this.iPAdress, + this.generalid, + this.patientOutSA, + this.sessionID, + this.isDentalAllowedBackend, + this.deviceTypeID, + this.referralNumber, + this.identificationNo}); + + SearchEReferralRequestModel.fromJson(Map json) { + patientMobileNumber = json['PatientMobileNumber']; + versionID = json['VersionID']; + channel = json['Channel']; + languageID = json['LanguageID']; + iPAdress = json['IPAdress']; + generalid = json['generalid']; + patientOutSA = json['PatientOutSA']; + sessionID = json['SessionID']; + isDentalAllowedBackend = json['isDentalAllowedBackend']; + deviceTypeID = json['DeviceTypeID']; + referralNumber = json['ReferralNumber']; + identificationNo = json['IdentificationNo']; + } + + Map toJson() { + final Map data = new Map(); + data['PatientMobileNumber'] = this.patientMobileNumber; + data['VersionID'] = this.versionID; + data['Channel'] = this.channel; + data['LanguageID'] = this.languageID; + data['IPAdress'] = this.iPAdress; + data['generalid'] = this.generalid; + data['PatientOutSA'] = this.patientOutSA; + data['SessionID'] = this.sessionID; + data['isDentalAllowedBackend'] = this.isDentalAllowedBackend; + data['DeviceTypeID'] = this.deviceTypeID; + data['ReferralNumber'] = this.referralNumber; + data['IdentificationNo'] = this.identificationNo; + return data; + } +} diff --git a/lib/features/hmg_services/models/resq_models/search_e_referral_resp_model.dart b/lib/features/hmg_services/models/resq_models/search_e_referral_resp_model.dart new file mode 100644 index 0000000..df72930 --- /dev/null +++ b/lib/features/hmg_services/models/resq_models/search_e_referral_resp_model.dart @@ -0,0 +1,161 @@ +import 'dart:convert'; + +class SearchEReferralResponseModel { + dynamic acceptedBrachCode; + dynamic acceptedBranchName; + dynamic acceptedBranchNameAr; + dynamic channel; + dynamic identityCardAttachment; + String? identityNumber; + dynamic insuranceCardAttachment; + bool? isInsuredPatient; + List? medicalReportAttachment; + String? otherRelationship; + String? patientContactNo; + int? patientId; + String? patientName; + dynamic preferredBranchCode; + dynamic preferredBranchName; + String? referralDate; + int? referralNumber; + RelationshipType? relationshipType; + String? requesterContactNo; + String? requesterName; + String? status; + String? statusAr; + + SearchEReferralResponseModel({ + this.acceptedBrachCode, + this.acceptedBranchName, + this.acceptedBranchNameAr, + this.channel, + this.identityCardAttachment, + this.identityNumber, + this.insuranceCardAttachment, + this.isInsuredPatient, + this.medicalReportAttachment, + this.otherRelationship, + this.patientContactNo, + this.patientId, + this.patientName, + this.preferredBranchCode, + this.preferredBranchName, + this.referralDate, + this.referralNumber, + this.relationshipType, + this.requesterContactNo, + this.requesterName, + this.status, + this.statusAr, + }); + + factory SearchEReferralResponseModel.fromRawJson(String str) => SearchEReferralResponseModel.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory SearchEReferralResponseModel.fromJson(Map json) => SearchEReferralResponseModel( + acceptedBrachCode: json["AcceptedBrachCode"], + acceptedBranchName: json["AcceptedBranchName"], + acceptedBranchNameAr: json["AcceptedBranchNameAr"], + channel: json["Channel"], + identityCardAttachment: json["IdentityCardAttachment"], + identityNumber: json["IdentityNumber"], + insuranceCardAttachment: json["InsuranceCardAttachment"], + isInsuredPatient: json["IsInsuredPatient"], + medicalReportAttachment: json["MedicalReportAttachment"] == null ? [] : List.from(json["MedicalReportAttachment"]!.map((x) => MedicalReportAttachment.fromJson(x))), + otherRelationship: json["OtherRelationship"], + patientContactNo: json["PatientContactNo"], + patientId: json["PatientId"], + patientName: json["PatientName"], + preferredBranchCode: json["PreferredBranchCode"], + preferredBranchName: json["PreferredBranchName"], + referralDate: json["ReferralDate"], + referralNumber: json["ReferralNumber"], + relationshipType: json["RelationshipType"] == null ? null : RelationshipType.fromJson(json["RelationshipType"]), + requesterContactNo: json["RequesterContactNo"], + requesterName: json["RequesterName"], + status: json["Status"], + statusAr: json["StatusAr"], + ); + + Map toJson() => { + "AcceptedBrachCode": acceptedBrachCode, + "AcceptedBranchName": acceptedBranchName, + "AcceptedBranchNameAr": acceptedBranchNameAr, + "Channel": channel, + "IdentityCardAttachment": identityCardAttachment, + "IdentityNumber": identityNumber, + "InsuranceCardAttachment": insuranceCardAttachment, + "IsInsuredPatient": isInsuredPatient, + "MedicalReportAttachment": medicalReportAttachment == null ? [] : List.from(medicalReportAttachment!.map((x) => x.toJson())), + "OtherRelationship": otherRelationship, + "PatientContactNo": patientContactNo, + "PatientId": patientId, + "PatientName": patientName, + "PreferredBranchCode": preferredBranchCode, + "PreferredBranchName": preferredBranchName, + "ReferralDate": referralDate, + "ReferralNumber": referralNumber, + "RelationshipType": relationshipType?.toJson(), + "RequesterContactNo": requesterContactNo, + "RequesterName": requesterName, + "Status": status, + "StatusAr": statusAr, + }; +} + +class MedicalReportAttachment { + String? base64String; + String? fileName; + + MedicalReportAttachment({ + this.base64String, + this.fileName, + }); + + factory MedicalReportAttachment.fromRawJson(String str) => MedicalReportAttachment.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory MedicalReportAttachment.fromJson(Map json) => MedicalReportAttachment( + base64String: json["Base64String"], + fileName: json["FileName"], + ); + + Map toJson() => { + "Base64String": base64String, + "FileName": fileName, + }; +} + +class RelationshipType { + int? id; + String? text; + String? textAr; + String? textEn; + + RelationshipType({ + this.id, + this.text, + this.textAr, + this.textEn, + }); + + factory RelationshipType.fromRawJson(String str) => RelationshipType.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory RelationshipType.fromJson(Map json) => RelationshipType( + id: json["ID"], + text: json["Text"], + textAr: json["Text_Ar"], + textEn: json["Text_En"], + ); + + Map toJson() => { + "ID": id, + "Text": text, + "Text_Ar": textAr, + "Text_En": textEn, + }; +} diff --git a/lib/features/hmg_services/models/ui_models/e_referral_form_model.dart b/lib/features/hmg_services/models/ui_models/e_referral_form_model.dart index fded059..32964b1 100644 --- a/lib/features/hmg_services/models/ui_models/e_referral_form_model.dart +++ b/lib/features/hmg_services/models/ui_models/e_referral_form_model.dart @@ -9,7 +9,10 @@ import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/ class ReferralFormData { String requesterName = ''; String requesterPhone = ''; + String searchPhone =''; CountryEnum countryEnum = CountryEnum.saudiArabia; + CountryEnum patientCountryEnum = CountryEnum.saudiArabia; + GetAllRelationshipTypeResponseModel? relationship; String otherRelationshipName = ''; @@ -39,4 +42,7 @@ class FormValidationErrors { String? medicalReport; String? branch; String? insuredDocument; + + String? searchValue; + String? searchPhone; } \ No newline at end of file diff --git a/lib/presentation/e_referral/e-referral_validator.dart b/lib/presentation/e_referral/e-referral_validator.dart index 78efb49..3e3399d 100644 --- a/lib/presentation/e_referral/e-referral_validator.dart +++ b/lib/presentation/e_referral/e-referral_validator.dart @@ -10,12 +10,12 @@ class ReferralValidator { errors.requesterName = 'Referral requester name is required'; } - if (formData.requesterPhone.trim().isEmpty) { - errors.requesterPhone = 'Phone number is required'; - } else if (formData.countryEnum.countryCode == '966' && - !_isValidSaudiPhone(formData.requesterPhone)) { - errors.requesterPhone = 'Please enter a valid Saudi phone number (5xxxxxxxx)'; - } + // if (formData.requesterPhone.trim().isEmpty) { + // errors.requesterPhone = 'Phone number is required'; + // } else if (formData.countryEnum.countryCode == '966' && + // !_isValidSaudiPhone(formData.requesterPhone)) { + // errors.requesterPhone = 'Please enter a valid Saudi phone number (5xxxxxxxx)'; + // } if (formData.relationship == null) { errors.relationship = 'Please select a relationship'; diff --git a/lib/presentation/e_referral/e_referral_form_manager.dart b/lib/presentation/e_referral/e_referral_form_manager.dart index a69a2fe..f7093c5 100644 --- a/lib/presentation/e_referral/e_referral_form_manager.dart +++ b/lib/presentation/e_referral/e_referral_form_manager.dart @@ -16,7 +16,16 @@ class ReferralFormManager extends ChangeNotifier { ReferralFormData get formData => _formData; FormValidationErrors get errors => _errors; - // Field-specific update methods that don't notify listeners immediately + + int _searchCriteria = 0; // 0 = Identification Number, 1 = Referral Number + String? _searchValue; + String? _searchPhone; + +// Getters + int get searchCriteria => _searchCriteria; + String? get searchValue => _searchValue; + String? get searchPhone => _searchPhone; + void updateRequesterName(String value) { _formData.requesterName = value; _clearError('requesterName'); @@ -30,9 +39,13 @@ class ReferralFormManager extends ChangeNotifier { void updateCountryEnum(CountryEnum value) { _formData.countryEnum = value; } + void updatePatientCountryEnum(CountryEnum value) { + _formData.patientCountryEnum = value; + } void updateRelationship(GetAllRelationshipTypeResponseModel? value) { _formData.relationship = value; + notifyListeners(); _clearError('relationship'); } @@ -59,6 +72,7 @@ class ReferralFormManager extends ChangeNotifier { void updatePatientCity(GetAllCitiesResponseModel? value) { _formData.patientCity = value; _clearError('patientCity'); + notifyListeners(); } void updateBranch(HospitalsModel? value) { @@ -83,6 +97,7 @@ class ReferralFormManager extends ChangeNotifier { if (index >= 0 && index < _formData.medicalReportImages.length) { _formData.medicalReportImages.removeAt(index); } + notifyListeners(); } void addInsuranceDocument(EReferralAttachment attachment) { @@ -94,8 +109,11 @@ class ReferralFormManager extends ChangeNotifier { if (index >= 0 && index < _formData.insuredPatientImages.length) { _formData.insuredPatientImages.removeAt(index); } + + notifyListeners(); } + // Error management void setErrors(FormValidationErrors newErrors) { _errors.requesterName = newErrors.requesterName; @@ -203,4 +221,70 @@ class ReferralFormManager extends ChangeNotifier { notifyListeners(); } } + void updateSearchCriteria(int criteria) { + _searchCriteria = criteria; + _errors.searchValue = _validateSearchValue(); + notifyListeners(); + } + + void updateSearchValue(String value) { + _searchValue = value; + _errors.searchValue = _validateSearchValue(); + } + void updateSearchPhone(String value) { + _searchPhone = value; + _errors.searchPhone = _validateSearchPhone(); + } + + String? _validateSearchValue() { + if (_searchValue == null || _searchValue!.isEmpty) { + return _searchCriteria == 0 + ? 'Identification Number is required' + : 'Referral Number is required'; + } + + if (_searchCriteria == 0) { + // Validate identification number format + if (!_isValidIdentificationNumber(_searchValue!)) { + return 'Please enter a valid identification number'; + } + } else { + // Validate referral number format + if (!_isValidReferralNumber(_searchValue!)) { + return 'Please enter a valid referral number'; + } + } + + return null; + } + + bool _isValidIdentificationNumber(String value) { + // Add your identification number validation logic + return value.isNotEmpty; // Basic validation + } + + bool _isValidReferralNumber(String value) { + // Add your referral number validation logic + return value.isNotEmpty; // Basic validation + } + + void validateSearchForm() { + _errors.searchValue = _validateSearchValue(); + _errors.searchPhone = _validateSearchPhone(); + notifyListeners(); + } + + String? _validateSearchPhone() { + if (_searchPhone == null || _searchPhone!.isEmpty) { + return 'Phone number is required'; + } + return null; + } + bool get isSearchFormInValid { + return ( + _errors.searchValue != null && + _errors.searchValue!.isNotEmpty || + _errors.searchPhone != null && + _errors.searchPhone!.isNotEmpty); + } } \ No newline at end of file diff --git a/lib/presentation/e_referral/e_referral_page_home.dart b/lib/presentation/e_referral/e_referral_page_home.dart deleted file mode 100644 index d41defb..0000000 --- a/lib/presentation/e_referral/e_referral_page_home.dart +++ /dev/null @@ -1,103 +0,0 @@ -import 'dart:ui'; -import 'package:easy_localization/easy_localization.dart'; -import 'package:flutter/material.dart'; -import 'package:hmg_patient_app_new/core/app_export.dart'; -import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; -import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; -import 'package:hmg_patient_app_new/features/contact_us/contact_us_view_model.dart'; -import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; -import 'package:hmg_patient_app_new/presentation/e_referral/new_e_referral.dart'; -import 'package:hmg_patient_app_new/presentation/e_referral/search_e_referral.dart'; -import 'package:hmg_patient_app_new/theme/colors.dart'; -import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; -import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; -import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart'; -import 'package:provider/provider.dart'; - -class EReferralPage extends StatefulWidget { - const EReferralPage({super.key}); - - @override - _EReferralPageState createState() => _EReferralPageState(); -} - -class _EReferralPageState extends State { - @override - void initState() { - super.initState(); - } - - @override - void dispose() { - super.dispose(); - } - - bool isNewReferral = true; - VoidCallback? onNextStep; - int _currentPageIndex = 0; - int activeTabIndex = 0; - @override - Widget build(BuildContext context) { - return Scaffold( - backgroundColor: AppColors.bgScaffoldColor, - body: CollapsingListView( - title: "E Referral".needTranslation, - child: Consumer(builder: (context, contactUsVM, child) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox(height: 16.h), - CustomTabBar( - activeTextColor: AppColors.primaryRedColor, - activeBackgroundColor: AppColors.primaryRedColor.withValues(alpha: .1), - tabs: [ - CustomTabBarModel(null, "New Referral".needTranslation), - CustomTabBarModel(null, "Search Referral".needTranslation), - ], - onTabChange: (index) { - activeTabIndex =index; - setState(() { - - }); - }, - ).paddingSymmetrical(24.h, 0.h), - SizedBox(height: 24.h), - activeTabIndex ==0 ? NewReferralPage( - onNextStep: (nextStep) { - WidgetsBinding.instance.addPostFrameCallback((_) { - setState(() { - onNextStep = nextStep; - }); - }); - }, - onStepChanged: (int value) { - setState(() { - _currentPageIndex = value; - }); - }, - ) : - SearchEReferralPage( - onNextStep: (onNextStep) { - WidgetsBinding.instance.addPostFrameCallback((_) { - setState(() { - onNextStep = onNextStep; - }); - }); - }, - ) - ], - ); - }), - ), - bottomNavigationBar: Padding( - padding: EdgeInsets.all(20.h), - child: CustomButton( - text:activeTabIndex == 0 ? _currentPageIndex >= 2 ? LocaleKeys.submit.tr() : LocaleKeys.next.tr() : LocaleKeys.search.tr(), - onPressed: onNextStep ?? () {}, - backgroundColor: AppColors.primaryRedColor, - borderColor: AppColors.primaryRedColor, - textColor: AppColors.whiteColor, - )), - ); - } -} diff --git a/lib/presentation/e_referral/e_referral_search_result.dart b/lib/presentation/e_referral/e_referral_search_result.dart new file mode 100644 index 0000000..3f3966e --- /dev/null +++ b/lib/presentation/e_referral/e_referral_search_result.dart @@ -0,0 +1,310 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_export.dart'; +import 'package:hmg_patient_app_new/core/utils/date_util.dart'; +import 'package:hmg_patient_app_new/core/utils/utils.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart'; +import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/search_e_referral_resp_model.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; +import 'package:provider/provider.dart'; +import 'package:smooth_corner/smooth_corner.dart'; + +class SearchResultPage extends StatefulWidget { + const SearchResultPage({super.key}); + + @override + _SearchResultPageState createState() => _SearchResultPageState(); +} + +class _SearchResultPageState extends State { + HmgServicesViewModel? hmgServicesVM; + + String _selectedFilter = 'All'; + + @override + void initState() { + hmgServicesVM = context.read(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return CollapsingListView( + title: "Search Result".needTranslation, + child: Column( + children: [ + // List of referrals + ListView.builder( + padding: EdgeInsets.zero, + shrinkWrap: true, + itemCount: hmgServicesVM?.searchReferralList.length, + itemBuilder: (context, index) { + return _buildReferralCard(hmgServicesVM!.searchReferralList[index]); + }, + ), + ], + ), + ); + } + + Widget _buildReferralCard(SearchEReferralResponseModel referral) { + return SmoothCard( + borderRadius: BorderRadius.circular(16.h), + margin: EdgeInsets.symmetric(horizontal: 16, vertical: 8), + color: AppColors.whiteColor, + child: Padding( + padding: EdgeInsets.all(16.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + + 'Referral No ${referral.referralNumber}'.needTranslation.toText18(isBold: true, color: AppColors.textColor), + + + Container( + padding: EdgeInsets.symmetric(horizontal: 12, vertical: 6), + decoration: BoxDecoration( + color: _getStatusColor(referral.status!), + borderRadius: BorderRadius.circular(20), + ), + child: + referral.status!.toText12(color: AppColors.whiteColor, + // style: TextStyle( + // color: Colors.white, + // fontSize: 12, + // fontWeight: FontWeight.w500, + // ), + ), + ), + ], + ), + + SizedBox(height: 16), + + // Patient information + Row( + children: [ + Container( + width: 50, + height: 50, + decoration: BoxDecoration( + color: AppColors.lightGrayBGColor, + shape: BoxShape.circle, + ), + child: Icon( + Icons.person, + color: AppColors.lightGrayBGColor, + size: 30, + ), + ), + SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + //Text( + referral.patientName!.toText16(isBold: true, color: AppColors.textColor), + // style: TextStyle( + // fontSize: 16, + // fontWeight: FontWeight.bold, + // ), + // ), + SizedBox(height: 4), + // Text( + 'ID: ${referral.identityNumber}'.toText14(color: AppColors.greyTextColor), + // style: TextStyle( + // color: Colors.grey[600], + // ), + // ), + ], + ), + ), + ], + ), + + SizedBox(height: 16), + + // Details row + Row( + children: [ + _buildDetailItem( + Icons.phone, + referral.patientContactNo!, + ), + SizedBox(width: 16), + _buildDetailItem(Icons.calendar_today, Utils.getDayMonthYearDateFormatted(DateUtil.convertStringToDateNoTimeZone(referral.referralDate!))), + ], + ), + + SizedBox(height: 16), + + // Requester information + Container( + padding: EdgeInsets.all(5.h), + decoration: BoxDecoration( + color: AppColors.lightGrayBGColor, + borderRadius: BorderRadius.circular(8), + ), + child: Row( + children: [ + Icon( + Icons.person_outline, + color: Colors.grey[600], + size: 20, + ), + SizedBox(width: 8), + //Text( + 'Requester: ${referral.requesterName}'.toText14(), + // style: TextStyle( + // color: Colors.grey[700], + // ), + // ), + SizedBox(width: 16), + Icon( + Icons.phone, + color: Colors.grey[600], + size: 16, + ), + SizedBox(width: 4), + // Text( + referral.requesterContactNo!.toText14(), + // style: TextStyle( + // color: Colors.grey[700], + // ), + // ), + ], + ), + ), + + SizedBox(height: 12), + + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Icon( + Icons.group, + color: Colors.grey[600], + size: 16, + ), + SizedBox(width: 4), + //Text( + 'Relationship: ${referral.relationshipType?.text}'.toText14(color:AppColors.greyTextColor), + // style: TextStyle( + // color: Colors.grey[700], + // fontSize: 14, + // ), + // ), + ], + ), + Row( + children: [ + Icon( + Icons.attach_file, + color: Colors.grey[600], + size: 16, + ), + SizedBox(width: 4), + // Text( + '${referral.medicalReportAttachment?.length} file(s)'.toText14(color:AppColors.greyTextColor), + // style: TextStyle( + // color: Colors.grey[700], + // fontSize: 14, + // ), + // ), + ], + ), + ], + ), + + SizedBox(height: 16), + + // Action buttons + ], + ), + ), + ); + } + + Widget _buildDetailItem(IconData icon, String text) { + return Row( + children: [ + Icon( + icon, + color: Colors.grey[600], + size: 16, + ), + SizedBox(width: 4), + // Text( + text.toText14(color: AppColors.greyTextColor), + // style: TextStyle( + // color: Colors.grey[700], + // ), + // ), + ], + ); + } + + Color _getStatusColor(String status) { + switch (status) { + case 'Pending': + return AppColors.alertColor; + case 'Completed': + return AppColors.bgGreenColor; + case 'Rejected': + return AppColors.primaryRedColor; + default: + return AppColors.lightGrayColor; + } + } + + void _showFilterOptions() { + showModalBottomSheet( + context: context, + builder: (context) { + return Container( + padding: EdgeInsets.all(16), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + 'Filter Results', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 16), + _buildFilterOption('All'), + _buildFilterOption('Pending'), + _buildFilterOption('Completed'), + _buildFilterOption('Rejected'), + ], + ), + ); + }, + ); + } + + Widget _buildFilterOption(String filter) { + return ListTile( + leading: Icon( + _selectedFilter == filter ? Icons.radio_button_checked : Icons.radio_button_off, + color: _selectedFilter == filter ? Colors.blue[700] : Colors.grey, + ), + title: Text(filter), + onTap: () { + setState(() { + _selectedFilter = filter; + }); + Navigator.pop(context); + }, + ); + } +} diff --git a/lib/presentation/e_referral/new_e_referral.dart b/lib/presentation/e_referral/new_e_referral.dart index ca96153..e48d8ca 100644 --- a/lib/presentation/e_referral/new_e_referral.dart +++ b/lib/presentation/e_referral/new_e_referral.dart @@ -1,27 +1,35 @@ - +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_export.dart'; +import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart'; +import 'package:hmg_patient_app_new/core/utils/utils.dart'; +import 'package:hmg_patient_app_new/extensions/route_extensions.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart'; import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/create_e_referral_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/ui_models/e_referral_form_model.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/presentation/e_referral/search_e_referral.dart'; import 'package:hmg_patient_app_new/presentation/e_referral/widget/e-referral_otp.dart'; import 'package:hmg_patient_app_new/presentation/e_referral/widget/e_referral_other_details.dart'; import 'package:hmg_patient_app_new/presentation/e_referral/widget/e_referral_patient_info.dart'; import 'package:hmg_patient_app_new/presentation/e_referral/widget/e_referral_requester_form.dart'; -import 'package:hmg_patient_app_new/presentation/e_referral/widget/e_referral_stepper.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; +import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; +import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; +import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; +import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; +import 'package:hmg_patient_app_new/widgets/stepper/stepper_widget.dart'; import 'package:provider/provider.dart'; import 'e-referral_validator.dart'; import 'e_referral_form_manager.dart'; class NewReferralPage extends StatefulWidget { - final Function(VoidCallback) onNextStep; - final Function(int) onStepChanged; - const NewReferralPage({ super.key, - required this.onNextStep, - required this.onStepChanged, }); @override @@ -33,17 +41,14 @@ class _NewReferralPageState extends State { final ReferralFormManager _formManager = ReferralFormManager(); // Use manager int _currentStep = 0; - final List _steps = [ - 'Requester Info', - 'Patient Information', - 'Other details' - ]; + double widthOfOneState = ((ResponsiveExtension.screenWidth) / 3) - (20.h); + + final List _steps = ['Requester Info', 'Patient Information', 'Other details']; @override void initState() { super.initState(); _loadData(); - widget.onNextStep(_handleNextStep); } void _handleNextStep() { @@ -52,8 +57,7 @@ class _NewReferralPageState extends State { if (_validateCurrentStep()) { OTPService.openOTPScreen( context: context, - phoneNumber: _formManager.formData.requesterPhone ?? '', - countryEnum: _formManager.formData.countryEnum, + formManager: _formManager, onSuccess: _proceedToNextStep, ); } @@ -99,44 +103,45 @@ class _NewReferralPageState extends State { duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, ); - setState(() { - _currentStep++; - }); - widget.onStepChanged(_currentStep); + // setState(() { + _currentStep++; + // }); + // widget.onStepChanged(_currentStep); } void _submitReferral() { - - CreateEReferralRequestModel createReferrralRequestModel = - CreateEReferralRequestModel( + CreateEReferralRequestModel createReferralRequestModel = CreateEReferralRequestModel( isInsuredPatient: _formManager.formData.isPatientInsured, - cityCode: _formManager.formData.patientCity!.iD!.toString(), - cityName: _formManager.formData.patientCity!.description, + cityCode: _formManager.formData.patientCity!.iD!.toString(), + cityName: _formManager.formData.patientCity!.description, requesterName: _formManager.formData.requesterName, - requesterContactNo: _formManager.formData.requesterPhone, + requesterContactNo: _formManager.formData.countryEnum.countryCode + _formManager.formData.requesterPhone, requesterRelationship: _formManager.formData.relationship?.iD, otherRelationship: _formManager.formData.relationship!.iD.toString(), fullName: _formManager.formData.patientName, identificationNo: int.tryParse(_formManager.formData!.patientIdentification ?? '0'), - patientMobileNumber: _formManager.formData.patientPhone, + patientMobileNumber: _formManager.formData.patientCountryEnum.countryCode + _formManager.formData.patientPhone, preferredBranchCode: _formManager.formData.branch!.iD, medicalReportAttachment: _formManager.formData.medicalReportImages, insuranceCardAttachment: _formManager.formData.insuredPatientImages, - preferredBranchName: _formManager.formData.branch!.desciption - ); + preferredBranchName: _formManager.formData.branch!.desciption); final hmgServicesVM = context.read(); + LoaderBottomSheet.showLoader(); + hmgServicesVM.createEReferral( - requestModel: createReferrralRequestModel, - onSuccess: (response) { - print("E-Referral submitted successfully"); + requestModel: createReferralRequestModel, + onSuccess: (GenericApiModel response) { + + showSuccessBottomSheet(int.parse(response.data), hmgServicesVM); + LoaderBottomSheet.hideLoader(); + }, onError: (errorMessage) { // Handle error (e.g., show error message) print(errorMessage); }, ); - } void _loadData() { @@ -152,32 +157,107 @@ class _NewReferralPageState extends State { @override Widget build(BuildContext context) { - return ChangeNotifierProvider.value( - value: _formManager, - child: SizedBox( - height: MediaQuery.of(context).size.height, + return Scaffold( + backgroundColor: AppColors.bgScaffoldColor, + body: CollapsingListView( + title: "E Referral".needTranslation, + isClose: false, + search: () async { + await Navigator.of(context).push( + CustomPageRoute( + page: SearchEReferralPage(), + fullScreenDialog: true, + direction: AxisDirection.down, + ), + ); + }, + bottomChild: Container( + color: Colors.white, + padding: EdgeInsets.all(ResponsiveExtension(20).h), + child: CustomButton( + text: _currentStep <=1 ? LocaleKeys.next.tr() : LocaleKeys.submit.tr(), + // icon: AppAssets.search_icon, + iconColor: Colors.white, + onPressed: () => {_handleNextStep()}, + ), + ), + child: ChangeNotifierProvider.value( + value: _formManager, + child: SizedBox( + height: ResponsiveExtension.screenHeight * 0.65, + child: Column( + children: [ + const SizedBox(height: 8), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: List.generate(3, (index) { + if (_currentStep == index) { + return StepperWidget(widthOfOneState, AppColors.primaryRedColor, true, 4.h); + } else { + return StepperWidget(widthOfOneState, AppColors.greyLightColor, false, 4.h); + } + })), + Expanded( + child: PageView( + controller: _pageController, + physics: const NeverScrollableScrollPhysics(), + onPageChanged: (index) => { + // setState(() => _currentStep = index) + }, + children: [ + RequesterFormStep(), + PatientInformationStep(), + OtherDetailsStep(), + ], + )), + ], + )), + ))); + // ); + } + + + showSuccessBottomSheet(int requestId, HmgServicesViewModel hmgServicesViewModel) { + return showCommonBottomSheetWithoutHeight( + context, + child: Padding( + padding: EdgeInsets.all(16.w), child: Column( children: [ - const SizedBox(height: 8), - ProgressStepperWidget( - currentStep: _currentStep, - steps: _steps, + Utils.getSuccessWidget(loadingText: "Your Referral has been created Successfully.".needTranslation), + Row( + children: [ + "Here is your Referral #: ".needTranslation.toText14( + color: AppColors.textColorLight, + weight: FontWeight.w500, + ), + SizedBox(width: 4.w), + ("$requestId").toText16(isBold: true), + ], ), - Expanded( - child: PageView( - controller: _pageController, - physics: const NeverScrollableScrollPhysics(), - onPageChanged: (index) => setState(() => _currentStep = index), - children: [ - RequesterFormStep(), - PatientInformationStep(), - OtherDetailsStep(), - ], - ), + SizedBox(height: 24.h), + Row( + children: [ + Expanded( + child: CustomButton( + height: 56.h, + text: LocaleKeys.ok.tr(), + onPressed: () { + context.pop(); + context.pop(); + _currentStep =0; + }, + textColor: AppColors.whiteColor, + ), + ), + ], ), ], ), ), + isCloseButtonVisible: false, + isDismissible: false, + isFullScreen: false, ); } -} \ No newline at end of file +} diff --git a/lib/presentation/e_referral/search_e_referral.dart b/lib/presentation/e_referral/search_e_referral.dart index fabf115..ea749ee 100644 --- a/lib/presentation/e_referral/search_e_referral.dart +++ b/lib/presentation/e_referral/search_e_referral.dart @@ -1,19 +1,24 @@ - +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart'; -import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/create_e_referral_model.dart'; +import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/search_e_referral_req_model.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/e_referral/widget/search_e_referral_form.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; +import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; +import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; +import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:provider/provider.dart'; -import 'e-referral_validator.dart'; import 'e_referral_form_manager.dart'; +import 'e_referral_search_result.dart'; class SearchEReferralPage extends StatefulWidget { - final Function(VoidCallback) onNextStep; - const SearchEReferralPage({ super.key, - required this.onNextStep, }); @override @@ -21,78 +26,95 @@ class SearchEReferralPage extends StatefulWidget { } class _SearchEReferralPageState extends State { - final PageController _pageController = PageController(); - final ReferralFormManager _formManager = ReferralFormManager(); // Use manager - int _currentStep = 0; - - - + // final PageController _pageController = PageController(); + final ReferralFormManager _formManager = ReferralFormManager(); + late HmgServicesViewModel hmgServicesVM; @override void initState() { super.initState(); _loadData(); - widget.onNextStep(_handleNextStep); } - void _handleNextStep() { - _searchReferral(); + void _handleSearch() { + _formManager.validateSearchForm(); + if (!_formManager.isSearchFormInValid) { + _searchReferral(); } + } void _searchReferral() { + SearchEReferralRequestModel searchEReferralReq; - CreateEReferralRequestModel createReferrralRequestModel = - CreateEReferralRequestModel( - isInsuredPatient: _formManager.formData.isPatientInsured, - cityCode: _formManager.formData.patientCity!.iD!.toString(), - cityName: _formManager.formData.patientCity!.description, - requesterName: _formManager.formData.requesterName, - requesterContactNo: _formManager.formData.requesterPhone, - requesterRelationship: _formManager.formData.relationship?.iD, - otherRelationship: _formManager.formData.relationship!.iD.toString(), - fullName: _formManager.formData.patientName, - identificationNo: int.tryParse(_formManager.formData!.patientIdentification ?? '0'), - patientMobileNumber: _formManager.formData.patientPhone, - preferredBranchCode: _formManager.formData.branch!.iD, - medicalReportAttachment: _formManager.formData.medicalReportImages, - insuranceCardAttachment: _formManager.formData.insuredPatientImages, - preferredBranchName: _formManager.formData.branch!.desciption - ); + if (_formManager.searchCriteria == 0) { + searchEReferralReq = SearchEReferralRequestModel( + identificationNo: _formManager.searchValue, + patientMobileNumber: _formManager.formData.countryEnum.countryCode + _formManager.searchPhone!, + referralNumber: 0, + ); + } else { - final hmgServicesVM = context.read(); - hmgServicesVM.createEReferral( - requestModel: createReferrralRequestModel, - onSuccess: (response) { - print("E-Referral submitted successfully"); - }, - onError: (errorMessage) { - // Handle error (e.g., show error message) - print(errorMessage); + searchEReferralReq = SearchEReferralRequestModel(referralNumber: int.parse(_formManager.searchValue!), patientMobileNumber: _formManager.formData.patientPhone, identificationNo: ''); + } + + hmgServicesVM = context.read(); + LoaderBottomSheet.showLoader(); + hmgServicesVM.searchEReferral( + requestModel: searchEReferralReq, + onSuccess: (response) async { + LoaderBottomSheet.hideLoader(); + await Navigator.of(context).push( + CustomPageRoute( + page: SearchResultPage(), + fullScreenDialog: true, + direction: AxisDirection.down, + ), + ); }, - ); + ); } void _loadData() { - + WidgetsBinding.instance.addPostFrameCallback((_) {}); } @override Widget build(BuildContext context) { - return ChangeNotifierProvider.value( - value: _formManager, - child: SizedBox( - height: MediaQuery.of(context).size.height, - child: Column( - children: [ - - Expanded( - child: + return CollapsingListView( + title: "Search E-Referral".needTranslation, + isClose: true, + bottomChild: Container( + color: Colors.white, + padding: EdgeInsets.all(ResponsiveExtension(20).h), + child: CustomButton( + text: LocaleKeys.search.tr(), + icon: AppAssets.search_icon, + iconColor: Colors.white, + onPressed: () => { + _handleSearch() + }, + ), + ), + child: ChangeNotifierProvider.value( + value: _formManager, + child: Column( + children: [ + _buildHeader(), SearchEReferralFormForm(), - ), - ], - ), + ], + )), + ); + } + + Widget _buildHeader() { + return Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [SizedBox(height: 8), 'Please enter the required information to search for an e-referral'.needTranslation.toText12()], ), ); } -} \ No newline at end of file + +} diff --git a/lib/presentation/e_referral/widget/e-referral_otp.dart b/lib/presentation/e_referral/widget/e-referral_otp.dart index 3fcac6c..c3f5872 100644 --- a/lib/presentation/e_referral/widget/e-referral_otp.dart +++ b/lib/presentation/e_referral/widget/e-referral_otp.dart @@ -1,8 +1,17 @@ // services/otp_service.dart +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/enums.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/core/utils/validation_utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/presentation/e_referral/e_referral_form_manager.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/bottomsheet/generic_bottom_sheet.dart'; +import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; +import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; import 'package:provider/provider.dart'; import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart'; @@ -14,92 +23,101 @@ import 'package:hmg_patient_app_new/features/authentication/widgets/otp_verifica class OTPService { static void openOTPScreen({ required BuildContext context, - required String phoneNumber, - required CountryEnum countryEnum, + required ReferralFormManager formManager, required Function onSuccess, }) { - final hmgServicesViewModel = context.read(); - - hmgServicesViewModel.eReferralSendActivationCode( - requestModel: SendActivationCodeForEReferralRequestModel( - patientMobileNumber: int.parse(phoneNumber), - zipCode: countryEnum.countryCode, - patientOutSA: countryEnum.countryCode == '966' ? 0 : 1, - ), - onSuccess: (GenericApiModel response) { - _showOTPVerificationSheet( - context: context, - phoneNumber: phoneNumber, - loginTokenID: response.data, - onSuccess: onSuccess, - ); - }, - onError: (String errorMessage) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(errorMessage)) - ); - }, - ); + + + _showOTPVerificationSheet(context: context, formManager: formManager , onSuccess: onSuccess); + // + // LoaderBottomSheet.showLoader(); + } static void _showOTPVerificationSheet({ required BuildContext context, - required String phoneNumber, - required String loginTokenID, + required ReferralFormManager formManager, required Function onSuccess, }) { - showCommonBottomSheet( - context, - isFullScreen: true, - title: "OTP Verification", - isCloseButtonVisible: false, - height:ResponsiveExtension.screenHeight * 0.75, - child: OTPVerificationScreen( - phoneNumber: phoneNumber, - checkActivationCode: (int code) { - _verifyOTP( - context: context, - loginTokenID: loginTokenID, - code: code, - onSuccess: onSuccess, - ); - }, - onResendOTPPressed: (String phoneNumber) { - Navigator.pop(context); - openOTPScreen( - context: context, - phoneNumber: phoneNumber, - countryEnum: CountryEnum.saudiArabia, - onSuccess: onSuccess, - ); - }, - isFormFamilyFile: false, - ), - ); - } - static void _verifyOTP({ - required BuildContext context, - required String loginTokenID, - required int code, - required Function onSuccess, - }) { - final hmgServicesViewModel = context.read(); - - hmgServicesViewModel.checkEReferralActivationCode( - requestModel: CheckActivationCodeForEReferralRequestModel( - logInTokenID: loginTokenID, - activationCode: code.toString(), - ), - onSuccess: (GenericApiModel response) { - Navigator.pop(context); - onSuccess(); - }, - onError: (String errorMessage) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(errorMessage)) - ); - }, + showModalBottomSheet( + context: context, + isScrollControlled: true, + isDismissible: false, + useSafeArea: true, + backgroundColor: Colors.transparent, + builder: (bottomSheetContext) => Padding( + padding: EdgeInsets.only(bottom: MediaQuery.of(bottomSheetContext).viewInsets.bottom), + child: SingleChildScrollView( + child: GenericBottomSheet( + isEnableCountryDropdown:true, + textController: TextEditingController(), + onChange: (value) { + formManager.updateRequesterPhone(value ?? ''); + }, + onCountryChange: (value) { + formManager.updateCountryEnum(value); + }, + autoFocus: true, + buttons: [ + Padding( + padding: const EdgeInsets.only(bottom: 10), + child: CustomButton( + text: LocaleKeys.sendOTPSMS.tr(), + onPressed: () async { + + if (ValidationUtils.isValidatePhone( + phoneNumber: formManager.formData.requesterPhone, + onOkPress: () { + + Navigator.pop(context); + + + }, + )) { + Navigator.pop(context); + final hmgServicesViewModel = context.read(); + + LoaderBottomSheet.showLoader(); + hmgServicesViewModel.eReferralSendActivationCode( + requestModel: SendActivationCodeForEReferralRequestModel( + patientMobileNumber: int.parse(formManager.formData.requesterPhone), + zipCode: formManager.formData.countryEnum.countryCode, + patientOutSA: formManager.formData.countryEnum.countryCode == '966' ? 0 : 1, + ), + onSuccess: (GenericApiModel response) { + LoaderBottomSheet.hideLoader(); + hmgServicesViewModel.navigateToOTPScreen(otpTypeEnum: OTPTypeEnum.sms, phoneNumber: formManager.formData.requesterPhone, loginToken:response.data , onSuccess: (){ + Navigator.pop(context); + onSuccess(); + }); + + }, + onError: (String errorMessage) { + LoaderBottomSheet.hideLoader(); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(errorMessage)) + ); + }, + ); + + + } + }, + backgroundColor: AppColors.primaryRedColor, + borderColor: AppColors.primaryRedBorderColor, + textColor: AppColors.whiteColor, + icon: AppAssets.message, + ), + ), + + ], + ), + )), ); } -} \ No newline at end of file + + + + +} diff --git a/lib/presentation/e_referral/widget/e_referral_other_details.dart b/lib/presentation/e_referral/widget/e_referral_other_details.dart index d473dfe..cf8563d 100644 --- a/lib/presentation/e_referral/widget/e_referral_other_details.dart +++ b/lib/presentation/e_referral/widget/e_referral_other_details.dart @@ -1,8 +1,10 @@ // widgets/other_details_step.dart import 'dart:io'; - +import 'dart:convert'; import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_export.dart'; import 'package:hmg_patient_app_new/core/utils/validation_utils.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/create_e_referral_model.dart'; import 'package:hmg_patient_app_new/presentation/e_referral/e_referral_form_manager.dart'; @@ -55,6 +57,7 @@ class _OtherDetailsStepState extends State { _insuranceController.text = hasInsuranceDocs ? '${_formManager.formData.insuredPatientImages.length} file(s) selected' : ''; + } @override @@ -105,7 +108,7 @@ class _OtherDetailsStepState extends State { onTap: () { ImageOptions.showImageOptionsNew( context, - true, + false, (String image, File file) { _addMedicalReport(image, file, formManager); }, @@ -199,7 +202,7 @@ class _OtherDetailsStepState extends State { onTap: () { ImageOptions.showImageOptionsNew( context, - true, + false, (String image, File file) { _addInsuranceDocument(image, file, formManager); }, @@ -245,22 +248,28 @@ class _OtherDetailsStepState extends State { ); } - return ListView.separated( + return DecoratedBox( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: Colors.white, + customBorder: BorderRadius.all(Radius.circular(24.h)) , + + ), child: ListView.builder( shrinkWrap: true, + padding: EdgeInsets.all(16.h), physics: const BouncingScrollPhysics(), itemBuilder: (context, index) { final branch = hospitals[index]; return ListTile( - title: Text(branch.desciption ?? 'Unknown'), + title: (branch.desciption ?? 'Unknown').toText14(), onTap: () { formManager.updateBranch(branch); Navigator.pop(context); }, ); }, - separatorBuilder: (context, index) => const Divider(), + // separatorBuilder: (context, index) => const Divider(), itemCount: hospitals.length, - ); + )); }, ), useSafeArea: true, diff --git a/lib/presentation/e_referral/widget/e_referral_patient_info.dart b/lib/presentation/e_referral/widget/e_referral_patient_info.dart index 779804e..2bd8e92 100644 --- a/lib/presentation/e_referral/widget/e_referral_patient_info.dart +++ b/lib/presentation/e_referral/widget/e_referral_patient_info.dart @@ -1,6 +1,8 @@ // widgets/patient_information_step.dart import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_export.dart'; import 'package:hmg_patient_app_new/core/utils/validation_utils.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/presentation/e_referral/e_referral_form_manager.dart'; import 'package:provider/provider.dart'; @@ -160,7 +162,7 @@ class PatientInformationStepState extends State { formManager.updatePatientPhone(value ?? ''); }, onCountryChange: (value) { - formManager.updateCountryEnum(value); + formManager.updatePatientCountryEnum(value); }, prefix: '966', isBorderAllowed: false, @@ -192,50 +194,8 @@ class PatientInformationStepState extends State { }); } - // void _showCountryBottomSheet(BuildContext context, ReferralFormManager formManager) { - // final authVM = context.read(); - // - // showCommonBottomSheetWithoutHeight( - // context, - // title: "Select Country", - // child: Consumer( - // builder: (context, authVM, child) { - // final countries = authVM.countriesList; - // if (countries == null || countries.isEmpty) { - // return const Center( - // child: Padding( - // padding: EdgeInsets.all(16.0), - // child: Text('No countries available'), - // ), - // ); - // } - // - // return ListView.separated( - // shrinkWrap: true, - // physics: const BouncingScrollPhysics(), - // itemBuilder: (context, index) { - // final country = countries[index]; - // return ListTile( - // title: Text(country.name ?? 'Unknown'), - // onTap: () { - // formManager.updatePatientCountry(country); - // Navigator.pop(context); - // }, - // ); - // }, - // separatorBuilder: (context, index) => const Divider(), - // itemCount: countries.length, - // ); - // }, - // ), - // useSafeArea: true, - // isFullScreen: false, - // isCloseButtonVisible: true, - // ); - // } void _showCityBottomSheet(BuildContext context, ReferralFormManager formManager) { - final hmgServicesVM = context.read(); showCommonBottomSheetWithoutHeight( context, @@ -252,22 +212,28 @@ class PatientInformationStepState extends State { ); } - return ListView.separated( + return DecoratedBox( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: Colors.white, + customBorder: BorderRadius.all(Radius.circular(24.h)) , + + ), child: ListView.builder( shrinkWrap: true, + padding: EdgeInsets.all(16.h), physics: const BouncingScrollPhysics(), itemBuilder: (context, index) { final city = cities[index]; return ListTile( - title: Text(city.description ?? 'Unknown'), + title: (city.description ?? 'Unknown').toText14(), onTap: () { formManager.updatePatientCity(city); Navigator.pop(context); }, ); }, - separatorBuilder: (context, index) => const Divider(), + // separatorBuilder: (context, index) => const Divider(), itemCount: cities.length, - ); + )); }, ), useSafeArea: true, diff --git a/lib/presentation/e_referral/widget/e_referral_requester_form.dart b/lib/presentation/e_referral/widget/e_referral_requester_form.dart index b6952a5..5f9e4d8 100644 --- a/lib/presentation/e_referral/widget/e_referral_requester_form.dart +++ b/lib/presentation/e_referral/widget/e_referral_requester_form.dart @@ -1,6 +1,8 @@ // widgets/requester_form_step.dart import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_export.dart'; import 'package:hmg_patient_app_new/core/utils/validation_utils.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/ui_models/e_referral_form_model.dart'; import 'package:hmg_patient_app_new/presentation/e_referral/e-referral_validator.dart'; @@ -63,15 +65,17 @@ class RequesterFormStepState extends State { return Consumer( builder: (context, formManager, child) { return Padding( - padding: const EdgeInsets.symmetric(horizontal: 24.0), + padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 0.0), child: ListView( + padding: EdgeInsets.zero, + shrinkWrap: true, physics: const BouncingScrollPhysics(), children: [ - const SizedBox(height: 12), + // const SizedBox(height: 12), _buildSectionTitle('Referral requester information'), - const SizedBox(height: 12), + const SizedBox(height: 12), _buildNameField(formManager), - _buildPhoneField(formManager), + // _buildPhoneField(formManager), _buildRelationshipField(context, formManager), if (_showOtherNameField(formManager)) _buildOtherNameField(formManager), ], @@ -98,43 +102,43 @@ class RequesterFormStepState extends State { labelText: 'Requester Name', keyboardType: TextInputType.text, errorMessage: formManager.errors.requesterName, + isAllowLeadingIcon: true, + leadingIcon: AppAssets.user_circle, hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.requesterName), onChange: (value) { formManager.updateRequesterName(value ?? ''); }, - onSubmitted: (value) { - _phoneFocusNode.requestFocus(); - }, - ).paddingSymmetrical(0, 8), - ); - } - Widget _buildPhoneField(ReferralFormManager formManager) { - return Focus( - focusNode: _phoneFocusNode, - child: TextInputWidget( - labelText: 'Phone Number', - hintText: "5xxxxxxxx", - controller: _phoneController, - padding: const EdgeInsets.all(8), - keyboardType: TextInputType.number, - onChange: (value) { - formManager.updateRequesterPhone(value ?? ''); - }, - onCountryChange: (value) { - formManager.updateCountryEnum(value); - }, - prefix: '966', - isBorderAllowed: false, - isAllowLeadingIcon: true, - fontSize: 13, - isCountryDropDown: true, - leadingIcon: AppAssets.smart_phone, - errorMessage: formManager.errors.requesterPhone, - hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.requesterPhone) ).paddingSymmetrical(0, 8), ); } + // + // Widget _buildPhoneField(ReferralFormManager formManager) { + // return Focus( + // focusNode: _phoneFocusNode, + // child: TextInputWidget( + // labelText: 'Phone Number', + // hintText: "5xxxxxxxx", + // controller: _phoneController, + // padding: const EdgeInsets.all(8), + // keyboardType: TextInputType.number, + // onChange: (value) { + // formManager.updateRequesterPhone(value ?? ''); + // }, + // onCountryChange: (value) { + // formManager.updateCountryEnum(value); + // }, + // prefix: '966', + // isBorderAllowed: false, + // isAllowLeadingIcon: true, + // fontSize: 13, + // isCountryDropDown: true, + // leadingIcon: AppAssets.smart_phone, + // errorMessage: formManager.errors.requesterPhone, + // hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.requesterPhone) + // ).paddingSymmetrical(0, 8), + // ); + // } Widget _buildRelationshipField(BuildContext context, ReferralFormManager formManager) { return DropdownWidget( @@ -159,6 +163,7 @@ class RequesterFormStepState extends State { focusNode: _otherNameFocusNode, child: TextInputWidget( controller: _otherNameController, + keyboardType: TextInputType.text, padding: const EdgeInsets.symmetric(horizontal: 16.0), hintText: 'Other Name*', labelText: 'Other Name', @@ -192,22 +197,28 @@ class RequesterFormStepState extends State { ); } - return ListView.separated( + return DecoratedBox( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: Colors.white, + customBorder: BorderRadius.all(Radius.circular(24.h)) , + + ), child: ListView.builder( shrinkWrap: true, + padding: EdgeInsets.all(16.h), physics: const BouncingScrollPhysics(), itemBuilder: (context, index) { final relationship = hmgServicesVM.relationTypes[index]; return ListTile( - title: Text(relationship.textEn ?? 'Unknown'), + title:relationship.textEn?.toText14(), onTap: () { formManager.updateRelationship(relationship); Navigator.pop(context); }, ); }, - separatorBuilder: (context, index) => const Divider(), + //separatorBuilder: (context, index) => const Divider(), itemCount: hmgServicesVM.relationTypes.length, - ); + )); }, ), isFullScreen: false, diff --git a/lib/presentation/e_referral/widget/e_referral_stepper.dart b/lib/presentation/e_referral/widget/e_referral_stepper.dart deleted file mode 100644 index a6cd771..0000000 --- a/lib/presentation/e_referral/widget/e_referral_stepper.dart +++ /dev/null @@ -1,72 +0,0 @@ -// widgets/progress_stepper_widget.dart -import 'package:flutter/material.dart'; -import 'package:hmg_patient_app_new/theme/colors.dart'; - -class ProgressStepperWidget extends StatelessWidget { - final int currentStep; - final List steps; - - const ProgressStepperWidget({ - super.key, - required this.currentStep, - required this.steps, - }); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 24.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - for (int i = 0; i < steps.length; i++) - _buildStep( - title: steps[i], - active: i == currentStep, - showDivider: i < steps.length - 1, - ), - ], - ), - ); - } - - Widget _buildStep({required String title, required bool active, bool showDivider = true}) { - final Color activeColor = active ? AppColors.primaryRedColor : Colors.grey.shade400; - - return Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - CircleAvatar( - radius: 13, - backgroundColor: active ? activeColor : Colors.grey.shade300, - child: Icon(Icons.check, size: 14, color: Colors.white), - ), - if (showDivider) - Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: Divider(thickness: 1), - ), - ], - ), - const SizedBox(height: 6), - Text(title, style: const TextStyle(fontSize: 11, fontWeight: FontWeight.w600)), - const SizedBox(height: 6), - Container( - padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4), - decoration: BoxDecoration( - color: active ? activeColor.withOpacity(0.15) : Colors.grey.shade100, - borderRadius: BorderRadius.circular(6), - ), - child: Text( - active ? 'Active' : 'Inactive', - style: TextStyle(fontSize: 9, color: active ? activeColor : Colors.grey) - ), - ), - ], - ), - ); - } -} \ No newline at end of file diff --git a/lib/presentation/e_referral/widget/search_e_referral_form.dart b/lib/presentation/e_referral/widget/search_e_referral_form.dart index 37d4025..21bada6 100644 --- a/lib/presentation/e_referral/widget/search_e_referral_form.dart +++ b/lib/presentation/e_referral/widget/search_e_referral_form.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/utils/validation_utils.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; @@ -12,8 +11,10 @@ import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_mode import 'package:hmg_patient_app_new/widgets/dropdown/dropdown_widget.dart'; import 'package:hmg_patient_app_new/widgets/input_widget.dart'; import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; + class SearchEReferralFormForm extends StatefulWidget { - const SearchEReferralFormForm({super.key}); + final VoidCallback? onFormValidated; + const SearchEReferralFormForm({super.key, this.onFormValidated}); @override State createState() => SearchEReferralFormFormState(); @@ -22,35 +23,25 @@ class SearchEReferralFormForm extends StatefulWidget { class SearchEReferralFormFormState extends State { late TextEditingController _searchController; late TextEditingController _phoneController; - late FocusNode _searchFocusNode; + late FocusNode _searchFocusNode; late FocusNode _phoneFocusNode; - int criteria =0; - - List> criteriaList = [ - {0: 'Identification Number'}, - {1: 'Referral Number'}, - ]; - late ReferralFormManager _formManager; - @override void initState() { super.initState(); - _formManager = context.read(); _searchController = TextEditingController(); _phoneController = TextEditingController(); _searchFocusNode = FocusNode(); _phoneFocusNode = FocusNode(); - // Initialize controllers with current values - _searchController.text = ''; - _phoneController.text = ''; - - // Auto-focus the name field when the step loads + // Initialize controllers with current values from form manager WidgetsBinding.instance.addPostFrameCallback((_) { if (mounted) { - _searchFocusNode.requestFocus(); + final formManager = context.read(); + _searchController.text = formManager.searchValue ?? ''; + _phoneController.text = formManager.searchPhone ?? ''; + // _searchFocusNode.requestFocus(); } }); } @@ -62,14 +53,13 @@ class SearchEReferralFormFormState extends State { return Padding( padding: const EdgeInsets.symmetric(horizontal: 24.0), child: ListView( - physics: const BouncingScrollPhysics(), + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), children: [ - - const SizedBox(height: 12), + // const SizedBox(height: 12), _buildSelectionField(context, formManager), _buildNameField(formManager), _buildPhoneField(formManager), - ], ), ); @@ -77,24 +67,21 @@ class SearchEReferralFormFormState extends State { ); } - - Widget _buildNameField(ReferralFormManager formManager) { return Focus( focusNode: _searchFocusNode, + autofocus: true, child: TextInputWidget( controller: _searchController, padding: const EdgeInsets.symmetric(horizontal: 16.0), - hintText:criteria ==0 ? "Enter Identification Number" : "Enter Referral Number", - labelText: criteria ==0 ? "Identification Number" : "Referral Number", - keyboardType: TextInputType.text, - // errorMessage: formManager.errors.requesterName, - hasError: !ValidationUtils.isNullOrEmpty(_searchController.text), + hintText: formManager.searchCriteria == 0 ? "Enter Identification Number" : "Enter Referral Number", + labelText: formManager.searchCriteria == 0 ? "Identification Number" : "Referral Number", + keyboardType: TextInputType.number, + errorMessage: formManager.errors.searchValue, + hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.searchValue), onChange: (value) { - - }, - onSubmitted: (value) { - + formManager.updateSearchValue(value ?? ''); + //_validateForm(formManager); }, ).paddingSymmetrical(0, 8), ); @@ -103,17 +90,25 @@ class SearchEReferralFormFormState extends State { Widget _buildPhoneField(ReferralFormManager formManager) { return Focus( focusNode: _phoneFocusNode, + autofocus: false, child: TextInputWidget( + autoFocus: false, labelText: 'Phone Number', hintText: "5xxxxxxxx", controller: _phoneController, padding: const EdgeInsets.all(8), keyboardType: TextInputType.number, onChange: (value) { - formManager.updateRequesterPhone(value ?? ''); + formManager.updateSearchPhone(value ?? ''); + // _validateForm(formManager); }, + // onSubmitted: (value) { + // formManager.updateRequesterPhone(value ?? ''); + // _validateForm(formManager); + // }, onCountryChange: (value) { formManager.updateCountryEnum(value); + // _validateForm(formManager); }, prefix: '966', isBorderAllowed: false, @@ -121,8 +116,8 @@ class SearchEReferralFormFormState extends State { fontSize: 13, isCountryDropDown: true, leadingIcon: AppAssets.smart_phone, - errorMessage: formManager.errors.requesterPhone, - hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.requesterPhone) + errorMessage: formManager.errors.searchPhone, + hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.searchPhone), ).paddingSymmetrical(0, 8), ); } @@ -130,7 +125,7 @@ class SearchEReferralFormFormState extends State { Widget _buildSelectionField(BuildContext context, ReferralFormManager formManager) { return DropdownWidget( labelText: "Select the Search Criteria", - hintText: criteria ==0 ? "Identification Number" : "Referral Number", + hintText: formManager.searchCriteria == 0 ? "Identification Number" : "Referral Number", isEnable: false, hasSelectionCustomIcon: false, labelColor: Colors.black, @@ -138,38 +133,63 @@ class SearchEReferralFormFormState extends State { leadingIcon: AppAssets.search_icon, dropdownItems: [], ).paddingSymmetrical(0, 8).onPress(() { - _showCriteriaBottomSheet(context); + _showCriteriaBottomSheet(context, formManager); }); } - - - void _showCriteriaBottomSheet(BuildContext context,) { - + void _showCriteriaBottomSheet(BuildContext context, ReferralFormManager formManager) { + final criteriaList = [ + {0: 'Identification Number'}, + {1: 'Referral Number'}, + ]; showCommonBottomSheetWithoutHeight( context, title: "Select Criteria", child: ListView.separated( - shrinkWrap: true, - physics: const BouncingScrollPhysics(), - itemBuilder: (context, index) { - return ListTile( - title: Text(criteriaList[index].values.first), - onTap: () { - setState(() { - criteria = index; - }); + shrinkWrap: true, + physics: const BouncingScrollPhysics(), + itemBuilder: (context, index) { + final criteria = criteriaList[index]; + final criteriaKey = criteria.keys.first; + final criteriaValue = criteria.values.first; + + return ListTile( + leading: Radio( + value: criteriaKey, + groupValue: formManager.searchCriteria, + onChanged: (value) { + if (value != null) { + formManager.updateSearchCriteria(value); + _searchController.clear(); + _validateForm(formManager); Navigator.pop(context); - }, - ); + } + }, + ), + title: Text(criteriaValue), + onTap: () { + formManager.updateSearchCriteria(criteriaKey); + _searchController.clear(); + _validateForm(formManager); + Navigator.pop(context); }, - separatorBuilder: (context, index) => const Divider(), - itemCount: criteriaList.length, - ), + ); + }, + separatorBuilder: (context, index) => const Divider(), + itemCount: criteriaList.length, + ), isFullScreen: false, isCloseButtonVisible: true, - ); + ); + } + + void _validateForm(ReferralFormManager formManager) { + // Trigger validation + formManager.validateSearchForm(); + + // Notify parent if form validation state changes + widget.onFormValidated?.call(); } @override @@ -178,7 +198,6 @@ class SearchEReferralFormFormState extends State { _phoneController.dispose(); _searchFocusNode.dispose(); _phoneFocusNode.dispose(); - super.dispose(); } -} \ No newline at end of file +} diff --git a/lib/presentation/emergency_services/call_ambulance/widgets/pickup_location.dart b/lib/presentation/emergency_services/call_ambulance/widgets/pickup_location.dart index 9410af1..051c850 100644 --- a/lib/presentation/emergency_services/call_ambulance/widgets/pickup_location.dart +++ b/lib/presentation/emergency_services/call_ambulance/widgets/pickup_location.dart @@ -23,7 +23,7 @@ class PickupLocation extends StatelessWidget { children: [ "Select Pickup Direction" .needTranslation - .toText24(color: AppColors.textColor,isBold: true), + .toText24(color: AppColors.textColor, isBold: true), SizedBox( height: 16.h, ), @@ -34,128 +34,147 @@ class PickupLocation extends StatelessWidget { height: 12.h, ), Selector( - selector: (context, viewModel) => viewModel.callingPlace, - builder: (context, value, _) { - return Column( - spacing: 12.h, - children: [ - RadioGroup( - groupValue: value, - onChanged: (value) { - context - .read() - .updateCallingPlace(value); - }, - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - spacing: 24.h, - children: [ - Row( - children: [ - Radio( - value: AmbulanceCallingPlace.TO_HOSPITAL, - groupValue: value, - activeColor: AppColors.primaryRedColor, - - fillColor: MaterialStateProperty.all(AppColors.primaryRedColor), - ), - "To Hospital" - .needTranslation - .toText14(color: AppColors.textColor, weight: FontWeight.w500) - ], - ).onPress((){ - context - .read() - .updateCallingPlace(AmbulanceCallingPlace.TO_HOSPITAL); - }), - Row( - children: [ - Radio( - value: AmbulanceCallingPlace.FROM_HOSPITAL, - activeColor: AppColors.primaryRedColor, - - fillColor: MaterialStateProperty.all(AppColors.primaryRedColor), - ), - "From Hospital" - .needTranslation - .toText14(color: AppColors.textColor, weight: FontWeight.w500) - ], - ).onPress((){ - context - .read() - .updateCallingPlace(AmbulanceCallingPlace.FROM_HOSPITAL); - }), - ], + selector: (context, viewModel) => viewModel.callingPlace, + builder: (context, value, _) { + return Column( + children: [ + SizedBox(height: 12.h), + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Expanded( + child: Row( + children: [ + Radio( + value: AmbulanceCallingPlace.TO_HOSPITAL, + groupValue: value, + onChanged: (AmbulanceCallingPlace? newValue) { + if (newValue != null) { + context + .read() + .updateCallingPlace(newValue); + } + }, + activeColor: AppColors.primaryRedColor, + fillColor: MaterialStateProperty.all(AppColors.primaryRedColor), + ), + "To Hospital" + .needTranslation + .toText14(color: AppColors.textColor, weight: FontWeight.w500) + ], + ).onPress(() { + context + .read() + .updateCallingPlace(AmbulanceCallingPlace.TO_HOSPITAL); + }), ), - ), - Visibility( - visible: value == AmbulanceCallingPlace.TO_HOSPITAL, - child: Selector( - selector: (context, viewModel) => - viewModel.ambulanceDirection, - builder: (context, directionValue, _) { - return Column( - spacing: 12.h, - crossAxisAlignment: CrossAxisAlignment.start, + Expanded( + child: Row( + children: [ + Radio( + value: AmbulanceCallingPlace.FROM_HOSPITAL, + groupValue: value, + onChanged: (AmbulanceCallingPlace? newValue) { + if (newValue != null) { + context + .read() + .updateCallingPlace(newValue); + } + }, + activeColor: AppColors.primaryRedColor, + fillColor: MaterialStateProperty.all(AppColors.primaryRedColor), + ), + "From Hospital" + .needTranslation + .toText14(color: AppColors.textColor, weight: FontWeight.w500) + ], + ).onPress(() { + context + .read() + .updateCallingPlace(AmbulanceCallingPlace.FROM_HOSPITAL); + }), + ), + ], + ), + Visibility( + visible: value == AmbulanceCallingPlace.TO_HOSPITAL, + child: Selector( + selector: (context, viewModel) => viewModel.ambulanceDirection, + builder: (context, directionValue, _) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 16.h), + "Select Way" + .needTranslation + .toText16(color: AppColors.textColor, weight: FontWeight.w600), + SizedBox(height: 12.h), + Row( + mainAxisAlignment: MainAxisAlignment.start, children: [ - "Select Way" - .needTranslation - .toText16(color: AppColors.textColor, weight: FontWeight.w600), - RadioGroup( - groupValue: directionValue, - onChanged: (value) { + Expanded( + child: Row( + children: [ + Radio( + value: AmbulanceDirection.ONE_WAY, + groupValue: directionValue, + onChanged: (AmbulanceDirection? newValue) { + if (newValue != null) { + context + .read() + .updateDirection(newValue); + } + }, + activeColor: AppColors.primaryRedColor, + fillColor: MaterialStateProperty.all(AppColors.primaryRedColor), + ), + "One Way" + .needTranslation + .toText12(color: AppColors.textColor, fontWeight: FontWeight.w500) + ], + ).onPress(() { context .read() - .updateDirection(value); - }, + .updateDirection(AmbulanceDirection.ONE_WAY); + }), + ), + Expanded( child: Row( - mainAxisAlignment: MainAxisAlignment.start, - spacing: 24.h, children: [ - Row( - children: [ - Radio( - value: AmbulanceDirection.ONE_WAY, - activeColor: AppColors.primaryRedColor, - - fillColor: MaterialStateProperty.all(AppColors.primaryRedColor), - ), - "One Way" - .needTranslation - .toText12(color: AppColors.textColor, fontWeight: FontWeight.w500) - ], - ).onPress((){ - context - .read() - .updateDirection(AmbulanceDirection.ONE_WAY); - }), - Row( - children: [ - Radio( - value: AmbulanceDirection.TWO_WAY, - // activeColor: AppColors.primaryRedColor, - - fillColor: MaterialStateProperty.all(AppColors.primaryRedColor), - ), - "Two Way" - .needTranslation - .toText14(color: AppColors.textColor, weight: FontWeight.w500) - ], - ).onPress((){ - context - .read() - .updateDirection(AmbulanceDirection.TWO_WAY); - }), + Radio( + value: AmbulanceDirection.TWO_WAY, + groupValue: directionValue, + onChanged: (AmbulanceDirection? newValue) { + if (newValue != null) { + context + .read() + .updateDirection(newValue); + } + }, + activeColor: AppColors.primaryRedColor, + fillColor: MaterialStateProperty.all(AppColors.primaryRedColor), + ), + "Two Way" + .needTranslation + .toText14(color: AppColors.textColor, weight: FontWeight.w500) ], - ), + ).onPress(() { + context + .read() + .updateDirection(AmbulanceDirection.TWO_WAY); + }), ), ], - ); - }), - ) - ], - ); - }), + ), + ], + ); + }, + ), + ) + ], + ); + }, + ), SizedBox( height: 16.h, ), @@ -170,4 +189,4 @@ class PickupLocation extends StatelessWidget { ], ); } -} +} \ No newline at end of file diff --git a/lib/routes/app_routes.dart b/lib/routes/app_routes.dart index a0ee1e5..b37ebfa 100644 --- a/lib/routes/app_routes.dart +++ b/lib/routes/app_routes.dart @@ -3,7 +3,7 @@ import 'package:hmg_patient_app_new/presentation/authentication/login.dart'; import 'package:hmg_patient_app_new/presentation/authentication/register.dart'; import 'package:hmg_patient_app_new/presentation/authentication/register_step2.dart'; import 'package:hmg_patient_app_new/presentation/comprehensive_checkup/comprehensive_checkup_page.dart'; -import 'package:hmg_patient_app_new/presentation/e_referral/e_referral_page_home.dart'; +import 'package:hmg_patient_app_new/presentation/e_referral/new_e_referral.dart'; import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart'; import 'package:hmg_patient_app_new/presentation/home_health_care/hhc_procedures_page.dart'; import 'package:hmg_patient_app_new/presentation/medical_file/medical_file_page.dart'; @@ -27,7 +27,7 @@ class AppRoutes { register: (context) => RegisterNew(), registerStepTwo: (context) => RegisterNewStep2(), medicalFilePage: (context) => MedicalFilePage(), - eReferralPage: (context) => EReferralPage(), + eReferralPage: (context) => NewReferralPage(), comprehensiveCheckupPage: (context) => ComprehensiveCheckupPage(), homeHealthCarePage: (context) => HhcProceduresPage() }; diff --git a/lib/widgets/image_picker.dart b/lib/widgets/image_picker.dart index 9444159..e25b117 100644 --- a/lib/widgets/image_picker.dart +++ b/lib/widgets/image_picker.dart @@ -147,6 +147,7 @@ void cameraImageAndroid(Function(String, File) image) async { if (base64Encode != null) { image(base64Encode, _image); } + } class _BottomSheet extends StatelessWidget { diff --git a/lib/widgets/order_tracking/request_tracking.dart b/lib/widgets/order_tracking/request_tracking.dart index c0847f9..f07c0b1 100644 --- a/lib/widgets/order_tracking/request_tracking.dart +++ b/lib/widgets/order_tracking/request_tracking.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/order_tracking/order_tracking_state.dart'; +import 'package:hmg_patient_app_new/widgets/stepper/stepper_widget.dart'; class OrderTrackingWidget extends StatelessWidget { final double? height; @@ -35,9 +36,9 @@ class OrderTrackingWidget extends StatelessWidget { return Row( children: List.generate(4, (index) { if (index == 0) { - return oneProgressBar(widthOfOneState, AppColors.primaryRedColor, true); + return StepperWidget(widthOfOneState, AppColors.primaryRedColor, true, height); } else { - return oneProgressBar(widthOfOneState, AppColors.greyLightColor, false); + return StepperWidget(widthOfOneState, AppColors.greyLightColor, false, height); } })); } @@ -46,9 +47,9 @@ class OrderTrackingWidget extends StatelessWidget { return Row( children: List.generate(4, (index){ if(index == 0) { - return oneProgressBar(widthOfOneState, AppColors.primaryRedColor, true); + return StepperWidget(widthOfOneState, AppColors.primaryRedColor, true, height); } else { - return oneProgressBar(widthOfOneState, AppColors.greyLightColor, false); + return StepperWidget(widthOfOneState, AppColors.greyLightColor, false, height); } }) ); @@ -57,9 +58,9 @@ class OrderTrackingWidget extends StatelessWidget { return Row( children: List.generate(4, (index){ if(index == 1 || index == 0) { - return oneProgressBar(widthOfOneState, AppColors.primaryRedColor, index == 1 ); + return StepperWidget(widthOfOneState, AppColors.primaryRedColor, index == 1, height ); } else { - return oneProgressBar(widthOfOneState, AppColors.greyLightColor, false); + return StepperWidget(widthOfOneState, AppColors.greyLightColor, false, height); } }) ); @@ -67,9 +68,9 @@ class OrderTrackingWidget extends StatelessWidget { return Row( children: List.generate(4, (index){ if(index == 2 || index == 1 || index == 0) { - return oneProgressBar(widthOfOneState, AppColors.primaryRedColor, index == 2); + return StepperWidget(widthOfOneState, AppColors.primaryRedColor, index == 2, height); } else { - return oneProgressBar(widthOfOneState, AppColors.greyLightColor, false); + return StepperWidget(widthOfOneState, AppColors.greyLightColor, false, height); } }) ); @@ -77,7 +78,7 @@ class OrderTrackingWidget extends StatelessWidget { return Row( children: List.generate(4, (index){ // if(index == 3) { - return oneProgressBar(widthOfOneState, AppColors.successLightColor, index == 3); + return StepperWidget(widthOfOneState, AppColors.successLightColor, index == 3, height); // } else { // return oneProgressBar(widthOfOneState, AppColors.greyLightColor, false); // } @@ -87,7 +88,7 @@ class OrderTrackingWidget extends StatelessWidget { return Row( children: List.generate(4, (index){ // if(index == 3) { - return oneProgressBar(widthOfOneState, AppColors.errorColor, index == 3); + return StepperWidget(widthOfOneState, AppColors.errorColor, index == 3, height); // } else { // return oneProgressBar(widthOfOneState, AppColors.greyLightColor, false); // } @@ -97,7 +98,7 @@ class OrderTrackingWidget extends StatelessWidget { return Row( children: List.generate(4, (index){ // if(index == 3) { - return oneProgressBar(widthOfOneState, AppColors.errorColor, index == 3); + return StepperWidget(widthOfOneState, AppColors.errorColor, index == 3, height); // } else { // return oneProgressBar(widthOfOneState, AppColors.greyLightColor, false); // } @@ -106,58 +107,6 @@ class OrderTrackingWidget extends StatelessWidget { } } - Widget oneProgressBar(double width, Color color, bool hasThumb) { - return Row( - children: [ - AnimatedSize( - duration: const Duration(seconds:1), - child: SizedBox( - height: 28.h, - width: width, - child: Stack( - clipBehavior: Clip.none, - children: [ - SizedBox( - height: 28.h, - child: Container( - width: width, - height: height, - decoration: BoxDecoration( - color: color, - shape: BoxShape.rectangle, - borderRadius: BorderRadius.circular(30.h) - ), - ), - ), - Visibility( - visible: hasThumb, - child: Positioned( - top: -6.h, // move thumb above bar center - left: width - 22.h, // move to right end - child: thumb(color), - ), - ) - ], - ), - ), - ), - SizedBox(width: 8.h) - ], - ); - } - - Widget thumb(Color color) { - return Container( - width: 18.h, - height: 18.h, - padding: EdgeInsets.zero, - decoration: BoxDecoration( - color: color, - shape: BoxShape.circle, - border: Border.all(color: Colors.white, width: 2.h) - ), - ); - } } \ No newline at end of file diff --git a/lib/widgets/stepper/stepper_widget.dart b/lib/widgets/stepper/stepper_widget.dart new file mode 100644 index 0000000..5d901ac --- /dev/null +++ b/lib/widgets/stepper/stepper_widget.dart @@ -0,0 +1,71 @@ +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_export.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; + +class StepperWidget extends StatelessWidget { + + double width = 80.w; + Color activeColor = AppColors.primaryRedColor; + bool hasThumb = true; + double? height = 4.h; + StepperWidget( this.width, this.activeColor, this.hasThumb, this.height, {super.key}); + + @override + Widget build(BuildContext context) { + return oneProgressBar(width, activeColor, hasThumb); + } + + Widget oneProgressBar(double width, Color color, bool hasThumb) { + return Row( + children: [ + AnimatedSize( + duration: const Duration(seconds: 1), + child: SizedBox( + height: 28.h, + width: width, + child: Stack( + clipBehavior: Clip.none, + children: [ + SizedBox( + height: height, + child: Container( + width: width, + height: height, + decoration: BoxDecoration( + color: color, + shape: BoxShape.rectangle, + borderRadius: BorderRadius.circular(30.h) + ), + ), + ), + + Visibility( + visible: hasThumb, + child: Positioned( + top: -6.h, // move thumb above bar center + left: width - 22.h, // move to right end + child: thumb(color), + ), + ) + ], + ), + ), + ), + SizedBox(width: 8.h) + ], + ); + } + + Widget thumb(Color color) { + return Container( + width: 18.h, + height: 18.h, + padding: EdgeInsets.zero, + decoration: BoxDecoration( + color: color, + shape: BoxShape.circle, + border: Border.all(color: Colors.white, width: 2.h) + ), + ); + } +} \ No newline at end of file From 2532b494f7d94856b8d5e094a3ad598b18172657 Mon Sep 17 00:00:00 2001 From: Sultan khan Date: Tue, 25 Nov 2025 14:43:51 +0300 Subject: [PATCH 4/5] e-referral done. --- .../e_referral/e-referral_validator.dart | 28 ++-- .../e_referral/e_referral_search_result.dart | 34 +---- .../e_referral/new_e_referral.dart | 1 - .../e_referral/search_e_referral.dart | 1 - .../e_referral/widget/e-referral_otp.dart | 137 ++++++++---------- .../widget/e_referral_other_details.dart | 32 ++-- .../widget/e_referral_patient_info.dart | 38 ++--- .../widget/e_referral_requester_form.dart | 53 ++----- .../widget/search_e_referral_form.dart | 4 - 9 files changed, 107 insertions(+), 221 deletions(-) diff --git a/lib/presentation/e_referral/e-referral_validator.dart b/lib/presentation/e_referral/e-referral_validator.dart index 3e3399d..e0a8006 100644 --- a/lib/presentation/e_referral/e-referral_validator.dart +++ b/lib/presentation/e_referral/e-referral_validator.dart @@ -1,4 +1,3 @@ -// utils/referral_validator.dart 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'; @@ -7,24 +6,19 @@ class ReferralValidator { final errors = FormValidationErrors(); if (formData.requesterName.trim().isEmpty) { - errors.requesterName = 'Referral requester name is required'; + errors.requesterName = 'Referral requester name is required'.needTranslation; } - // if (formData.requesterPhone.trim().isEmpty) { - // errors.requesterPhone = 'Phone number is required'; - // } else if (formData.countryEnum.countryCode == '966' && - // !_isValidSaudiPhone(formData.requesterPhone)) { - // errors.requesterPhone = 'Please enter a valid Saudi phone number (5xxxxxxxx)'; - // } + if (formData.relationship == null) { - errors.relationship = 'Please select a relationship'; + 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'; + errors.otherRelationshipName = 'Other relationship name is required'.needTranslation; } return errors; @@ -34,19 +28,19 @@ class ReferralValidator { final errors = FormValidationErrors(); if (formData.patientIdentification.trim().isEmpty) { - errors.patientIdentification = 'Identification number is required'; + errors.patientIdentification = 'Identification number is required'.needTranslation; } if (formData.patientName.trim().isEmpty) { - errors.patientName = 'Patient name is required'; + errors.patientName = 'Patient name is required'.needTranslation; } if (formData.patientPhone == null) { - errors.patientPhone = 'Please Enter patient phone number'; + errors.patientPhone = 'Please Enter patient phone number'.needTranslation; } if (formData.patientCity == null) { - errors.patientCity = 'Please select patient city'; + errors.patientCity = 'Please select patient city'.needTranslation; } return errors; @@ -56,15 +50,15 @@ class ReferralValidator { final errors = FormValidationErrors(); if (formData.medicalReportImages.isEmpty) { - errors.medicalReport = 'At least one medical report is required'; + errors.medicalReport = 'At least one medical report is required'.needTranslation; } if (formData.branch == null) { - errors.branch = 'Please select a branch'; + errors.branch = 'Please select a branch'.needTranslation; } if (formData.isPatientInsured && formData.insuredPatientImages.isEmpty) { - errors.insuredDocument = 'Insurance document is required for insured patients'; + errors.insuredDocument = 'Insurance document is required for insured patients'.needTranslation; } return errors; diff --git a/lib/presentation/e_referral/e_referral_search_result.dart b/lib/presentation/e_referral/e_referral_search_result.dart index 3f3966e..ba31f1b 100644 --- a/lib/presentation/e_referral/e_referral_search_result.dart +++ b/lib/presentation/e_referral/e_referral_search_result.dart @@ -241,12 +241,9 @@ class _SearchResultPageState extends State { size: 16, ), SizedBox(width: 4), - // Text( + text.toText14(color: AppColors.greyTextColor), - // style: TextStyle( - // color: Colors.grey[700], - // ), - // ), + ], ); } @@ -264,33 +261,6 @@ class _SearchResultPageState extends State { } } - void _showFilterOptions() { - showModalBottomSheet( - context: context, - builder: (context) { - return Container( - padding: EdgeInsets.all(16), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - 'Filter Results', - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - ), - ), - SizedBox(height: 16), - _buildFilterOption('All'), - _buildFilterOption('Pending'), - _buildFilterOption('Completed'), - _buildFilterOption('Rejected'), - ], - ), - ); - }, - ); - } Widget _buildFilterOption(String filter) { return ListTile( diff --git a/lib/presentation/e_referral/new_e_referral.dart b/lib/presentation/e_referral/new_e_referral.dart index e48d8ca..c736216 100644 --- a/lib/presentation/e_referral/new_e_referral.dart +++ b/lib/presentation/e_referral/new_e_referral.dart @@ -43,7 +43,6 @@ class _NewReferralPageState extends State { double widthOfOneState = ((ResponsiveExtension.screenWidth) / 3) - (20.h); - final List _steps = ['Requester Info', 'Patient Information', 'Other details']; @override void initState() { diff --git a/lib/presentation/e_referral/search_e_referral.dart b/lib/presentation/e_referral/search_e_referral.dart index ea749ee..1a243da 100644 --- a/lib/presentation/e_referral/search_e_referral.dart +++ b/lib/presentation/e_referral/search_e_referral.dart @@ -26,7 +26,6 @@ class SearchEReferralPage extends StatefulWidget { } class _SearchEReferralPageState extends State { - // final PageController _pageController = PageController(); final ReferralFormManager _formManager = ReferralFormManager(); late HmgServicesViewModel hmgServicesVM; @override diff --git a/lib/presentation/e_referral/widget/e-referral_otp.dart b/lib/presentation/e_referral/widget/e-referral_otp.dart index c3f5872..390fc71 100644 --- a/lib/presentation/e_referral/widget/e-referral_otp.dart +++ b/lib/presentation/e_referral/widget/e-referral_otp.dart @@ -1,9 +1,7 @@ -// services/otp_service.dart import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/enums.dart'; -import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/validation_utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; @@ -15,10 +13,7 @@ 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/check_activation_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/widgets/common_bottom_sheet.dart'; -import 'package:hmg_patient_app_new/features/authentication/widgets/otp_verification_screen.dart'; class OTPService { static void openOTPScreen({ @@ -26,12 +21,7 @@ class OTPService { required ReferralFormManager formManager, required Function onSuccess, }) { - - - _showOTPVerificationSheet(context: context, formManager: formManager , onSuccess: onSuccess); - // - // LoaderBottomSheet.showLoader(); - + _showOTPVerificationSheet(context: context, formManager: formManager, onSuccess: onSuccess); } static void _showOTPVerificationSheet({ @@ -39,7 +29,6 @@ class OTPService { required ReferralFormManager formManager, required Function onSuccess, }) { - showModalBottomSheet( context: context, isScrollControlled: true, @@ -49,75 +38,65 @@ class OTPService { 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)) - ); - }, - ); - - - } + 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); }, - backgroundColor: AppColors.primaryRedColor, - borderColor: AppColors.primaryRedBorderColor, - textColor: AppColors.whiteColor, - icon: AppAssets.message, - ), - ), - - ], + )) { + 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 index cf8563d..fd1649f 100644 --- a/lib/presentation/e_referral/widget/e_referral_other_details.dart +++ b/lib/presentation/e_referral/widget/e_referral_other_details.dart @@ -1,4 +1,3 @@ -// widgets/other_details_step.dart import 'dart:io'; import 'dart:convert'; import 'package:flutter/material.dart'; @@ -48,14 +47,14 @@ class _OtherDetailsStepState extends State { void _updateMedicalReportText() { final hasMedicalReports = _formManager.formData.medicalReportImages.isNotEmpty; _medicalReportController.text = hasMedicalReports - ? '${_formManager.formData.medicalReportImages.length} file(s) selected' + ? '${_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' + ? '${_formManager.formData.insuredPatientImages.length} file(s) selected'.needTranslation : ''; } @@ -70,7 +69,7 @@ class _OtherDetailsStepState extends State { physics: const BouncingScrollPhysics(), children: [ const SizedBox(height: 12), - _buildSectionTitle('Other Details'), + _buildSectionTitle('Other Details'.needTranslation), const SizedBox(height: 12), _buildMedicalReportField(formManager), _buildBranchField(context, formManager), @@ -98,8 +97,8 @@ class _OtherDetailsStepState extends State { child: TextInputWidget( controller: _medicalReportController, padding: const EdgeInsets.symmetric(horizontal: 16.0), - hintText: 'Medical Report', - labelText: 'Select Attachment', + hintText: 'Medical Report'.needTranslation, + labelText: 'Select Attachment'.needTranslation, suffix: const Icon(Icons.attachment), isReadOnly: true, errorMessage: formManager.errors.medicalReport, @@ -123,7 +122,7 @@ class _OtherDetailsStepState extends State { children: formManager.formData.medicalReportImages.asMap().entries.map((entry) { final index = entry.key; return Chip( - label: Text('Medical Report ${index + 1}'), + label: Text('Medical Report ${index + 1}'.needTranslation), deleteIcon: const Icon(Icons.close, size: 16), onDeleted: () { _removeMedicalReport(index, formManager); @@ -171,12 +170,15 @@ class _OtherDetailsStepState extends State { } }, ), - const Padding( + Padding( padding: EdgeInsets.all(5.0), - child: Text( - "Patient is Insured", - style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600), - ), + child: + "Patient is Insured".needTranslation.toText14( + color: Colors.black, + weight: FontWeight.w600, + ), + // style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600), + //), ), ], ), @@ -192,8 +194,8 @@ class _OtherDetailsStepState extends State { child: TextInputWidget( controller: _insuranceController, padding: const EdgeInsets.symmetric(horizontal: 16.0), - hintText: 'Insurance Document', - labelText: 'Select Attachment', + hintText: 'Insurance Document'.needTranslation, + labelText: 'Select Attachment'.needTranslation, suffix: const Icon(Icons.attachment), isReadOnly: true, errorMessage: formManager.errors.insuredDocument, @@ -235,7 +237,7 @@ class _OtherDetailsStepState extends State { showCommonBottomSheetWithoutHeight( context, - title: "Select Branch", + title: "Select Branch".needTranslation, child: Consumer( builder: (context, habibWalletVM, child) { final hospitals = habibWalletVM.advancePaymentHospitals; diff --git a/lib/presentation/e_referral/widget/e_referral_patient_info.dart b/lib/presentation/e_referral/widget/e_referral_patient_info.dart index 2bd8e92..469755c 100644 --- a/lib/presentation/e_referral/widget/e_referral_patient_info.dart +++ b/lib/presentation/e_referral/widget/e_referral_patient_info.dart @@ -1,4 +1,3 @@ -// widgets/patient_information_step.dart import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_export.dart'; import 'package:hmg_patient_app_new/core/utils/validation_utils.dart'; @@ -7,7 +6,6 @@ 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/authentication/authentication_view_model.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'; @@ -67,14 +65,14 @@ class PatientInformationStepState extends State { physics: const BouncingScrollPhysics(), children: [ const SizedBox(height: 12), - _buildSectionTitle('Patient information'), + _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'), + _buildSectionTitle('Where the patient located'.needTranslation), _buildPatientCityField(context, formManager), ], ), @@ -96,8 +94,8 @@ class PatientInformationStepState extends State { child: TextInputWidget( controller: _identificationController, padding: const EdgeInsets.symmetric(horizontal: 16.0), - hintText: 'Enter Identification Number*', - labelText: 'Identification Number', + hintText: 'Enter Identification Number*'.needTranslation, + labelText: 'Identification Number'.needTranslation, errorMessage: formManager.errors.patientIdentification, hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.patientIdentification), onChange: (value) { @@ -116,8 +114,8 @@ class PatientInformationStepState extends State { child: TextInputWidget( controller: _nameController, padding: const EdgeInsets.symmetric(horizontal: 16.0), - hintText: 'Patient Name*', - labelText: 'Name', + hintText: 'Patient Name*'.needTranslation, + labelText: 'Name'.needTranslation, keyboardType: TextInputType.text, errorMessage: formManager.errors.patientName, hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.patientName), @@ -131,29 +129,11 @@ class PatientInformationStepState extends State { ); } - // Widget _buildPatientCountryField(BuildContext context, ReferralFormManager formManager) { - // return DropdownWidget( - // labelText: 'Country', - // hintText: formManager.formData.patientCountry?.name ?? "Select Country", - // 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.patientCountry, - // hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.patientCountry), - // ).paddingSymmetrical(0, 4).onPress(() { - // _showCountryBottomSheet(context, formManager); - // }); - // } - Widget _buildPatientPhoneField(ReferralFormManager formManager) { return Focus( focusNode: _phoneFocusNode, child: TextInputWidget( - labelText: 'Phone Number', + labelText: 'Phone Number'.needTranslation, hintText: "5xxxxxxxx", controller: _phoneController, padding: const EdgeInsets.all(8), @@ -179,7 +159,7 @@ class PatientInformationStepState extends State { Widget _buildPatientCityField(BuildContext context, ReferralFormManager formManager) { return DropdownWidget( labelText: 'City', - hintText: formManager.formData.patientCity?.description ?? "Select City", + hintText: formManager.formData.patientCity?.description ?? "Select City".needTranslation, isEnable: false, hasSelectionCustomIcon: true, labelColor: Colors.black, @@ -199,7 +179,7 @@ class PatientInformationStepState extends State { showCommonBottomSheetWithoutHeight( context, - title: "Select City", + title: "Select City".needTranslation, child: Consumer( builder: (context, hmgServicesVM, child) { final cities = hmgServicesVM.getAllCitiesList; diff --git a/lib/presentation/e_referral/widget/e_referral_requester_form.dart b/lib/presentation/e_referral/widget/e_referral_requester_form.dart index 5f9e4d8..cc981a9 100644 --- a/lib/presentation/e_referral/widget/e_referral_requester_form.dart +++ b/lib/presentation/e_referral/widget/e_referral_requester_form.dart @@ -1,21 +1,16 @@ -// widgets/requester_form_step.dart import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_export.dart'; import 'package:hmg_patient_app_new/core/utils/validation_utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; -import 'package:hmg_patient_app_new/features/hmg_services/models/ui_models/e_referral_form_model.dart'; -import 'package:hmg_patient_app_new/presentation/e_referral/e-referral_validator.dart'; 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/authentication/authentication_view_model.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'; -// widgets/requester_form_step.dart -// widgets/requester_form_step.dart + class RequesterFormStep extends StatefulWidget { const RequesterFormStep({super.key}); @@ -72,7 +67,7 @@ class RequesterFormStepState extends State { physics: const BouncingScrollPhysics(), children: [ // const SizedBox(height: 12), - _buildSectionTitle('Referral requester information'), + _buildSectionTitle('Referral requester information'.needTranslation), const SizedBox(height: 12), _buildNameField(formManager), // _buildPhoneField(formManager), @@ -98,8 +93,8 @@ class RequesterFormStepState extends State { child: TextInputWidget( controller: _nameController, padding: const EdgeInsets.symmetric(horizontal: 16.0), - hintText: 'Enter Referral Requester Name*', - labelText: 'Requester Name', + hintText: 'Enter Referral Requester Name*'.needTranslation, + labelText: 'Requester Name'.needTranslation, keyboardType: TextInputType.text, errorMessage: formManager.errors.requesterName, isAllowLeadingIcon: true, @@ -112,40 +107,12 @@ class RequesterFormStepState extends State { ).paddingSymmetrical(0, 8), ); } - // - // Widget _buildPhoneField(ReferralFormManager formManager) { - // return Focus( - // focusNode: _phoneFocusNode, - // child: TextInputWidget( - // labelText: 'Phone Number', - // hintText: "5xxxxxxxx", - // controller: _phoneController, - // padding: const EdgeInsets.all(8), - // keyboardType: TextInputType.number, - // onChange: (value) { - // formManager.updateRequesterPhone(value ?? ''); - // }, - // onCountryChange: (value) { - // formManager.updateCountryEnum(value); - // }, - // prefix: '966', - // isBorderAllowed: false, - // isAllowLeadingIcon: true, - // fontSize: 13, - // isCountryDropDown: true, - // leadingIcon: AppAssets.smart_phone, - // errorMessage: formManager.errors.requesterPhone, - // hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.requesterPhone) - // ).paddingSymmetrical(0, 8), - // ); - // } - Widget _buildRelationshipField(BuildContext context, ReferralFormManager formManager) { return DropdownWidget( - labelText: "Relationship", - hintText: formManager.formData.relationship?.textEn ?? "Select Relation", + labelText: "Relationship".needTranslation, + hintText: formManager.formData.relationship?.textEn ?? "Select Relation".needTranslation, isEnable: false, - selectedValue: formManager.formData.relationship?.textEn ?? "Select Relation", + selectedValue: formManager.formData.relationship?.textEn ?? "Select Relation".needTranslation, errorMessage: formManager.errors.relationship, hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.relationship), hasSelectionCustomIcon: false, @@ -165,8 +132,8 @@ class RequesterFormStepState extends State { controller: _otherNameController, keyboardType: TextInputType.text, padding: const EdgeInsets.symmetric(horizontal: 16.0), - hintText: 'Other Name*', - labelText: 'Other Name', + hintText: 'Other Name*'.needTranslation, + labelText: 'Other Name'.needTranslation, errorMessage: formManager.errors.otherRelationshipName, onChange: (value) { formManager.updateOtherRelationshipName(value ?? ''); @@ -185,7 +152,7 @@ class RequesterFormStepState extends State { showCommonBottomSheetWithoutHeight( context, - title: "Select Relation", + title: "Select Relation".needTranslation, child: Consumer( builder: (context, hmgServicesVM, child) { if (hmgServicesVM.relationTypes.isEmpty) { diff --git a/lib/presentation/e_referral/widget/search_e_referral_form.dart b/lib/presentation/e_referral/widget/search_e_referral_form.dart index 21bada6..fa3ae19 100644 --- a/lib/presentation/e_referral/widget/search_e_referral_form.dart +++ b/lib/presentation/e_referral/widget/search_e_referral_form.dart @@ -1,13 +1,9 @@ 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/features/hmg_services/models/ui_models/e_referral_form_model.dart'; -import 'package:hmg_patient_app_new/presentation/e_referral/e-referral_validator.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/authentication/authentication_view_model.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'; From 9e0336687cb5f7be15bfb4e73f7bd557573a1348 Mon Sep 17 00:00:00 2001 From: Sultan khan Date: Tue, 25 Nov 2025 17:07:16 +0300 Subject: [PATCH 5/5] virtual tour. --- lib/core/utils/utils.dart | 6 ++++ .../hmg_services_component_model.dart | 9 ++++-- .../hmg_services/services_page.dart | 28 +++++++++++++++---- .../hmg_services/services_view.dart | 13 +++++++-- lib/presentation/home/navigation_screen.dart | 2 +- 5 files changed, 46 insertions(+), 12 deletions(-) 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/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/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(