diff --git a/assets/images/png/default_MEP.png b/assets/images/png/default_MEP.png new file mode 100644 index 00000000..d79b58d2 Binary files /dev/null and b/assets/images/png/default_MEP.png differ diff --git a/assets/images/svg/my_radiology_icon.svg b/assets/images/svg/my_radiology_icon.svg index 7b5ebe46..1848607f 100644 --- a/assets/images/svg/my_radiology_icon.svg +++ b/assets/images/svg/my_radiology_icon.svg @@ -1,3 +1,4 @@ - - + + + diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 4ac9878c..08adc616 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -301,6 +301,12 @@ "medicinesSubtitle": "الوصفات", "vitalSigns": "علامات حيوية", "vitalSignsSubTitle": "التقارير", + "vitalSignNormal": "طبيعي", + "vitalSignLow": "منخفض", + "vitalSignHigh": "مرتفع", + "vitalSignObese": "بدين", + "vitalSignOverweight": "زيادة الوزن", + "vitalSignUnderweight": "نقص الوزن", "myMedical": "نشط", "myMedicalSubtitle": "الأدوية", "myDoctor": "أطبائي", @@ -717,6 +723,11 @@ "bloodDonation": "التبرع بالدم", "bloodDonationInfo": "من خلال هذه الخدمة، يمكنك تسجيل اسمك كمتبرع بالدم حيث سيتواصل معك بنك الدم في مجموعة الحبيب الطبية في حالة الحاجة إلى فصيلة الدم.", "bloodInstruction": "أدخل المعلومات المطلوبة، من أجل التسجيل في خدمة التبرع بالدم", + "aboutBloodDonation": "عن التبرع بالدم", + "bloodDonationDescription": "التبرع بالدم هو عمل نبيل ينقذ الأرواح. كل تبرع واحد يمكن أن ينقذ ما يصل إلى ثلاثة أشخاص. من خلال التبرع بالدم، فإنك تساعد المرضى الذين يحتاجون إلى عمليات نقل دم، وحالات الطوارئ، والعلاجات الطبية.", + "bloodDonationBenefit1": "عملية آمنة وسريعة (10-15 دقيقة)", + "bloodDonationBenefit2": "يمكنك التبرع كل 8-12 أسبوع", + "bloodDonationBenefit3": "فحص صحي مجاني لدمك", "viewTerms": "لعرض الشروط والأحكام", "wantConnectHmgNetwork": "عزيزي العميل، لا يوجد وصول إلى الإنترنت، هل ترغب في الاتصال بشبكة مجموعة الحبيب الطبية لاستخدام تطبيقنا، تأكد من أنك في نطاق شبكة مجموعة الحبيب الطبية", "failedToAccessHmgServices": "متصل بشبكة مجموعة الحبيب الطبية،\n\nلكن فشل في الوصول إلى خدمات مجموعة الحبيب الطبية", diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 84d51a6c..18484fc1 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -300,6 +300,12 @@ "medicinesSubtitle": "Prescriptions", "vitalSigns": "Vital Signs", "vitalSignsSubTitle": "Reports", + "vitalSignNormal": "Normal", + "vitalSignLow": "Low", + "vitalSignHigh": "High", + "vitalSignObese": "Obese", + "vitalSignOverweight": "Overweight", + "vitalSignUnderweight": "Underweight", "myMedical": "Active", "myMedicalSubtitle": "Medications", "myDoctor": "My Doctors", @@ -710,6 +716,11 @@ "bloodDonation": "Blood Donation", "bloodDonationInfo": "Through this service, you can register your name as a blood donor where the blood bank in the Habib Medical Group will communicate you in case of need for blood type.", "bloodInstruction": "Enter the required information, in order to register for Blood Donation Service", + "aboutBloodDonation": "About Blood Donation", + "bloodDonationDescription": "Blood donation is a noble act that saves lives. Every single donation can save up to three people. By donating blood, you help patients in need of transfusions, emergencies, and medical treatments.", + "bloodDonationBenefit1": "Safe and quick process (10-15 minutes)", + "bloodDonationBenefit2": "You can donate every 8-12 weeks", + "bloodDonationBenefit3": "Free health check of your blood", "viewTerms": "To view the terms and conditions", "wantConnectHmgNetwork": "Dear customer there is no internet access, do you want to connect with HMG network to use our app, make sure you are in range of HMG network", "failedToAccessHmgServices": "Connected with HMG Network,\n\nBut failed to access HMG services", diff --git a/lib/core/app_assets.dart b/lib/core/app_assets.dart index 8c57598e..44ca3b10 100644 --- a/lib/core/app_assets.dart +++ b/lib/core/app_assets.dart @@ -400,6 +400,7 @@ class AppAssets { static const String fullBodyFrontFemale = '$pngBasePath/full_body_front_female.png'; static const String fullBodyBackFemale = '$pngBasePath/full_body_back_female.png'; static const String bmiFullBody = '$pngBasePath/bmi_image_1.png'; + static const String defaultMEP = '$pngBasePath/default_MEP.png'; } class AppAnimations { diff --git a/lib/core/utils/utils.dart b/lib/core/utils/utils.dart index ac7af39d..bc4b82c0 100644 --- a/lib/core/utils/utils.dart +++ b/lib/core/utils/utils.dart @@ -923,7 +923,8 @@ class Utils { ), TextSpan( // text: NumberFormat.currency(locale: 'en_US', symbol: " ", decimalDigits: 0).format(habibWalletAmount), - text: " ${habibWalletAmount.toStringAsFixed(2)}", + // text: " ${(NumberFormat.decimalPattern().format(habibWalletAmount)).toStringAsFixed(2)}", + text: " ${(NumberFormat.decimalPattern().format(habibWalletAmount))}", style: TextStyle( color: resolvedTextColor, fontSize: fontSize ?? 32.f, diff --git a/lib/features/authentication/authentication_view_model.dart b/lib/features/authentication/authentication_view_model.dart index d94b100b..45c2d620 100644 --- a/lib/features/authentication/authentication_view_model.dart +++ b/lib/features/authentication/authentication_view_model.dart @@ -667,6 +667,11 @@ class AuthenticationViewModel extends ChangeNotifier { _appState.setPrivilegeModelList(activation.list!.first.listPrivilege!); _appState.setUserBloodGroup = activation.patientBlodType ?? "N/A"; + // Refresh privileges after user switching to ensure correct privileges are loaded + if (isSwitchUser) { + print("🔄 Switching user - refreshing privileges"); + await getServicePrivilege(); + } // Fetch patient blood type from new API // await getPatientBloodTypeNew(); diff --git a/lib/features/blood_donation/blood_donation_view_model.dart b/lib/features/blood_donation/blood_donation_view_model.dart index 8ad5bdec..7aa67d4f 100644 --- a/lib/features/blood_donation/blood_donation_view_model.dart +++ b/lib/features/blood_donation/blood_donation_view_model.dart @@ -1,11 +1,14 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:get_it/get_it.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/core/enums.dart'; import 'package:hmg_patient_app_new/core/utils/doctor_response_mapper.dart'; +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/authentication/authentication_view_model.dart'; import 'package:hmg_patient_app_new/features/blood_donation/blood_donation_repo.dart'; import 'package:hmg_patient_app_new/features/blood_donation/models/blood_group_hospitals_model.dart'; @@ -20,6 +23,7 @@ import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/services/dialog_service.dart'; import 'package:hmg_patient_app_new/services/error_handler_service.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart'; +import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; class BloodDonationViewModel extends ChangeNotifier { @@ -263,7 +267,13 @@ class BloodDonationViewModel extends ChangeNotifier { await bloodDonationRepo.updateBloodGroup(request: payload); await addUserAgreementForBloodDonation(); LoaderBottomSheet.hideLoader(); - dialogService.showSuccessBottomSheetWithoutH(message: "Blood Group Updated Successfully", onOkPressed: () {}); + showCommonBottomSheetWithoutHeight(GetIt.instance().navigatorKey.currentContext!, + child: Utils.getSuccessWidget(loadingText: "Blood Group Updated Successfully").paddingSymmetrical(0.h, 24.h), + callBackFunc: () {}, + isFullScreen: false, + isCloseButtonVisible: false, + isAutoDismiss: true); + // dialogService.showSuccessBottomSheetWithoutH(message: "Blood Group Updated Successfully", onOkPressed: () {}); } Future addUserAgreementForBloodDonation() async { diff --git a/lib/features/emergency_services/emergency_services_repo.dart b/lib/features/emergency_services/emergency_services_repo.dart index f8eb896f..10c5541a 100644 --- a/lib/features/emergency_services/emergency_services_repo.dart +++ b/lib/features/emergency_services/emergency_services_repo.dart @@ -183,7 +183,7 @@ class EmergencyServicesRepoImp implements EmergencyServicesRepo { @override Future>>> getProjectList() async { - Map request = {'IsOnlineCheckIn': false, 'IsAdvancePayment': false, 'IsForER': true}; + Map request = {'IsOnlineCheckIn': true, 'IsAdvancePayment': false, 'IsForER': true}; try { GenericApiModel>? apiResponse; diff --git a/lib/features/hmg_services/hmg_services_view_model.dart b/lib/features/hmg_services/hmg_services_view_model.dart index 7b32cc6e..10b340fd 100644 --- a/lib/features/hmg_services/hmg_services_view_model.dart +++ b/lib/features/hmg_services/hmg_services_view_model.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'dart:io'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart'; @@ -21,6 +22,7 @@ import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/ import 'package:hmg_patient_app_new/features/symptoms_checker/symptoms_checker_view_model.dart'; import 'package:hmg_patient_app_new/services/error_handler_service.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart'; +import 'package:sms_otp_auto_verify/sms_otp_auto_verify.dart'; import 'models/req_models/check_activation_e_referral_req_model.dart'; import 'models/resq_models/get_covid_payment_info_resp.dart'; @@ -632,6 +634,9 @@ class HmgServicesViewModel extends ChangeNotifier { Function(GenericApiModel)? onSuccess, Function(String)? onError, }) async { + // Get and set the SMS signature for auto-fill + requestModel.sMSSignature = await getSignature(); + notifyListeners(); final result = await hmgServicesRepo.sendEReferralActivationCode(requestModel); @@ -660,6 +665,14 @@ class HmgServicesViewModel extends ChangeNotifier { ); } + Future getSignature() async { + if (Platform.isAndroid) { + return await SmsVerification.getAppSignature(); + } else { + return null; + } + } + Future checkEReferralActivationCode({ required CheckActivationCodeForEReferralRequestModel requestModel, Function(GenericApiModel)? onSuccess, 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 index 0380de8e..288498d2 100644 --- 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 @@ -10,6 +10,7 @@ class SendActivationCodeForEReferralRequestModel { dynamic sessionID; bool? isDentalAllowedBackend; int? deviceTypeID; + String? sMSSignature; SendActivationCodeForEReferralRequestModel( {this.patientMobileNumber, @@ -22,7 +23,8 @@ class SendActivationCodeForEReferralRequestModel { this.patientOutSA, this.sessionID, this.isDentalAllowedBackend, - this.deviceTypeID}); + this.deviceTypeID, + this.sMSSignature}); SendActivationCodeForEReferralRequestModel.fromJson(Map json) { patientMobileNumber = json['PatientMobileNumber']; @@ -36,6 +38,7 @@ class SendActivationCodeForEReferralRequestModel { sessionID = json['SessionID']; isDentalAllowedBackend = json['isDentalAllowedBackend']; deviceTypeID = json['DeviceTypeID']; + sMSSignature = json['SMSSignature']; } Map toJson() { @@ -51,6 +54,7 @@ class SendActivationCodeForEReferralRequestModel { data['SessionID'] = this.sessionID; data['isDentalAllowedBackend'] = this.isDentalAllowedBackend; data['DeviceTypeID'] = this.deviceTypeID; + data['SMSSignature'] = this.sMSSignature; return data; } } diff --git a/lib/features/hmg_services/models/ui_models/vital_sign_ui_model.dart b/lib/features/hmg_services/models/ui_models/vital_sign_ui_model.dart index 90c513c4..ea0782bd 100644 --- a/lib/features/hmg_services/models/ui_models/vital_sign_ui_model.dart +++ b/lib/features/hmg_services/models/ui_models/vital_sign_ui_model.dart @@ -3,6 +3,16 @@ import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; +/// Enum for vital sign status to avoid string-based comparisons +enum VitalSignStatus { + normal, + high, + low, + obese, + overweight, + underweight, +} + /// UI-only helper model for Vital Sign cards. /// /// Keeps presentation logic (chip colors, icon colors, simple status rules) @@ -22,13 +32,12 @@ class VitalSignUiModel { /// Returns a color scheme for a card based on its [status] and [label]. /// - /// Rules (mirrors existing behavior in Medical File page): + /// Rules: /// - Height is always blue. - /// - High => red scheme. - /// - Low => yellow scheme. + /// - High, Obese, Overweight => red scheme. + /// - Low, Underweight => yellow scheme. /// - Otherwise => green scheme (Normal). - static VitalSignUiModel scheme({required String? status, required String label}) { - final s = (status ?? '').toLowerCase(); + static VitalSignUiModel scheme({required VitalSignStatus? status, required String label}) { final l = label.toLowerCase(); // Height should always be blue. @@ -41,7 +50,10 @@ class VitalSignUiModel { ); } - if (s.contains('high')) { + // High, Obese, Overweight => red scheme (health concerns) + if (status == VitalSignStatus.high || + status == VitalSignStatus.obese || + status == VitalSignStatus.overweight) { return VitalSignUiModel( iconBg: AppColors.chipSecondaryLightRedColor, iconFg: AppColors.primaryRedColor, @@ -50,8 +62,8 @@ class VitalSignUiModel { ); } - // Warning for both low and overweight/underweight BMI, since they can indicate potential health issues. - if (s.contains('low') || s.contains("OverWeight") || s.contains("وزن زائد") || s.contains("UnderWeight") || s.contains("وزن ناقص")) { + // Low, Underweight => yellow scheme (warning) + if (status == VitalSignStatus.low || status == VitalSignStatus.underweight) { final Color yellowBg = AppColors.highAndLow.withValues(alpha: 0.12); return VitalSignUiModel( iconBg: yellowBg, @@ -71,19 +83,37 @@ class VitalSignUiModel { ); } + /// Convert VitalSignStatus enum to localized string + static String statusToString(VitalSignStatus status) { + switch (status) { + case VitalSignStatus.normal: + return LocaleKeys.vitalSignNormal.tr(); + case VitalSignStatus.high: + return LocaleKeys.vitalSignHigh.tr(); + case VitalSignStatus.low: + return LocaleKeys.vitalSignLow.tr(); + case VitalSignStatus.obese: + return LocaleKeys.vitalSignObese.tr(); + case VitalSignStatus.overweight: + return LocaleKeys.vitalSignOverweight.tr(); + case VitalSignStatus.underweight: + return LocaleKeys.vitalSignUnderweight.tr(); + } + } + /// Simple, user-friendly classification: /// - Low: systolic < 90 OR diastolic < 60 /// - High: systolic >= 140 OR diastolic >= 90 /// - Normal: otherwise /// Returns null if values are missing/unparseable. - static String? bloodPressureStatus({dynamic systolic, dynamic diastolic}) { + static VitalSignStatus? bloodPressureStatus({dynamic systolic, dynamic diastolic}) { final int? s = toIntOrNull(systolic); final int? d = toIntOrNull(diastolic); if (s == null || d == null) return null; - if (s < 90 || d < 60) return 'Low'; - if (s >= 140 || d >= 90) return 'High'; - return 'Normal'; + if (s < 90 || d < 60) return VitalSignStatus.low; + if (s >= 140 || d >= 90) return VitalSignStatus.high; + return VitalSignStatus.normal; } static int? toIntOrNull(dynamic v) { @@ -93,28 +123,47 @@ class VitalSignUiModel { return int.tryParse(v.toString()); } - static String bmiStatus(dynamic bmi) { - String bmiStatus = 'Normal'; + static VitalSignStatus? bmiStatus(dynamic bmi) { + // Return null if BMI is not available or is 0 final double bmiResult = double.tryParse(bmi.toString()) ?? 0; - if (bmiResult >= 30) { - bmiStatus = LocaleKeys.obese.tr(); - } else if (bmiResult < 30 && bmiResult >= 25) { - bmiStatus = LocaleKeys.overWeight.tr(); - } else if (bmiResult < 25 && bmiResult >= 18.5) { - bmiStatus = LocaleKeys.normal.tr(); - } else if (bmiResult < 18.5) { - bmiStatus = LocaleKeys.underWeight.tr(); + if (bmiResult == 0 || bmi == null) { + return null; + } + + // BMI >= 25 (Overweight or Obese) => High + if (bmiResult >= 25) { + return VitalSignStatus.high; } + // BMI >= 18.5 and < 25 => Normal + else if (bmiResult >= 18.5) { + return VitalSignStatus.normal; + } + // BMI < 18.5 (Underweight) => Low + else { + return VitalSignStatus.low; + } + } + + /// Weight status based on BMI with detailed classification + /// Returns: Obese, Overweight, Normal, Underweight + static VitalSignStatus? weightStatus(dynamic bmi) { + // Return null if BMI is not available or is 0 + final double bmiResult = double.tryParse(bmi.toString()) ?? 0; - // if (bmi == null) return 'N/A'; - // final double bmiValue = double.tryParse(bmi.toString()) ?? 0; - // if (bmiValue < 18.5) return 'Underweight'; - // if (bmiValue < 25) return 'Normal'; - // if (bmiValue < 30) return 'Overweight'; - // return 'High'; + if (bmiResult == 0 || bmi == null) { + return null; + } - return bmiStatus; + if (bmiResult >= 30) { + return VitalSignStatus.obese; + } else if (bmiResult >= 25) { + return VitalSignStatus.overweight; + } else if (bmiResult >= 18.5) { + return VitalSignStatus.normal; + } else { + return VitalSignStatus.underweight; + } } } diff --git a/lib/features/medical_file/medical_file_view_model.dart b/lib/features/medical_file/medical_file_view_model.dart index b93bb62d..05f82208 100644 --- a/lib/features/medical_file/medical_file_view_model.dart +++ b/lib/features/medical_file/medical_file_view_model.dart @@ -215,27 +215,40 @@ class MedicalFileViewModel extends ChangeNotifier { final result = await medicalFileRepo.getPatientVaccinesList(); result.fold( - // (failure) async => await errorHandlerService.handleError( - // failure: failure, - // onOkPressed: () { - // onError!(failure.message); - // }, - // ), (failure) async { - // onError!(failure.message); + print("❌ Vaccine API Failure: ${failure.message}"); isPatientVaccineListLoading = false; notifyListeners(); + if (onError != null) { + onError(failure.message); + } }, (apiResponse) { + print("📦 Vaccine API Response - Status: ${apiResponse.messageStatus}, Data: ${apiResponse.data}"); + if (apiResponse.messageStatus == 2) { - // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); + print("⚠️ Vaccine API Error: ${apiResponse.errorMessage}"); + isPatientVaccineListLoading = false; + notifyListeners(); + if (onError != null) { + onError(apiResponse.errorMessage ?? "Unknown error"); + } } else if (apiResponse.messageStatus == 1) { - patientVaccineList = apiResponse.data!; + if (apiResponse.data != null) { + patientVaccineList = apiResponse.data!; + print("✅ Vaccine data loaded: ${patientVaccineList.length} items"); + } else { + print("⚠️ Vaccine API returned success but data is null"); + } isPatientVaccineListLoading = false; notifyListeners(); if (onSuccess != null) { onSuccess(apiResponse); } + } else { + print("⚠️ Unexpected messageStatus: ${apiResponse.messageStatus}"); + isPatientVaccineListLoading = false; + notifyListeners(); } }, ); diff --git a/lib/features/medical_file/models/patient_vaccine_response_model.dart b/lib/features/medical_file/models/patient_vaccine_response_model.dart index ff27b063..255a7198 100644 --- a/lib/features/medical_file/models/patient_vaccine_response_model.dart +++ b/lib/features/medical_file/models/patient_vaccine_response_model.dart @@ -1,160 +1,169 @@ +import 'dart:convert'; + class PatientVaccineResponseModel { - String? setupID; - int? projectID; - int? patientID; + String? setupId; + int? projectId; + int? patientId; int? invoiceNo; - String? procedureID; + String? procedureId; String? vaccineName; - Null? vaccineNameN; + dynamic vaccineNameN; String? invoiceDate; - int? doctorID; - int? clinicID; + int? doctorId; + int? clinicId; String? firstName; String? middleName; String? lastName; - Null? firstNameN; - Null? middleNameN; - Null? lastNameN; + dynamic firstNameN; + dynamic middleNameN; + dynamic lastNameN; String? dateofBirth; int? actualDoctorRate; String? age; String? clinicName; - Null? decimalDoctorRate; - Null? doctorImageURL; + double? decimalDoctorRate; + dynamic doctorImageUrl; String? doctorName; int? doctorRate; - int? doctorStarsRate; + double? doctorStarsRate; String? doctorTitle; int? gender; - Null? genderDescription; - Null? invoiceNoVP; + dynamic genderDescription; + dynamic invoiceNoVp; bool? isActiveDoctorProfile; bool? isDoctorAllowVedioCall; bool? isExecludeDoctor; int? noOfPatientsRate; String? patientName; String? projectName; - String? qR; + String? qr; + List? speciality; String? vaccinationDate; - PatientVaccineResponseModel( - {this.setupID, - this.projectID, - this.patientID, - this.invoiceNo, - this.procedureID, - this.vaccineName, - this.vaccineNameN, - this.invoiceDate, - this.doctorID, - this.clinicID, - this.firstName, - this.middleName, - this.lastName, - this.firstNameN, - this.middleNameN, - this.lastNameN, - this.dateofBirth, - this.actualDoctorRate, - this.age, - this.clinicName, - this.decimalDoctorRate, - this.doctorImageURL, - this.doctorName, - this.doctorRate, - this.doctorStarsRate, - this.doctorTitle, - this.gender, - this.genderDescription, - this.invoiceNoVP, - this.isActiveDoctorProfile, - this.isDoctorAllowVedioCall, - this.isExecludeDoctor, - this.noOfPatientsRate, - this.patientName, - this.projectName, - this.qR, - this.vaccinationDate}); + PatientVaccineResponseModel({ + this.setupId, + this.projectId, + this.patientId, + this.invoiceNo, + this.procedureId, + this.vaccineName, + this.vaccineNameN, + this.invoiceDate, + this.doctorId, + this.clinicId, + this.firstName, + this.middleName, + this.lastName, + this.firstNameN, + this.middleNameN, + this.lastNameN, + this.dateofBirth, + this.actualDoctorRate, + this.age, + this.clinicName, + this.decimalDoctorRate, + this.doctorImageUrl, + this.doctorName, + this.doctorRate, + this.doctorStarsRate, + this.doctorTitle, + this.gender, + this.genderDescription, + this.invoiceNoVp, + this.isActiveDoctorProfile, + this.isDoctorAllowVedioCall, + this.isExecludeDoctor, + this.noOfPatientsRate, + this.patientName, + this.projectName, + this.qr, + this.speciality, + this.vaccinationDate, + }); + + factory PatientVaccineResponseModel.fromRawJson(String str) => PatientVaccineResponseModel.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); - PatientVaccineResponseModel.fromJson(Map json) { - setupID = json['SetupID']; - projectID = json['ProjectID']; - patientID = json['PatientID']; - invoiceNo = json['InvoiceNo']; - procedureID = json['ProcedureID']; - vaccineName = json['VaccineName']; - vaccineNameN = json['VaccineNameN']; - invoiceDate = json['InvoiceDate']; - doctorID = json['DoctorID']; - clinicID = json['ClinicID']; - firstName = json['FirstName']; - middleName = json['MiddleName']; - lastName = json['LastName']; - firstNameN = json['FirstNameN']; - middleNameN = json['MiddleNameN']; - lastNameN = json['LastNameN']; - dateofBirth = json['DateofBirth']; - actualDoctorRate = json['ActualDoctorRate']; - age = json['Age']; - clinicName = json['ClinicName']; - decimalDoctorRate = json['DecimalDoctorRate']; - doctorImageURL = json['DoctorImageURL']; - doctorName = json['DoctorName']; - doctorRate = json['DoctorRate']; - doctorStarsRate = json['DoctorStarsRate']; - doctorTitle = json['DoctorTitle']; - gender = json['Gender']; - genderDescription = json['GenderDescription']; - invoiceNoVP = json['InvoiceNo_VP']; - isActiveDoctorProfile = json['IsActiveDoctorProfile']; - isDoctorAllowVedioCall = json['IsDoctorAllowVedioCall']; - isExecludeDoctor = json['IsExecludeDoctor']; - noOfPatientsRate = json['NoOfPatientsRate']; - patientName = json['PatientName']; - projectName = json['ProjectName']; - qR = json['QR']; - vaccinationDate = json['VaccinationDate']; - } + factory PatientVaccineResponseModel.fromJson(Map json) => PatientVaccineResponseModel( + setupId: json["SetupID"], + projectId: json["ProjectID"], + patientId: json["PatientID"], + invoiceNo: json["InvoiceNo"], + procedureId: json["ProcedureID"], + vaccineName: json["VaccineName"], + vaccineNameN: json["VaccineNameN"], + invoiceDate: json["InvoiceDate"], + doctorId: json["DoctorID"], + clinicId: json["ClinicID"], + firstName: json["FirstName"], + middleName: json["MiddleName"], + lastName: json["LastName"], + firstNameN: json["FirstNameN"], + middleNameN: json["MiddleNameN"], + lastNameN: json["LastNameN"], + dateofBirth: json["DateofBirth"], + actualDoctorRate: json["ActualDoctorRate"], + age: json["Age"], + clinicName: json["ClinicName"], + decimalDoctorRate: json["DecimalDoctorRate"]?.toDouble(), + doctorImageUrl: json["DoctorImageURL"], + doctorName: json["DoctorName"], + doctorRate: json["DoctorRate"], + doctorStarsRate: json["DoctorStarsRate"]?.toDouble(), + doctorTitle: json["DoctorTitle"], + gender: json["Gender"], + genderDescription: json["GenderDescription"], + invoiceNoVp: json["InvoiceNo_VP"], + isActiveDoctorProfile: json["IsActiveDoctorProfile"], + isDoctorAllowVedioCall: json["IsDoctorAllowVedioCall"], + isExecludeDoctor: json["IsExecludeDoctor"], + noOfPatientsRate: json["NoOfPatientsRate"], + patientName: json["PatientName"], + projectName: json["ProjectName"], + qr: json["QR"], + speciality: json["Speciality"] == null ? [] : List.from(json["Speciality"]!.map((x) => x)), + vaccinationDate: json["VaccinationDate"], + ); - Map toJson() { - final Map data = new Map(); - data['SetupID'] = this.setupID; - data['ProjectID'] = this.projectID; - data['PatientID'] = this.patientID; - data['InvoiceNo'] = this.invoiceNo; - data['ProcedureID'] = this.procedureID; - data['VaccineName'] = this.vaccineName; - data['VaccineNameN'] = this.vaccineNameN; - data['InvoiceDate'] = this.invoiceDate; - data['DoctorID'] = this.doctorID; - data['ClinicID'] = this.clinicID; - data['FirstName'] = this.firstName; - data['MiddleName'] = this.middleName; - data['LastName'] = this.lastName; - data['FirstNameN'] = this.firstNameN; - data['MiddleNameN'] = this.middleNameN; - data['LastNameN'] = this.lastNameN; - data['DateofBirth'] = this.dateofBirth; - data['ActualDoctorRate'] = this.actualDoctorRate; - data['Age'] = this.age; - data['ClinicName'] = this.clinicName; - data['DecimalDoctorRate'] = this.decimalDoctorRate; - data['DoctorImageURL'] = this.doctorImageURL; - data['DoctorName'] = this.doctorName; - data['DoctorRate'] = this.doctorRate; - data['DoctorStarsRate'] = this.doctorStarsRate; - data['DoctorTitle'] = this.doctorTitle; - data['Gender'] = this.gender; - data['GenderDescription'] = this.genderDescription; - data['InvoiceNo_VP'] = this.invoiceNoVP; - data['IsActiveDoctorProfile'] = this.isActiveDoctorProfile; - data['IsDoctorAllowVedioCall'] = this.isDoctorAllowVedioCall; - data['IsExecludeDoctor'] = this.isExecludeDoctor; - data['NoOfPatientsRate'] = this.noOfPatientsRate; - data['PatientName'] = this.patientName; - data['ProjectName'] = this.projectName; - data['QR'] = this.qR; - data['VaccinationDate'] = this.vaccinationDate; - return data; - } + Map toJson() => { + "SetupID": setupId, + "ProjectID": projectId, + "PatientID": patientId, + "InvoiceNo": invoiceNo, + "ProcedureID": procedureId, + "VaccineName": vaccineName, + "VaccineNameN": vaccineNameN, + "InvoiceDate": invoiceDate, + "DoctorID": doctorId, + "ClinicID": clinicId, + "FirstName": firstName, + "MiddleName": middleName, + "LastName": lastName, + "FirstNameN": firstNameN, + "MiddleNameN": middleNameN, + "LastNameN": lastNameN, + "DateofBirth": dateofBirth, + "ActualDoctorRate": actualDoctorRate, + "Age": age, + "ClinicName": clinicName, + "DecimalDoctorRate": decimalDoctorRate, + "DoctorImageURL": doctorImageUrl, + "DoctorName": doctorName, + "DoctorRate": doctorRate, + "DoctorStarsRate": doctorStarsRate, + "DoctorTitle": doctorTitle, + "Gender": gender, + "GenderDescription": genderDescription, + "InvoiceNo_VP": invoiceNoVp, + "IsActiveDoctorProfile": isActiveDoctorProfile, + "IsDoctorAllowVedioCall": isDoctorAllowVedioCall, + "IsExecludeDoctor": isExecludeDoctor, + "NoOfPatientsRate": noOfPatientsRate, + "PatientName": patientName, + "ProjectName": projectName, + "QR": qr, + "Speciality": speciality == null ? [] : List.from(speciality!.map((x) => x)), + "VaccinationDate": vaccinationDate, + }; } diff --git a/lib/features/my_appointments/my_appointments_view_model.dart b/lib/features/my_appointments/my_appointments_view_model.dart index 1b49b0ca..73f8d331 100644 --- a/lib/features/my_appointments/my_appointments_view_model.dart +++ b/lib/features/my_appointments/my_appointments_view_model.dart @@ -676,9 +676,9 @@ class MyAppointmentsViewModel extends ChangeNotifier { Future getPatientFavouriteDoctors({bool forceRefresh = false, Function(dynamic)? onSuccess, Function(String)? onError}) async { // If data is already fetched and not forcing refresh, skip API call - if (isFavouriteDoctorsDataFetched && !forceRefresh) { - return; - } + // if (isFavouriteDoctorsDataFetched && !forceRefresh) { + // return; + // } isPatientFavouriteDoctorsLoading = true; patientFavouriteDoctorsList.clear(); diff --git a/lib/features/prescriptions/models/resp_models/prescription_detail_response_model.dart b/lib/features/prescriptions/models/resp_models/prescription_detail_response_model.dart index b35cfef7..ef1bc6f7 100644 --- a/lib/features/prescriptions/models/resp_models/prescription_detail_response_model.dart +++ b/lib/features/prescriptions/models/resp_models/prescription_detail_response_model.dart @@ -33,6 +33,7 @@ class PrescriptionDetailResponseModel { num? scaleOffset; String? startDate; bool? hasReminder; + bool? isHomeMedicineDeliverySupported; PrescriptionDetailResponseModel( {this.address, @@ -68,7 +69,7 @@ class PrescriptionDetailResponseModel { this.sKU, this.scaleOffset, this.startDate, - this.hasReminder = false}); + this.hasReminder = false, this.isHomeMedicineDeliverySupported}); PrescriptionDetailResponseModel.fromJson(Map json) { address = json['Address']; diff --git a/lib/features/prescriptions/prescriptions_repo.dart b/lib/features/prescriptions/prescriptions_repo.dart index 3bfcc7fe..387d6410 100644 --- a/lib/features/prescriptions/prescriptions_repo.dart +++ b/lib/features/prescriptions/prescriptions_repo.dart @@ -99,7 +99,11 @@ class PrescriptionsRepoImp implements PrescriptionsRepo { // throw Exception("prescription list is empty"); } - final prescriptionOrders = list.map((item) => PrescriptionDetailResponseModel.fromJson(item as Map)).toList().cast(); + final List prescriptionOrders = + list.map((item) => PrescriptionDetailResponseModel.fromJson(item as Map)).toList().cast(); + if (prescriptionOrders.isNotEmpty) { + prescriptionOrders.first.isHomeMedicineDeliverySupported = response['IsHomeMedicineDeliverySupported'] ?? false; + } apiResponse = GenericApiModel>( messageStatus: messageStatus, diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart index 3e3bee02..17950a46 100644 --- a/lib/generated/codegen_loader.g.dart +++ b/lib/generated/codegen_loader.g.dart @@ -19,6 +19,8 @@ class CodegenLoader extends AssetLoader{ "arabic": "العربية", "login": "تسجيل الدخول", "noDataAvailable": "لا توجد بيانات متاحة", + "noRatingAvailable": "لا يوجد تقييم متاح", + "doctorDoesNotHaveRating": "لم يحصل هذا الطبيب على أي تقييمات بعد.", "confirm": "تأكيد", "loadingText": "جاري التحميل، يرجى الانتظار...", "kilometerUnit": "كم", @@ -203,7 +205,7 @@ class CodegenLoader extends AssetLoader{ "lastName": "اسم العائلة", "female": "أنثى", "male": "ذكر", - "preferredLanguage": "اللغة المفضلة *", + "preferredLanguage": "اللغة المفضلة", "locationsRegister": "أين ترغب في إنشاء هذا الملف؟", "ksa": "السعودية", "dubai": "دبي", @@ -314,9 +316,15 @@ class CodegenLoader extends AssetLoader{ "medicinesSubtitle": "الوصفات", "vitalSigns": "علامات حيوية", "vitalSignsSubTitle": "التقارير", + "vitalSignNormal": "طبيعي", + "vitalSignLow": "منخفض", + "vitalSignHigh": "مرتفع", + "vitalSignObese": "بدين", + "vitalSignOverweight": "زيادة الوزن", + "vitalSignUnderweight": "نقص الوزن", "myMedical": "نشط", "myMedicalSubtitle": "الأدوية", - "myDoctor": "طبيبي", + "myDoctor": "أطبائي", "myDoctorSubtitle": "القائمة", "eye": "العين", "eyeSubtitle": "القياس", @@ -378,7 +386,7 @@ class CodegenLoader extends AssetLoader{ "paymentOnline": "الخدمة", "onlineCheckIn": "تسجيل الوصول عبر الإنترنت", "myBalances": "رصيدي", - "myWallet": "محف��تي", + "myWallet": "محفظتي", "balanceAmount": "مبلغ المحفظة", "totalBalance": "إجمالي الرصيد", "createAdvancedPayment": "إعادة شحن المحفظة", @@ -502,7 +510,7 @@ class CodegenLoader extends AssetLoader{ "services2": "الخدمات", "cantSeeProfile": "لرؤية ملفك الطبي، يرجى تسجيل الدخول أو التسجيل الآن", "loginRegisterNow": "تسجيل الدخول أو التسجيل الآن", - "hmgPharmacy": "صيدلية مجموعة الحبيب الطبية", + "hmgPharmacy": "صيدلية الحبيب", "ecommerceSolution": "حلول التجارة الإلكترونية", "comprehensive": "شامل", "onlineConsulting": "استشارات عبر الإنترنت", @@ -705,7 +713,7 @@ class CodegenLoader extends AssetLoader{ "retouch": "روتوش", "bikini": "بيكيني", "totalMinutes": "إجمالي الدقائق", - "feedback": "ملاحظات", + "feedback": "رأيك يهمنا", "likeToHear": "نود سماع ملاحظاتك، ومخاوفك بشأن خدمات الرعاية الصحية وتجربة الخدمات الإلكترونية. يرجى استخدام النموذج أدناه", "emptySubject": "يرجى إدخال الموضوع", "emptyMessage": "يرجى إدخال الرسالة", @@ -713,10 +721,10 @@ class CodegenLoader extends AssetLoader{ "complainAppo": "شكوى بخصوص الموعد", "complainAppoAlt": "شكوى بدون موعد", "messageType": "نوع الرسالة", - "feedbackType": "نوع الملاحظات", + "feedbackType": "نوع الرسالة", "compliment": "تقدير", "suggestion": "اقتراح", - "yourFeedback": "تم إرسال ملاحظاتك", + "yourFeedback": "تم إرسال رأيك", "selectPart": "يرجى اختيار الجزء الذي تشتكي منه", "number": "رقم", "notClassified": "غير مصنف", @@ -728,6 +736,11 @@ class CodegenLoader extends AssetLoader{ "bloodDonation": "التبرع بالدم", "bloodDonationInfo": "من خلال هذه الخدمة، يمكنك تسجيل اسمك كمتبرع بالدم حيث سيتواصل معك بنك الدم في مجموعة الحبيب الطبية في حالة الحاجة إلى فصيلة الدم.", "bloodInstruction": "أدخل المعلومات المطلوبة، من أجل التسجيل في خدمة التبرع بالدم", + "aboutBloodDonation": "عن التبرع بالدم", + "bloodDonationDescription": "التبرع بالدم هو عمل نبيل ينقذ الأرواح. كل تبرع واحد يمكن أن ينقذ ما يصل إلى ثلاثة أشخاص. من خلال التبرع بالدم، فإنك تساعد المرضى الذين يحتاجون إلى عمليات نقل دم، وحالات الطوارئ، والعلاجات الطبية.", + "bloodDonationBenefit1": "عملية آمنة وسريعة (10-15 دقيقة)", + "bloodDonationBenefit2": "يمكنك التبرع كل 8-12 أسبوع", + "bloodDonationBenefit3": "فحص صحي مجاني لدمك", "viewTerms": "لعرض الشروط والأحكام", "wantConnectHmgNetwork": "عزيزي العميل، لا يوجد وصول إلى الإنترنت، هل ترغب في الاتصال بشبكة مجموعة الحبيب الطبية لاستخدام تطبيقنا، تأكد من أنك في نطاق شبكة مجموعة الحبيب الطبية", "failedToAccessHmgServices": "متصل بشبكة مجموعة الحبيب الطبية،\n\nلكن فشل في الوصول إلى خدمات مجموعة الحبيب الطبية", @@ -871,7 +884,7 @@ class CodegenLoader extends AssetLoader{ "onboardingBody1": "ببضع نقرات فقط يمكنك استشارة الطبيب الذي تختاره.", "onboardingHeading2": "الوصول إلى السجل الطبي بين يديك", "onboardingBody2": "تتبع تاريخك الطبي بما في ذلك الفحوصات المخبرية، الوصفات الطبية، التأمين، وغيرها.", - "hmgHospitals": "مستشفيات مجموعة الحبيب الطبية", + "hmgHospitals": "مستشفيات الحبيب", "hmcMedicalClinic": "مراكز مجموعة الحبيب الطبية", "applyFilter": "تطبيق الفلتر", "facilityAndLocation": "المرفق والموقع", @@ -920,6 +933,7 @@ class CodegenLoader extends AssetLoader{ "vat15": "ضريبة القيمة المضافة 15%", "liveCare": "لايف كير", "recentVisits": "الزيارات الأخيرة", + "favouriteDoctors": "الأطباء المفضلون", "searchByClinic": "البحث حسب العيادة", "tapToSelectClinic": "انقر لاختيار العيادة", "searchByDoctor": "البحث حسب الطبيب", @@ -945,7 +959,7 @@ class CodegenLoader extends AssetLoader{ "hospitalInformation": "معلومات المستشفى", "fetchingAppointmentShare": "جاري جلب تفاصيل الموعد...", "bookingYourAppointment": "جاري حجز موعدك...", - "selectLiveCareClinic": "اختر عيادة لايف كير", + "selectLiveCareClinic": "عيادة لايف كير", "checkingForExistingDentalPlan": "جاري التحقق من وجود خطة أسنان حالية، يرجى الانتظار...", "dentalTreatmentPlan": "خطة علاج الأسنان", "youHaveExistingTreatmentPlan": "لديك خطة علاج حالية: ", @@ -1256,7 +1270,7 @@ class CodegenLoader extends AssetLoader{ "quickLinks": "روابط سريعة", "viewMedicalFileLandingPage": "عرض الملف الطبي", "immediateLiveCareRequest": "طلب لايف كير فوري", - "yourTurnIsAfterPatients": "دورك بعد {count} مريض.", + "yourTurnIsAfterPatients": "دورك بعد {count} مريض. يرجى الانتظار حتى يحين دورك، وسيناديك الطبيب قريباً.", "dontHaveHHCOrders": "ليس لديك أي أوامر رعاية صحية منزلية حتى الآن.", "hhcOrders": "أوامر الرعاية الصحية المنزلية", "requestedServices": "الخدمات المطلوبة", @@ -1583,7 +1597,7 @@ class CodegenLoader extends AssetLoader{ "noFreeSlot": "لا توجد مواعيد متاحة في التاريخ المحدد. يرجى اختيار تاريخ آخر أو إعادة المحاولة لاحقاً.", "noThanksIKnowTheClinic": "لا، شكراً. أنا أعرف العيادة.", "unableToSendOTP": "تعذر إرسال رمز التحقق لمرة واحدة", - "loadingAIAnalysis": "جارٍ تحليل نتائج المختبر، يرجى التحلي بالصبر والسماح للذكاء الاصطناعي بالقيام بالمهمة، قد يستغرق هذا بعض الوقت.", + "loadingAIAnalysis": "نقوم بتحليل نتائجك، يرجى التحلي بالصبر والسماح للذكاء الاصطناعي بالقيام بالمهمة، قد يستغرق هذا بعض الوقت.", "symptoms": "أعراض", "insuranceInActive": "التأمين غير نشط", "insuranceInActiveContactSupport": "لديك تأمين ساري المفعول، ولكنه غير مُفعّل حاليًا في مجموعة حبيب الطبية. يُرجى التواصل مع خدمة عملاء مجموعة حبيب الطبية.", @@ -1593,9 +1607,24 @@ class CodegenLoader extends AssetLoader{ "invalidEligibility": "لا يمكنك إجراء الدفع عبر الإنترنت لأنك غير مؤهل لاستخدام الخدمة المقدمة.", "invalidInsurance": "لا يمكنك إجراء الدفع عبر الإنترنت لأنه ليس لديك تأمين صالح.", "continueCash": "متابعة الدفع نقدًا", + "applewatch": "ساعة آبل", + "applehealthapplicationshouldbeinstalledinyourphone": "يجب تثبيت تطبيق Apple Health على هاتفك", + "unabletodetectapplicationinstalledpleasecomebackonceinstalled": "لا يمكننا اكتشاف التطبيق المثبت على جهازك. يرجى العودة إلى هنا بمجرد تثبيت هذا التطبيق.", + "applewatchshouldbeconnected": "يجب توصيل ساعة آبل", + "samsungwatch": "ساعة سامسونج", + "samsunghealthapplicationshouldbeinstalledinyourphone": "يجب تثبيت تطبيق Samsung Health على هاتفك", + "samsungwatchshouldbeconnected": "يجب توصيل ساعة سامسونج", + "huaweiwatch": "ساعة هواوي", + "huaweihealthapplicationshouldbeinstalledinyourphone": "يجب تثبيت تطبيق Huawei Health على هاتفك", + "huaweiwatchshouldbeconnected": "يجب توصيل ساعة هواوي", + "whoopwatch": "ساعة Whoop", + "whoophealthapplicationshouldbeinstalledinyourphone": "يجب تثبيت تطبيق Whoop Health على هاتفك", + "whoopwatchshouldbeconnected": "يجب توصيل ساعة Whoop", + "updatetheinformation": "سيتيح ذلك جمع أحدث المعلومات من ساعة آبل الخاصة بك", "timeFor": "الوقت", "hmgPolicies": "سياسات مجموعة الحبيب الطبية", "darkMode": "المظهر الداكن", + "featureComingSoonDescription": "هذه الميزة ستتوفر قريباً. نحن نعمل جاهدين لإضافة ميزات أكثر تميزاً إلى التطبيق. انتظرونا لمتابعة التحديثات.", "generateAiAnalysisResult": "قم بإجراء تحليل لهذا المختبر AI", "ratings": "التقييمات", "hmgPharmacyText": "صيدلية الحبيب، المتجر الصيدلاني الإلكتروني المتكامل الذي تقدمه لكم مجموعة خدمات الدكتور سليمان الحبيب الطبية.", @@ -1604,13 +1633,106 @@ class CodegenLoader extends AssetLoader{ "verifyInsurance": "التحقق من التأمين", "tests": "تحليل", "calendarPermissionAlert": "يرجى منح إذن الوصول إلى التقويم من إعدادات التطبيق لضبط تذكير تناول الدواء.", - "sortByLocation": "الترتيب حسب الموقع" + "sortByNearestLocation": "فرز حسب الأقرب إلى موقعك", + "giveLocationPermissionForNearestList": "يرجى منح إذن الوصول إلى الموقع من إعدادات التطبيق لعرض أقرب المواقع.", + "sortByLocation": "الترتيب حسب الموقع", + "timeForFirstReminder": "وقت التذكير الأول", + "reminderRemovalNote": "يمكنك إزالتها من التقويم الخاص بك لاحقاً عن طريق إيقاف تشغيل التذكير", + "communicationLanguage": "لغة التواصل", + "cmcServiceHeader": "فحص صحي شامل: تشخيص متقدم، معلومات صحية مفصلة", + "cmcServiceDescription": "احصل على معلومات تفصيلية عن صحتك من خلال خدمات التشخيص المتقدمة لدينا. افهم جسمك بشكل أفضل لمستقبل صحي.", + "eReferralServiceHeader": "نظام الإحالة الإلكترونية في مستشفى حبيب: تبسيط عملية إحالة المرضى", + "eReferralServiceDescription": "نُسهّل عملية نقل المرضى بسلاسة إلى مستشفى حبيب من خلال نظام الإحالة الإلكترونية الآمن لدينا. نضمن استمرارية الرعاية لكل مريض.", + "bloodDonationServiceHeader": "تبرع بالدم، أنقذ الأرواح. تبرعك يُحدث فرقاً.", + "bloodDonationServiceDescription": "تبرّع بالدم، وأنقذ الأرواح. تبرعك يبعث الأمل. انضم لحملة التبرع بالدم وكن شريان حياة للمحتاجين. كل قطرة تُحدث فرقًا!", + "healthTrackersServiceHeader": "تتبّع مؤشراتك الحيوية بسهولة ويسر", + "healthTrackersServiceDescription": "أدخل بياناتك لمراقبة معدل ضربات القلب وضغط الدم بشكل مستمر، بالإضافة إلى ملخصات دقيقة لأنشطتك اليومية. ابقَ على اطلاع وحسّن صحتك بسهولة.", + "waterConsumptionServiceHeader": "حافظ على رطوبتك، حافظ على صحتك. تتبع كمية الماء التي تشربها يومياً بكل سهولة.", + "waterConsumptionServiceDescription": "أروِ عطشك، وتابع صحتك. راقب كمية الماء التي تتناولها يومياً بكل سهولة باستخدام تطبيقنا سهل الاستخدام، مما يضمن لك الترطيب الأمثل والصحة الجيدة.", + "smartWatchServiceHeader": "قم بمزامنة ساعتك الذكية مع تطبيقات الصحة", + "smartWatchServiceDescription": "قم بتوصيل ساعتك الذكية بسلاسة بتطبيقنا الصحي لتتبع البيانات بسهولة والحصول على رؤى شخصية.", + "liveChatServiceHeader": "مساعدة الخبراء على مدار الساعة طوال أيام الأسبوع\n\nمساعدة", + "liveChatServiceDescription": "هل تحتاج إلى مساعدة؟ تتيح لك خدمة الدردشة المباشرة لدينا التواصل مع فريق دعم الخبراء للإجابة على أي أسئلة لديك حول الميزات أو الإعدادات أو استكشاف الأخطاء وإصلاحها.", + "emergencyServiceHeader": "تسجيل الوصول إلى قسم الطوارئ، أسرع من أي وقت مضى. اتصل بالإسعاف / فريق الاستجابة السريعة على الفور", + "emergencyServiceDescription": "هل تواجه حالة طبية طارئة؟ سيارات الإسعاف وفرق الاستجابة السريعة لدينا جاهزة على مدار الساعة. بالإضافة إلى ذلك، يمكنك تسجيل دخولك إلى قسم الطوارئ بسرعة لتلقي رعاية أسرع.", + "homeHealthCareServiceHeader": "صحتك، في أبهى صورها. رعاية فائقة الجودة، تصلك إلى عتبة دارك.", + "homeHealthCareServiceDescription": "نقدم لكم رعاية صحية عالية الجودة تصلكم إلى عتبة منزلكم. ممرضات ذوات خبرة يقدمون رعاية حانية في راحة منزلكم.", + "profileOnlyText": "الملف الشخصي", + "information": "معلومة", + "noFavouriteDoctors": "ليس لديك أي قائمة مفضلة حتى الآن", + "addDoctors": "إضافة الأطباء", + "favouriteList": "قائمة المفضلة", + "later": "لاحقاً", + "cancelAppointmentConfirmMessage": "هل أنت متأكد من رغبتك في إلغاء هذا الموعد؟", + "acknowledged": "معترف به", + "searchLabResults": "بحث نتائج المختبر", + "callForAssistance": "اتصل للحصول على المساعدة الفورية", + "oneWaySubtitle": "نقل من الموقع إلى المستشفى", + "twoWaySubtitle": "نقل من الموقع إلى المستشفى والعودة مرة أخرى", + "toHospitalSubtitle": "نقل من موقعك الحالي إلى المستشفى", + "fromHospitalSubtitle": "نقل من المستشفى إلى منزلك", + "clinicSchedule": "جدول العيادة", + "doctorAddedToFavourite": "تمت إضافة إلى قائمة المفضلة", + "doctorRemovedFromFavourite": "تمت إزالة من قائمة الأطباء المفضلين", + "addNewFavorite": "إضافة مفضلة جديدة", + "favourites": "المفضلة", + "pendingAncillaryOrders": "مدفوعات طلبات الاضافية", + "liveCarePermissions": "يتطلب لايف كير أذونات الكاميرا والميكروفون وإشعارات، يرجى السماح لها بالمتابعة.", + "containsImage": "يحتوي على صورة", + "servicesSelected": "الخدمة (الخدمات) المختارة", + "selectMedicalFileFromFamily": "اختر ملف طبي من عائلتك", + "anyFileFromHMG": "أي ملف طبي نشط من مجموعة حبيب الطبية", + "noDistance": "المسافة غير متوفرة", + "enterAmount": "يرجى إدخال المبلغ للمتابعة.", + "selectHospitalContinue": "يرجى اختيار المستشفى للمتابعة.", + "year": "سنة", + "notifyPrescription": "أبلغني قبل وقت الاستهلاك", + "doctorRatingAppointment": "طبيب\nتصنيف", + "ratingSubmitted": "تم إرسال التقييم", + "rateTheDoctor": "يرجى تقييم الطبيب", + "downloadInvoice": "تحميل الفاتورة", + "approvalDetails": "تفاصيل الموافقة", + "obese": "سمين", + "overWeight": "وزن زائد", + "underWeight": "وزن ناقص", + "nearestGate": "أقرب بوابة", + "RRTDDetails": "فريق الاستجابة السريعة يقوم بتقديم رعاية صحية متكاملة لجميع الحالات الطارئة و المستقرة، الخدمة تشمل جميع الأعمار بما في ذلك حالات الأطفال.", + "normalMapType": "افتراضي", + "satelliteMapType": "قمر صناعي", + "terrainMapType": "تضاريس", + "hybridMapType": "هجين", + "openCamera": "افتح الكاميرا", + "openGallery": "افتح المعرض", + "openFiles": "فتح الملفات", + "generateAiAnalysisRadResult": "بإنشاء تحليل الذكاء الاصطناعي لهذا التقرير", + "grantLocationPermission": "يرجى منح إذن الوصول إلى الموقع من إعدادات التطبيق للحصول على نتائج أفضل.", + "NoBookedAppointments": "لا توجد مواعيد محجوزة", + "aiDisclaimerRad": "سيتم مشاركة بيانات نتائج الأشعة الخاصة بك بشكل آمن مع محلل الذكاء الاصطناعي لدينا لتحليلها. يساعد هذا في توفير رؤى صحية مخصصة. هل ترغب في المتابعة؟", + "imageSizeTooLarge": "حجم الصورة كبير جداً. الحد الأقصى للحجم هو 1 ميجابايت", + "profileImageUpdatedSuccessfully": "تم تحديث صورة الملف الشخصي بنجاح", + "failedToProcessImage": "فشل في معالجة الصورة. يرجى المحاولة مرة أخرى", + "cameraGalleryPermissionMessage": "مطلوب إذن الكاميرا والمعرض لتحميل صورة الملف الشخصي. يرجى منح الأذونات من إعدادات التطبيق.", + "cameraGalleryPermissionRequired": "مطلوب أذونات الكاميرا والمعرض", + "cameraPermissionMessage": "مطلوب إذن الكاميرا لالتقاط الصور. يرجى منح الإذن من إعدادات التطبيق.", + "cameraPermissionRequired": "مطلوب إذن الكاميرا", + "galleryPermissionMessage": "مطلوب إذن المعرض لتحديد الصور. يرجى منح الإذن من إعدادات التطبيق.", + "galleryPermissionRequired": "مطلوب إذن المعرض", + "failedToCheckPermissions": "فشل في التحقق من الأذونات. يرجى المحاولة مرة أخرى", + "takePhotoWithCamera": "التقط صورة بالكاميرا", + "selectFromPhotoLibrary": "اختر من مكتبة الصور", + "selectDocumentFiles": "اختر ملفات المستندات", + "profilePicture": "صورة الملف الشخصي", + "sendingAskDrRequest": "جارٍ إرسال الطلب...", + "askDrRequestSentSuccess": "تم إرسال الطلب بنجاح، وسيتواصل معك الطبيب قريباً.", + "onlineCheckIn24HoursOnly": "لا تتوفر خدمة تسجيل الوصول عبر الإنترنت إلا خلال 4 ساعات من موعد الحجز.لا تتوفر خدمة تسجيل الوصول عبر الإنترنت إلا خلال 4 ساعات من موعد الحجز." }; static const Map _en_US = { "english": "English", "arabic": "Arabic", "login": "Login", "noDataAvailable": "No Data Available", + "noRatingAvailable": "No Rating Available", + "doctorDoesNotHaveRating": "This doctor does not have any ratings yet.", "confirm": "Confirm", "loadingText": "Loading, please wait...", "kilometerUnit": "KM", @@ -1795,7 +1917,7 @@ static const Map _en_US = { "lastName": "Last Name", "female": "Female", "male": "Male", - "preferredLanguage": "Preferred Language *", + "preferredLanguage": "Preferred Language", "locationsRegister": "Where do you want to create this file?", "ksa": "KSA", "dubai": "Dubai", @@ -1906,9 +2028,15 @@ static const Map _en_US = { "medicinesSubtitle": "Prescriptions", "vitalSigns": "Vital Signs", "vitalSignsSubTitle": "Reports", + "vitalSignNormal": "Normal", + "vitalSignLow": "Low", + "vitalSignHigh": "High", + "vitalSignObese": "Obese", + "vitalSignOverweight": "Overweight", + "vitalSignUnderweight": "Underweight", "myMedical": "Active", "myMedicalSubtitle": "Medications", - "myDoctor": "My Doctor", + "myDoctor": "My Doctors", "myDoctorSubtitle": "List", "eye": "Eye", "eyeSubtitle": "Measurement", @@ -2316,6 +2444,11 @@ static const Map _en_US = { "bloodDonation": "Blood Donation", "bloodDonationInfo": "Through this service, you can register your name as a blood donor where the blood bank in the Habib Medical Group will communicate you in case of need for blood type.", "bloodInstruction": "Enter the required information, in order to register for Blood Donation Service", + "aboutBloodDonation": "About Blood Donation", + "bloodDonationDescription": "Blood donation is a noble act that saves lives. Every single donation can save up to three people. By donating blood, you help patients in need of transfusions, emergencies, and medical treatments.", + "bloodDonationBenefit1": "Safe and quick process (10-15 minutes)", + "bloodDonationBenefit2": "You can donate every 8-12 weeks", + "bloodDonationBenefit3": "Free health check of your blood", "viewTerms": "To view the terms and conditions", "wantConnectHmgNetwork": "Dear customer there is no internet access, do you want to connect with HMG network to use our app, make sure you are in range of HMG network", "failedToAccessHmgServices": "Connected with HMG Network,\n\nBut failed to access HMG services", @@ -2509,6 +2642,7 @@ static const Map _en_US = { "vat15": "VAT 15%", "liveCare": "LiveCare", "recentVisits": "Recent Visits", + "favouriteDoctors": "Favourite Doctors", "searchByClinic": "Search By Clinic", "tapToSelectClinic": "Tap to select clinic", "searchByDoctor": "Search By Doctor", @@ -2534,7 +2668,7 @@ static const Map _en_US = { "hospitalInformation": "Hospital Information", "fetchingAppointmentShare": "Fetching Appointment Share...", "bookingYourAppointment": "Booking your appointment...", - "selectLiveCareClinic": "Select LiveCare Clinic", + "selectLiveCareClinic": "LiveCare Clinic", "checkingForExistingDentalPlan": "Checking for an existing dental plan, Please wait...", "dentalTreatmentPlan": "Dental treatment plan", "youHaveExistingTreatmentPlan": "You have an existing treatment plan: ", @@ -2845,7 +2979,7 @@ static const Map _en_US = { "quickLinks": "Quick Links", "viewMedicalFileLandingPage": "View medical file", "immediateLiveCareRequest": "Immediate LiveCare Request", - "yourTurnIsAfterPatients": "Your turn is after {count} patients.", + "yourTurnIsAfterPatients": "Your turn is after {count} patients. Please wait for your turn, The doctor will call you shortly.", "dontHaveHHCOrders": "You don't have any Home Health Care orders yet.", "hhcOrders": "HHC Orders", "requestedServices": "Requested Services", @@ -2913,7 +3047,7 @@ static const Map _en_US = { "rejectedYourFamilyMemberRequest": "{status} your family member request", "notAvailable": "N/A", "selectAProfile": "Please select a profile", - "switchFamilyFile": "Switch from the below list of medical file", + "switchFamilyFile": "Switch from the below list of medical files", "medicalFiles": "Medical Files", "addANewFamilyMember": "Add a new family member", "viewInvoiceDetails": "View invoice details", @@ -3175,7 +3309,7 @@ static const Map _en_US = { "contextDependent": "Context Dependent", "calculatedValue": "Calculated Value", "calculationFormula": "Calculation Formula", - "loadingAIAnalysis": "Analysing your lab results, Please be patient and let the AI do the magic, This might take some time.", + "loadingAIAnalysis": "Analysing your results, Please be patient and let the AI do the magic, This might take some time.", "symptoms": "Symptoms", "insuranceInActive": "Insurance Inactive", "insuranceInActiveContactSupport": "You have a valid insurance but it is currently inactive in HMG. Please contact HMG customer support to activate it.", @@ -3185,8 +3319,23 @@ static const Map _en_US = { "invalidEligibility": "You cannot make online payment because you are not eligible to use the provided service.", "invalidInsurance": "You cannot make online payment because you do not have a valid insurance.", "continueCash": "Continue as cash", + "applewatch": "Apple Watch", + "applehealthapplicationshouldbeinstalledinyourphone": "Apple Health application should be installed in your phone", + "unabletodetectapplicationinstalledpleasecomebackonceinstalled": "We are unable to detect the application installed in your device. Please come back here once you have installed this application.", + "applewatchshouldbeconnected": "Apple Watch should be connected", + "samsungwatch": "Samsung Watch", + "samsunghealthapplicationshouldbeinstalledinyourphone": "Samsung Health application should be installed in your phone", + "samsungwatchshouldbeconnected": "Samsung Watch should be connected", + "huaweiwatch": "Huawei Watch", + "huaweihealthapplicationshouldbeinstalledinyourphone": "Huawei Health application should be installed in your phone", + "huaweiwatchshouldbeconnected": "Huawei Watch should be connected", + "whoopwatch": "Whoop Watch", + "whoophealthapplicationshouldbeinstalledinyourphone": "Whoop Health application should be installed in your phone", + "whoopwatchshouldbeconnected": "Whoop Watch should be connected", + "updatetheinformation": "This will allow to gather the most up to date information from your apple watch", "timeFor": "Time For", "hmgPolicies": "HMG Policies", + "featureComingSoonDescription": "Feature is coming soon. We are actively working to bring more exciting features into the app. Stay tuned for updates.", "darkMode": "Dark Mode", "generateAiAnalysisResult": "Generate AI analysis for this result", "ratings": "Ratings", @@ -3196,7 +3345,98 @@ static const Map _en_US = { "verifyInsurance": "Verify Insurance", "tests": "tests", "calendarPermissionAlert": "Please grant calendar access permission from app settings to set medication reminder.", - "sortByLocation": "Sort by location" + "timeForFirstReminder": "Time for 1st reminder", + "reminderRemovalNote": "You can remove it from your calendar later by switching off the reminder", + "sortByLocation": "Sort by location", + "sortByNearestLocation": "Sort by nearest to your location", + "giveLocationPermissionForNearestList": "Please grant location permission from app settings to see the nearest locations.", + "communicationLanguage": "Communication Language", + "cmcServiceHeader": "Complete Health Checkup: Advanced diagnostics, Detailed Health insights", + "cmcServiceDescription": "Get detailed insights into your health with our advanced diagnostics. Understand your body better for a healthier future.", + "eReferralServiceHeader": "HMG Hospital E-Referral: Streamlined patient referrals", + "eReferralServiceDescription": "Facilitate seamless patient transfers to HMG with our secure e-referral system. Ensure continuity of care for every patient.", + "bloodDonationServiceHeader": "Give Blood, Save Lives. Your donation makes a difference.", + "bloodDonationServiceDescription": "Donate blood, empower lives. Your contribution creates hope. Join our blood drive and be a lifeline for those in need. Every drop counts!", + "healthTrackersServiceHeader": "Track Your Vitals with Ease and effortlessly ", + "healthTrackersServiceDescription": "Input your metrics for continuous heart rate monitoring, blood pressure and precise daily activity summaries. Stay informed and optimize your well-being with ease.", + "waterConsumptionServiceHeader": "Stay Hydrated, Stay Healthy. Track your daily water intake with ease.", + "waterConsumptionServiceDescription": "Quench your thirst, track your health. Effortlessly monitor your daily water intake with our user-friendly app, ensuring optimal hydration and well-being.", + "smartWatchServiceHeader": "Sync Your Smartwatch with Health Apps", + "smartWatchServiceDescription": "Seamlessly connect your smartwatch to our health app for effortless data tracking and personalized insights.", + "liveChatServiceHeader": "24/7 Expert\nAssistance", + "liveChatServiceDescription": "Need help ? Our live chat connects you with expert support for any questions about features, settings, or troubleshooting.", + "emergencyServiceHeader": "ER Check-in, Faster Than Ever. Call ambulance / Rapid Response Team instantly", + "emergencyServiceDescription": "In a medical emergency? Our ambulances and rapid response teams are on standby 24/7. Plus, quick ER check-in for faster care.", + "homeHealthCareServiceHeader": "Your Health, Elevated. Premium care, delivered to your doorstep.", + "homeHealthCareServiceDescription": "We bring quality healthcare to your doorstep. Experienced nurses providing compassionate care in the comfort of your home.", + "profileOnlyText": "Profile", + "information": "Information", + "noFavouriteDoctors": "You don't have any favourite list yet", + "addDoctors": "Add Doctors", + "favouriteList": "Favourite List", + "later": "Later", + "cancelAppointmentConfirmMessage": "Are you sure you want to cancel this appointment?", + "acknowledged": "Acknowledged", + "searchLabResults": "Search lab results", + "callForAssistance": "Call for immediate assistance", + "oneWaySubtitle": "Pickup from location to hospital", + "twoWaySubtitle": "Round trip from location to hospital and back", + "toHospitalSubtitle": "Transfer from your current location to the hospital", + "fromHospitalSubtitle": "Transfer from the hospital back to your home", + "clinicSchedule": "Clinic Schedule", + "doctorAddedToFavourite": "Added to favorites list", + "doctorRemovedFromFavourite": "Removed from favorites list", + "favourites": "Favourites", + "addNewFavorite": "Add new favourite", + "pendingAncillaryOrders": "Pending ancillary orders", + "liveCarePermissions": "LiveCare requires Camera, Microphone & Notifications permissions to enable virtual consultation between patient & doctor, Open app settings to allow them.", + "containsImage": "Contains Image", + "servicesSelected": "Service(s) Selected", + "selectMedicalFileFromFamily": "Select a medical file from your family", + "anyFileFromHMG": "Any active medical file from HMG", + "noDistance": "Distance not available", + "enterAmount": "Please enter amount to continue.", + "selectHospitalContinue": "Please select hospital to continue.", + "year": "Year", + "notifyPrescription": "Notify me before the consumption time", + "doctorRatingAppointment": "Doctor\nRating", + "ratingSubmitted": "Rating Submitted", + "rateTheDoctor": "Please rate the doctor", + "downloadInvoice": "Download Invoice", + "approvalDetails": "Approval Details", + "obese": "Obese", + "overWeight": "OverWeight", + "underWeight": "UnderWeight", + "nearestGate": "Nearest Gate", + "RRTDDetails": "The Rapid response team provides a comprehensive medical service for all sorts of urgent and stable cases of all ages, including adult and paediatric patients.", + "normalMapType": "Default", + "satelliteMapType": "Satellite", + "terrainMapType": "Terrain", + "hybridMapType": "Hybrid", + "openCamera": "Open Camera", + "openGallery": "Open Gallery", + "openFiles": "Open Files", + "generateAiAnalysisRadResult": "Generate AI analysis for this result", + "grantLocationPermission": "Please grant location permission from app settings to see better results.", + "NoBookedAppointments": "No Booked Appointments", + "aiDisclaimerRad": "Your radiology result data will be securely shared with our AI Analyzer for analysis. This helps provide personalized health insights. Do you want to proceed?", + "imageSizeTooLarge": "Image size is too large. Maximum size is 1MB", + "profileImageUpdatedSuccessfully": "Profile image updated successfully", + "failedToProcessImage": "Failed to process image. Please try again", + "cameraGalleryPermissionMessage": "Camera and Gallery permissions are required to upload profile picture. Please grant permissions from app settings.", + "cameraGalleryPermissionRequired": "Camera and Gallery permissions are required", + "cameraPermissionMessage": "Camera permission is required to take photos. Please grant permission from app settings.", + "cameraPermissionRequired": "Camera permission is required", + "galleryPermissionMessage": "Gallery permission is required to select photos. Please grant permission from app settings.", + "galleryPermissionRequired": "Gallery permission is required", + "failedToCheckPermissions": "Failed to check permissions. Please try again", + "takePhotoWithCamera": "Take a photo with your camera", + "selectFromPhotoLibrary": "Select from your photo library", + "selectDocumentFiles": "Select document files", + "profilePicture": "Profile Picture", + "sendingAskDrRequest": "Sending Request...", + "askDrRequestSentSuccess": "Request has been sent successfully, The doctor will get back to you soon.", + "onlineCheckIn24HoursOnly": "Online Check-In is only available within 4 hours of the appointment time." }; static const Map> mapLocales = {"ar_SA": _ar_SA, "en_US": _en_US}; } diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index 1e9b1349..1bd8019d 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -302,6 +302,12 @@ abstract class LocaleKeys { static const medicinesSubtitle = 'medicinesSubtitle'; static const vitalSigns = 'vitalSigns'; static const vitalSignsSubTitle = 'vitalSignsSubTitle'; + static const vitalSignNormal = 'vitalSignNormal'; + static const vitalSignLow = 'vitalSignLow'; + static const vitalSignHigh = 'vitalSignHigh'; + static const vitalSignObese = 'vitalSignObese'; + static const vitalSignOverweight = 'vitalSignOverweight'; + static const vitalSignUnderweight = 'vitalSignUnderweight'; static const myMedical = 'myMedical'; static const myMedicalSubtitle = 'myMedicalSubtitle'; static const myDoctor = 'myDoctor'; @@ -716,6 +722,11 @@ abstract class LocaleKeys { static const bloodDonation = 'bloodDonation'; static const bloodDonationInfo = 'bloodDonationInfo'; static const bloodInstruction = 'bloodInstruction'; + static const aboutBloodDonation = 'aboutBloodDonation'; + static const bloodDonationDescription = 'bloodDonationDescription'; + static const bloodDonationBenefit1 = 'bloodDonationBenefit1'; + static const bloodDonationBenefit2 = 'bloodDonationBenefit2'; + static const bloodDonationBenefit3 = 'bloodDonationBenefit3'; static const viewTerms = 'viewTerms'; static const wantConnectHmgNetwork = 'wantConnectHmgNetwork'; static const failedToAccessHmgServices = 'failedToAccessHmgServices'; diff --git a/lib/presentation/appointments/appointment_details_page.dart b/lib/presentation/appointments/appointment_details_page.dart index c0b469b4..ef3b0c94 100644 --- a/lib/presentation/appointments/appointment_details_page.dart +++ b/lib/presentation/appointments/appointment_details_page.dart @@ -554,11 +554,15 @@ class _AppointmentDetailsPageState extends State { clinicDescription: widget.patientAppointmentHistoryResponseModel.clinicName, decimalDoctorRate: widget.patientAppointmentHistoryResponseModel.decimalDoctorRate, name: widget.patientAppointmentHistoryResponseModel.projectName, - isHomeMedicineDeliverySupported: false, + isHomeMedicineDeliverySupported: val.data.first.isHomeMedicineDeliverySupported ?? false, projectID: widget.patientAppointmentHistoryResponseModel.projectID, clinicID: widget.patientAppointmentHistoryResponseModel.clinicID, doctorID: widget.patientAppointmentHistoryResponseModel.doctorID, setupID: widget.patientAppointmentHistoryResponseModel.setupID, + isInOutPatient: widget.patientAppointmentHistoryResponseModel.isInOutPatient, + appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo, + episodeID: widget.patientAppointmentHistoryResponseModel.episodeID, + dischargeNo: 0, ); Navigator.of(context).push( CustomPageRoute( @@ -642,6 +646,13 @@ class _AppointmentDetailsPageState extends State { ); }, ), + callBackFunc: () { + myAppointmentsViewModel.getDoctorsRatingCheck( + doctorID: widget.patientAppointmentHistoryResponseModel.doctorID, + clinicID: widget.patientAppointmentHistoryResponseModel.clinicID, + projectID: widget.patientAppointmentHistoryResponseModel.projectID, + appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo); + }, isFullScreen: false, ); } diff --git a/lib/presentation/appointments/widgets/appointment_rating_widget.dart b/lib/presentation/appointments/widgets/appointment_rating_widget.dart index 735895a0..9b13c6cd 100644 --- a/lib/presentation/appointments/widgets/appointment_rating_widget.dart +++ b/lib/presentation/appointments/widgets/appointment_rating_widget.dart @@ -154,7 +154,8 @@ class _AppointmentRatingWidgetState extends State { Expanded( child: CustomButton( text: LocaleKeys.submit.tr(context: context), - onPressed: () async { + isDisabled: rating == 0, + onPressed: rating == 0 ? null : () async { // Set up clinic rating and show clinic rating view // appointmentRatingViewModel!.setTitle(LocaleKeys.rateDoctor.tr(context: context),); // appointmentRatingViewModel!.setSubTitle(LocaleKeys.howWasYourLastVisitWithDoctor.tr(context: context),); @@ -165,7 +166,6 @@ class _AppointmentRatingWidgetState extends State { // await appointmentRatingViewModel!.submitClinicRating(clinicRate: rating, clinicNote: note); LoaderBottomSheet.hideLoader(); Navigator.pop(context); - setState(() {}); }, ), diff --git a/lib/presentation/blood_donation/blood_donation_page.dart b/lib/presentation/blood_donation/blood_donation_page.dart index 277d78c0..c6767c2d 100644 --- a/lib/presentation/blood_donation/blood_donation_page.dart +++ b/lib/presentation/blood_donation/blood_donation_page.dart @@ -56,11 +56,11 @@ class _BloodDonationPageState extends State { child: CollapsingListView( title: LocaleKeys.bloodDonation.tr(), trailing: CustomButton( - text: "Book", + text: LocaleKeys.book.tr(context: context), onPressed: () { if (bloodDonationVM.isUserAuthanticated()) { bloodDonationVM.fetchHospitalsList().then((value) { - showCommonBottomSheetWithoutHeight(context, title: "Select Hospital", isDismissible: false, child: Consumer(builder: (_, data, __) { + showCommonBottomSheetWithoutHeight(context, title: LocaleKeys.selectHospital.tr(context: context), isDismissible: false, child: Consumer(builder: (_, data, __) { return HospitalBottomSheetBodySelection( isHideTitle: true, onUserHospitalSelection: (BdGetProjectsHaveBdClinic userChoice) { @@ -224,6 +224,109 @@ class _BloodDonationPageState extends State { child: SelectBloodGroupWidget(isArabic: appState.isArabic(), bloodDonationViewModel: bloodDonationVM), callBackFunc: () {}); }), + SizedBox(height: 24.h), + Container( + padding: EdgeInsets.all(16.h), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.bgRedLightColor.withValues(alpha: 0.1), + borderRadius: 12.r, + hasShadow: false, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Container( + padding: EdgeInsets.all(8.h), + decoration: BoxDecoration( + color: AppColors.primaryRedColor.withValues(alpha: 0.1), + borderRadius: BorderRadius.circular(8.r), + ), + child: Utils.buildSvgWithAssets( + icon: AppAssets.blood_icon, + width: 20.h, + height: 20.h, + iconColor: AppColors.primaryRedColor, + ), + ), + SizedBox(width: 12.w), + Expanded( + child: LocaleKeys.aboutBloodDonation.tr(context: context).toText16( + color: AppColors.primaryRedColor, + isBold: true, + ), + ), + ], + ), + SizedBox(height: 12.h), + LocaleKeys.bloodDonationDescription.tr(context: context).toText14( + color: AppColors.greyTextColor, + isBold: true, + ), + SizedBox(height: 12.h), + Row( + children: [ + Container( + width: 4.w, + height: 4.h, + decoration: BoxDecoration( + color: AppColors.primaryRedColor, + shape: BoxShape.circle, + ), + ), + SizedBox(width: 8.w), + Expanded( + child: LocaleKeys.bloodDonationBenefit1.tr(context: context).toText13( + color: AppColors.textColor, + isBold: true, + ), + ), + ], + ), + SizedBox(height: 8.h), + Row( + children: [ + Container( + width: 4.w, + height: 4.h, + decoration: BoxDecoration( + color: AppColors.primaryRedColor, + shape: BoxShape.circle, + ), + ), + SizedBox(width: 8.w), + Expanded( + child: LocaleKeys.bloodDonationBenefit2.tr(context: context).toText13( + color: AppColors.textColor, + isBold: true, + ), + ), + ], + ), + SizedBox(height: 8.h), + Row( + children: [ + Container( + width: 4.w, + height: 4.h, + decoration: BoxDecoration( + color: AppColors.primaryRedColor, + shape: BoxShape.circle, + ), + ), + SizedBox(width: 8.w), + Expanded( + child: LocaleKeys.bloodDonationBenefit3.tr(context: context).toText13( + color: AppColors.textColor, + isBold: true, + ), + ), + ], + ), + ], + ), + ), ], ), ), diff --git a/lib/presentation/book_appointment/book_appointment_page.dart b/lib/presentation/book_appointment/book_appointment_page.dart index 47c4f968..aff3e275 100644 --- a/lib/presentation/book_appointment/book_appointment_page.dart +++ b/lib/presentation/book_appointment/book_appointment_page.dart @@ -117,7 +117,7 @@ class _BookAppointmentPageState extends State { SizedBox(height: 24.h), getSelectedTabData(bookAppointmentsVM.selectedTabIndex), SizedBox(height: 24.h), - if (appState.isAuthenticated) ...[ + if (appState.isAuthenticated && bookAppointmentsVM.selectedTabIndex == 0) ...[ Consumer(builder: (context, myAppointmentsVM, child) { return myAppointmentsVM.isPatientMyDoctorsLoading ? SizedBox( diff --git a/lib/presentation/book_appointment/widgets/appointment_calendar.dart b/lib/presentation/book_appointment/widgets/appointment_calendar.dart index be8ae922..ef1cc273 100644 --- a/lib/presentation/book_appointment/widgets/appointment_calendar.dart +++ b/lib/presentation/book_appointment/widgets/appointment_calendar.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:ui' as ui; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; @@ -73,6 +74,8 @@ class _AppointmentCalendarState extends State { bookAppointmentsViewModel = Provider.of(context, listen: false); authVM = Provider.of(context, listen: false); appState = getIt.get(); + final isArabic = appState.isArabic(); + return Padding( padding: EdgeInsets.symmetric(horizontal: 0.h), child: Column( @@ -90,87 +93,93 @@ class _AppointmentCalendarState extends State { // ), SizedBox( height: 350.h, - child: Localizations.override( - context: context, - locale: const Locale('en'), - child: SfCalendar( - controller: _calendarController, - minDate: DateTime.now(), - showNavigationArrow: true, - headerHeight: 60.h, - headerStyle: CalendarHeaderStyle( - backgroundColor: AppColors.transparent, - textAlign: TextAlign.start, - textStyle: TextStyle(fontSize: 18.f, fontWeight: FontWeight.w600, letterSpacing: -0.46, color: AppColors.primaryRedColor, fontFamily: "Poppins"), - ), - viewHeaderStyle: ViewHeaderStyle( - // backgroundColor: AppColors.scaffoldBgColor, - dayTextStyle: TextStyle(fontSize: 14.f, fontWeight: FontWeight.w600, letterSpacing: -0.46, color: AppColors.textColor, fontFamily: "Poppins"), - ), - view: CalendarView.month, - todayHighlightColor: Colors.transparent, - todayTextStyle: TextStyle(color: AppColors.textColor, fontWeight: FontWeight.bold, fontFamily: "Poppins"), - selectionDecoration: ShapeDecoration( - color: AppColors.transparent, - shape: SmoothRectangleBorder( - borderRadius: BorderRadius.circular(10.r), - smoothness: 1, - side: BorderSide(color: AppColors.primaryRedColor, width: 1.5), + child: Directionality( + textDirection: isArabic ? ui.TextDirection.rtl : ui.TextDirection.ltr, + child: Localizations.override( + context: context, + locale: isArabic ? const Locale('ar', 'SA') : const Locale('en'), + child: SfCalendar( + controller: _calendarController, + minDate: DateTime.now(), + showNavigationArrow: true, + headerHeight: 60.h, + headerStyle: CalendarHeaderStyle( + backgroundColor: AppColors.transparent, + textAlign: isArabic ? TextAlign.end : TextAlign.start, + textStyle: TextStyle(fontSize: 18.f, fontWeight: FontWeight.w600, letterSpacing: -0.46, color: AppColors.primaryRedColor), ), - ), - cellBorderColor: AppColors.transparent, - dataSource: MeetingDataSource(_getDataSource()), - monthCellBuilder: (context, details) => Padding( - padding: EdgeInsets.all(12.h), - child: details.date.day.toString().toText14( - isCenter: true, - color: details.date == _calendarController.selectedDate ? AppColors.primaryRedColor : AppColors.textColor, - isEnglishOnly: true, - ), - ), - monthViewSettings: MonthViewSettings( - dayFormat: "EEE", - appointmentDisplayMode: MonthAppointmentDisplayMode.indicator, - showTrailingAndLeadingDates: false, - appointmentDisplayCount: 1, - monthCellStyle: MonthCellStyle( - textStyle: TextStyle(fontSize: 19.f), + viewHeaderStyle: ViewHeaderStyle( + // backgroundColor: AppColors.scaffoldBgColor, + dayTextStyle: TextStyle(fontSize: 14.f, fontWeight: FontWeight.w600, letterSpacing: -0.46, color: AppColors.textColor), ), + view: CalendarView.month, + todayHighlightColor: Colors.transparent, + todayTextStyle: TextStyle(color: AppColors.textColor, fontWeight: FontWeight.bold), + selectionDecoration: ShapeDecoration( + color: AppColors.transparent, + shape: SmoothRectangleBorder( + borderRadius: BorderRadius.circular(10.r), + smoothness: 1, + side: BorderSide(color: AppColors.primaryRedColor, width: 1.5), + ), + ), + cellBorderColor: AppColors.transparent, + dataSource: MeetingDataSource(_getDataSource()), + monthCellBuilder: (context, details) => Padding( + padding: EdgeInsets.all(12.h), + child: details.date.day.toString().toText14( + isCenter: true, + color: details.date == _calendarController.selectedDate ? AppColors.primaryRedColor : AppColors.textColor, + isEnglishOnly: true, + ), + ), + monthViewSettings: MonthViewSettings( + dayFormat: "EEE", + appointmentDisplayMode: MonthAppointmentDisplayMode.indicator, + showTrailingAndLeadingDates: false, + appointmentDisplayCount: 1, + monthCellStyle: MonthCellStyle( + textStyle: TextStyle(fontSize: 19.f), + ), + ), + onTap: (CalendarTapDetails details) { + _calendarController.selectedDate = details.date; + _onDaySelected(details.date!); + }, ), - onTap: (CalendarTapDetails details) { - _calendarController.selectedDate = details.date; - _onDaySelected(details.date!); - }, ), ), ), SizedBox(height: 10.h), Transform.translate( offset: const Offset(0.0, -10.0), - child: selectedDateDisplay.toText16(isBold: true, isEnglishOnly: true), + child: selectedDateDisplay.toText16(isBold: true, isEnglishOnly: !isArabic), ), //TODO: Add Next Day Span here dayEvents.isNotEmpty ? SizedBox( height: 100.h, - child: SingleChildScrollView( - scrollDirection: Axis.vertical, - child: Wrap( - direction: Axis.horizontal, - alignment: WrapAlignment.start, - spacing: 6.h, - runSpacing: 6.h, - children: List.generate( - dayEvents.length, // Generate a large number of items to ensure scrolling - (index) => TimeSlotChip( - label: dayEvents[index].isoTime!, - isSelected: index == selectedButtonIndex, - onTap: () { - setState(() { - selectedButtonIndex = index; - selectedTime = dayEvents[index].isoTime!; - }); - }, + child: Directionality( + textDirection: isArabic ? ui.TextDirection.rtl : ui.TextDirection.ltr, + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Wrap( + direction: Axis.horizontal, + alignment: WrapAlignment.start, + spacing: 6.h, + runSpacing: 6.h, + children: List.generate( + dayEvents.length, // Generate a large number of items to ensure scrolling + (index) => TimeSlotChip( + label: dayEvents[index].isoTime!, + isSelected: index == selectedButtonIndex, + onTap: () { + setState(() { + selectedButtonIndex = index; + selectedTime = dayEvents[index].isoTime!; + }); + }, + ), ), ), ), @@ -322,9 +331,12 @@ class _AppointmentCalendarState extends State { void _onDaySelected(DateTime day) { final DateFormat formatter = DateFormat('yyyy-MM-dd', "en-US"); + final isArabic = appState.isArabic(); setState(() { - selectedDateDisplay = DateUtil.getMonthDayYearDateFormatted(day); - selectedNextDate = DateUtil.getWeekDayMonthDayYearDateFormatted(day.add(Duration(days: 1)), "en"); + selectedDateDisplay = isArabic + ? DateUtil.getMonthDayYearDateFormattedAr(day) + : DateUtil.getMonthDayYearDateFormatted(day); + selectedNextDate = DateUtil.getWeekDayMonthDayYearDateFormatted(day.add(Duration(days: 1)), isArabic ? "ar" : "en"); _calendarController.selectedDate = day; openTimeSlotsPickerForDate(day, bookAppointmentsViewModel.docFreeSlots); selectedDate = formatter.format(day); diff --git a/lib/presentation/e_referral/new_e_referral.dart b/lib/presentation/e_referral/new_e_referral.dart index 61796dbd..8631fa62 100644 --- a/lib/presentation/e_referral/new_e_referral.dart +++ b/lib/presentation/e_referral/new_e_referral.dart @@ -104,10 +104,9 @@ class _NewReferralPageState extends State { duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, ); - // setState(() { - _currentStep++; - // }); - // widget.onStepChanged(_currentStep); + setState(() { + _currentStep++; + }); } void _submitReferral() { @@ -133,8 +132,10 @@ class _NewReferralPageState extends State { hmgServicesVM.createEReferral( requestModel: createReferralRequestModel, onSuccess: (GenericApiModel response) { - showSuccessBottomSheet(int.parse(response.data), hmgServicesVM); LoaderBottomSheet.hideLoader(); + showSuccessBottomSheet(int.parse(response.data), hmgServicesVM); + + }, onError: (errorMessage) { // Handle error (e.g., show error message) @@ -190,7 +191,8 @@ class _NewReferralPageState extends State { Row( mainAxisAlignment: MainAxisAlignment.center, children: List.generate(3, (index) { - if (_currentStep == index) { + // Fill current step and all previous completed steps + if (index <= _currentStep) { return StepperWidget(widthOfOneState, AppColors.primaryRedColor, true, 4.h); } else { return StepperWidget(widthOfOneState, AppColors.greyLightColor, false, 4.h); @@ -200,8 +202,8 @@ class _NewReferralPageState extends State { child: PageView( controller: _pageController, physics: const NeverScrollableScrollPhysics(), - onPageChanged: (index) => { - // setState(() => _currentStep = index) + onPageChanged: (index) { + setState(() => _currentStep = index); }, children: [ RequesterFormStep(), diff --git a/lib/presentation/e_referral/widget/e-referral_otp.dart b/lib/presentation/e_referral/widget/e-referral_otp.dart index 17ae4272..970dedaa 100644 --- a/lib/presentation/e_referral/widget/e-referral_otp.dart +++ b/lib/presentation/e_referral/widget/e-referral_otp.dart @@ -63,28 +63,12 @@ class OTPService { 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))); - }, + // Start listening for SMS before sending OTP + _startSmsListener( + context: context, + hmgServicesViewModel: hmgServicesViewModel, + formManager: formManager, + onSuccess: onSuccess, ); } }, @@ -100,4 +84,37 @@ class OTPService { )), ); } + + static void _startSmsListener({ + required BuildContext context, + required HmgServicesViewModel hmgServicesViewModel, + required ReferralFormManager formManager, + required Function onSuccess, + }) async { + LoaderBottomSheet.showLoader(); + + // Send OTP after starting listener + 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))); + }, + ); + } } diff --git a/lib/presentation/health_trackers/add_health_tracker_entry_page.dart b/lib/presentation/health_trackers/add_health_tracker_entry_page.dart index 89217aca..2d0356c3 100644 --- a/lib/presentation/health_trackers/add_health_tracker_entry_page.dart +++ b/lib/presentation/health_trackers/add_health_tracker_entry_page.dart @@ -159,14 +159,12 @@ class _AddHealthTrackerEntryPageState extends State { context, child: Utils.getSuccessWidget( loadingText: _getSuccessMessage(), - ), - callBackFunc: () { - Navigator.pop(context); - }, + ).paddingSymmetrical(0.h, 24.h), callBackFunc: () { + // Navigator.pop(context); + }, isCloseButtonVisible: false, isDismissible: true, - isFullScreen: false, - ); + isFullScreen: false, isAutoDismiss: true); } // Reusable method to build selection row widget diff --git a/lib/presentation/hmg_services/services_page.dart b/lib/presentation/hmg_services/services_page.dart index 680bf1c7..770a5cbe 100644 --- a/lib/presentation/hmg_services/services_page.dart +++ b/lib/presentation/hmg_services/services_page.dart @@ -430,6 +430,7 @@ class ServicesPage extends StatelessWidget { title: LandingPageData.getServiceCardsList[index].title, subtitle: LandingPageData.getServiceCardsList[index].subtitle, icon: LandingPageData.getServiceCardsList[index].largeCardIcon, + isPNG: LandingPageData.getServiceCardsList[index].isPNG, ), ), ), @@ -471,7 +472,7 @@ class ServicesPage extends StatelessWidget { Spacer(), getIt.get().isAuthenticated ? Consumer(builder: (context, habibWalletVM, child) { - return Utils.getPaymentAmountWithSymbol2(num.parse(NumberFormat.decimalPattern().format(habibWalletVM.habibWalletAmount)), isExpanded: false) + return Utils.getPaymentAmountWithSymbol2(num.parse(NumberFormat.decimalPattern().format(habibWalletVM.habibWalletAmount)), isExpanded: false, letterSpacing: -1) .toShimmer2(isShow: habibWalletVM.isWalletAmountLoading, radius: 12.r, width: 80.w, height: 24.h); }) : LocaleKeys.loginToViewWalletBalance.tr().toText12(isBold: true, maxLine: 2), @@ -635,73 +636,73 @@ class ServicesPage extends StatelessWidget { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row( - children: [ - Expanded( - child: Container( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 12.h, - hasShadow: false, - ), - child: Padding( - padding: EdgeInsets.all(16.h), - child: Row( - children: [ - Utils.buildSvgWithAssets( - icon: AppAssets.virtual_tour_icon, - width: 32.w, - height: 32.h, - fit: BoxFit.contain, - ), - SizedBox(width: 8.w), - LocaleKeys.virtualTour.tr().toText14(isBold: true) - ], - ), - ), - ).onPress(() { - Utils.openWebView( - url: 'https://hmgwebservices.com/vt_mobile/html/index.html', - ); - }), - ), - SizedBox(width: 16.w), - Expanded( - child: Container( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 12.h, - hasShadow: false, - ), - child: Padding( - padding: EdgeInsets.all(16.h), - child: Row( - children: [ - Utils.buildSvgWithAssets( - icon: AppAssets.car_parking_icon, - width: 32.w, - height: 32.h, - fit: BoxFit.contain, - ), - SizedBox(width: 8.w), - LocaleKeys.carParking.tr().toText14(isBold: true) - ], - ).onPress(() { - Navigator.push( - context, - MaterialPageRoute( - builder: (_) => ChangeNotifierProvider( - create: (_) => getIt(), - child: const ParkingPage(), - ), - ), - ); - }), - ), - ), - ), - ], - ), + // Row( + // children: [ + // // Expanded( + // // child: Container( + // // decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + // // color: AppColors.whiteColor, + // // borderRadius: 12.h, + // // hasShadow: false, + // // ), + // // child: Padding( + // // padding: EdgeInsets.all(16.h), + // // child: Row( + // // children: [ + // // Utils.buildSvgWithAssets( + // // icon: AppAssets.virtual_tour_icon, + // // width: 32.w, + // // height: 32.h, + // // fit: BoxFit.contain, + // // ), + // // SizedBox(width: 8.w), + // // LocaleKeys.virtualTour.tr().toText14(isBold: true) + // // ], + // // ), + // // ), + // // ).onPress(() { + // // Utils.openWebView( + // // url: 'https://hmgwebservices.com/vt_mobile/html/index.html', + // // ); + // // }), + // // ), + // SizedBox(width: 16.w), + // Expanded( + // child: Container( + // decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + // color: AppColors.whiteColor, + // borderRadius: 12.h, + // hasShadow: false, + // ), + // child: Padding( + // padding: EdgeInsets.all(16.h), + // child: Row( + // children: [ + // Utils.buildSvgWithAssets( + // icon: AppAssets.car_parking_icon, + // width: 32.w, + // height: 32.h, + // fit: BoxFit.contain, + // ), + // SizedBox(width: 8.w), + // LocaleKeys.carParking.tr().toText14(isBold: true) + // ], + // ).onPress(() { + // Navigator.push( + // context, + // MaterialPageRoute( + // builder: (_) => ChangeNotifierProvider( + // create: (_) => getIt(), + // child: const ParkingPage(), + // ), + // ), + // ); + // }), + // ), + // ), + // ), + // ], + // ), SizedBox(height: 16.h), Row( children: [ diff --git a/lib/presentation/home/data/landing_page_data.dart b/lib/presentation/home/data/landing_page_data.dart index 00142c23..a60a7c18 100644 --- a/lib/presentation/home/data/landing_page_data.dart +++ b/lib/presentation/home/data/landing_page_data.dart @@ -164,6 +164,7 @@ class LandingPageData { backgroundColor: AppColors.successColor, iconColor: AppColors.whiteColor, isBold: false, + isPNG: false ), // ServiceCardData( // icon: AppAssets.homeBottom, @@ -182,16 +183,18 @@ class LandingPageData { backgroundColor: AppColors.primaryRedColor, iconColor: AppColors.whiteColor, isBold: false, + isPNG: false ), ServiceCardData( serviceName: "pharmacy", - icon: AppAssets.pharmacy_icon, //359846 + icon: AppAssets.defaultMEP, //359846 title: LocaleKeys.hmgPharmacy, subtitle: LocaleKeys.hmgPharmacyText, largeCardIcon: AppAssets.pharmacyService, backgroundColor: AppColors.pharmacyBGColor, iconColor: null, isBold: true, + isPNG: true ), ]; } diff --git a/lib/presentation/home/data/service_card_data.dart b/lib/presentation/home/data/service_card_data.dart index c16147b1..f2f1d121 100644 --- a/lib/presentation/home/data/service_card_data.dart +++ b/lib/presentation/home/data/service_card_data.dart @@ -12,6 +12,7 @@ class ServiceCardData { final Color? backgroundColor; final bool isBold; final String largeCardIcon; + final bool isPNG; ServiceCardData({ this.serviceName = "", @@ -22,6 +23,7 @@ class ServiceCardData { this.backgroundColor, this.iconColor, this.textColor, - this.isBold = false + this.isBold = false, + this.isPNG = false, }); } diff --git a/lib/presentation/home/landing_page.dart b/lib/presentation/home/landing_page.dart index e61b6226..f103bd3f 100644 --- a/lib/presentation/home/landing_page.dart +++ b/lib/presentation/home/landing_page.dart @@ -204,7 +204,7 @@ class _LandingPageState extends State { // Refresh Appointments Data myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true); myAppointmentsViewModel.initAppointmentsViewModel(); - myAppointmentsViewModel.getPatientAppointments(true, true); + myAppointmentsViewModel.getPatientAppointments(true, false); // Refresh Appointments Data habibWalletVM.initHabibWalletProvider(); @@ -703,7 +703,7 @@ class _LandingPageState extends State { itemBuilder: (context, index) { return AnimationConfiguration.staggeredList( position: index, - duration: const Duration(milliseconds: 500), + duration: const Duration(milliseconds: 200), child: SlideAnimation( horizontalOffset: 100.0, child: FadeInAnimation( @@ -822,6 +822,7 @@ class _LandingPageState extends State { title: LandingPageData.getServiceCardsList[index].title, subtitle: LandingPageData.getServiceCardsList[index].subtitle, icon: LandingPageData.getServiceCardsList[index].largeCardIcon, + isPNG: LandingPageData.getServiceCardsList[index].isPNG, ), ), ), diff --git a/lib/presentation/home/widgets/large_service_card.dart b/lib/presentation/home/widgets/large_service_card.dart index fd6676f9..c36f7a90 100644 --- a/lib/presentation/home/widgets/large_service_card.dart +++ b/lib/presentation/home/widgets/large_service_card.dart @@ -28,6 +28,7 @@ class LargeServiceCard extends StatelessWidget { final String icon; final String title; final String subtitle; + final bool isPNG; const LargeServiceCard({ super.key, @@ -36,6 +37,7 @@ class LargeServiceCard extends StatelessWidget { this.icon = "", this.title = "", this.subtitle = "", + this.isPNG = false, }); @override @@ -72,7 +74,7 @@ class LargeServiceCard extends StatelessWidget { Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Container( + isPNG ? Image.asset(serviceCardData.icon, width: 35.h, height: 35.h) : Container( height: 48.h, width: 48.h, decoration: RoundedRectangleBorder().toSmoothCornerDecoration( @@ -172,6 +174,7 @@ class FadedLargeServiceCard extends StatelessWidget { final String icon; final String title; final String subtitle; + final bool isPNG; const FadedLargeServiceCard({ super.key, @@ -180,6 +183,7 @@ class FadedLargeServiceCard extends StatelessWidget { this.icon = "", this.title = "", this.subtitle = "", + this.isPNG = false, }); @override @@ -225,7 +229,7 @@ class FadedLargeServiceCard extends StatelessWidget { Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ - Container( + isPNG ? Image.asset(serviceCardData.icon, width: 32.h, height: 32.h).circle(100.h) : Container( height: 32.h, width: 32.h, decoration: RoundedRectangleBorder().toSmoothCornerDecoration( diff --git a/lib/presentation/medical_file/medical_file_page.dart b/lib/presentation/medical_file/medical_file_page.dart index 7bbfc936..89839f0d 100644 --- a/lib/presentation/medical_file/medical_file_page.dart +++ b/lib/presentation/medical_file/medical_file_page.dart @@ -34,6 +34,7 @@ import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_vie import 'package:hmg_patient_app_new/features/my_invoices/my_invoices_view_model.dart'; import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart'; import 'package:hmg_patient_app_new/features/todo_section/todo_section_view_model.dart'; +import 'package:hmg_patient_app_new/features/water_monitor/water_monitor_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/active_medication/active_medication_page.dart'; import 'package:hmg_patient_app_new/presentation/allergies/allergies_list_page.dart'; @@ -55,6 +56,8 @@ import 'package:hmg_patient_app_new/presentation/medical_file/eye_measurements_a import 'package:hmg_patient_app_new/presentation/medical_file/patient_sickleaves_list_page.dart'; import 'package:hmg_patient_app_new/presentation/medical_file/vaccine_list_page.dart'; import 'package:hmg_patient_app_new/presentation/medical_file/widgets/lab_rad_card.dart'; +import 'package:hmg_patient_app_new/presentation/medical_file/widgets/health_tracker_menu_card.dart'; +import 'package:hmg_patient_app_new/presentation/medical_file/widgets/health_tools_card.dart'; import 'package:hmg_patient_app_new/presentation/medical_file/widgets/medical_file_card.dart'; import 'package:hmg_patient_app_new/presentation/medical_file/widgets/patient_sick_leave_card.dart'; import 'package:hmg_patient_app_new/presentation/medical_report/medical_reports_page.dart'; @@ -524,7 +527,7 @@ class _MedicalFilePageState extends State { ], ), ExpandableListItem( - title: LocaleKeys.healthTrackers.tr(context: context).toText18(isBold: true), + title: LocaleKeys.healthTools.tr(context: context).toText18(isBold: true), expandedBackgroundColor: Colors.transparent, children: [ SizedBox(height: 10.h), @@ -979,7 +982,7 @@ class _MedicalFilePageState extends State { ), ).paddingSymmetrical(0.w, 0.h) : SizedBox( - height: 100.h, + height: 110.h, child: ListView.separated( scrollDirection: Axis.horizontal, itemCount: myAppointmentsVM.patientMyDoctorsList.length, @@ -1003,7 +1006,8 @@ class _MedicalFilePageState extends State { fit: BoxFit.cover, ).circle(100).toShimmer2(isShow: false, radius: 50.r), SizedBox(height: 8.h), - Expanded( + SizedBox( + width: 80.w, child: (myAppointmentsVM.patientMyDoctorsList[index].doctorName) .toString().toText12(isBold: true, isCenter: true, maxLine: 2).toShimmer2(isShow: false), ), @@ -1278,7 +1282,7 @@ class _MedicalFilePageState extends State { width: 62.w, height: 62.h, ), - ).paddingSymmetrical(0.w, 0.h); + ).paddingSymmetrical(0.w, 0.h); }), SizedBox(height: 16.h), GridView( @@ -1292,21 +1296,21 @@ class _MedicalFilePageState extends State { padding: EdgeInsets.zero, shrinkWrap: true, children: [ - MedicalFileCard( - label: LocaleKeys.monthlyReports.tr(context: context), - textColor: AppColors.blackColor, - backgroundColor: AppColors.whiteColor, - svgIcon: AppAssets.monthly_reports_icon, - isLargeText: true, - iconSize: 36.h, - ).onPress(() { - monthlyReportViewModel.setHealthSummaryEnabled(cacheService.getBool(key: CacheConst.isMonthlyReportEnabled) ?? false); - Navigator.of(context).push( - CustomPageRoute( - page: MonthlyReport(), - ), - ); - }), + // MedicalFileCard( + // label: LocaleKeys.monthlyReports.tr(context: context), + // textColor: AppColors.blackColor, + // backgroundColor: AppColors.whiteColor, + // svgIcon: AppAssets.monthly_reports_icon, + // isLargeText: true, + // iconSize: 36.h, + // ).onPress(() { + // monthlyReportViewModel.setHealthSummaryEnabled(cacheService.getBool(key: CacheConst.isMonthlyReportEnabled) ?? false); + // Navigator.of(context).push( + // CustomPageRoute( + // page: MonthlyReport(), + // ), + // ); + // }), MedicalFileCard( label: LocaleKeys.medicalReports.tr(context: context), textColor: AppColors.blackColor, @@ -1343,95 +1347,93 @@ class _MedicalFilePageState extends State { ], ); case 3: - // Trackers Tab Data + // Health Tools Tab Data return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - // Row( - // children: [ - // LocaleKeys.healthTrackers.tr(context: context).toText16(isBold: true, color: AppColors.textColor), - // ], - // ), - // SizedBox(height: 16.h), GridView( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 3, crossAxisSpacing: 10.h, mainAxisSpacing: 16.w, - mainAxisExtent: 115.h, + mainAxisExtent: 120.h, ), physics: NeverScrollableScrollPhysics(), - padding: EdgeInsets.zero, + padding: EdgeInsets.only(top: 12.h), shrinkWrap: true, children: [ - MedicalFileCard( - label: LocaleKeys.bloodSugar.tr(context: context), + // Health Trackers + HealthToolsCard( + label: LocaleKeys.healthTrackers.tr(context: context), textColor: AppColors.blackColor, - backgroundColor: AppColors.whiteColor, - svgIcon: AppAssets.blood_sugar_icon, - isLargeText: true, - iconSize: 36.w, - ).onPress(() => context.navigateWithName(AppRoutes.healthTrackerDetailPage, arguments: HealthTrackerTypeEnum.bloodSugar)), - MedicalFileCard( - label: LocaleKeys.bloodPressure.tr(context: context), + svgIcon: AppAssets.general_health, + iconColor:null, + iconSize: 24.w, + ).onPress(() { + if (getIt.get().isAuthenticated) { + context.navigateWithName(AppRoutes.healthTrackersPage); + } + }), + // Daily Water Monitor + HealthToolsCard( + label: LocaleKeys.dailyWaterMonitor.tr(context: context), textColor: AppColors.blackColor, - backgroundColor: AppColors.whiteColor, - svgIcon: AppAssets.lab_result_icon, - isLargeText: true, - iconSize: 36.w, - ).onPress(() => context.navigateWithName(AppRoutes.healthTrackerDetailPage, arguments: HealthTrackerTypeEnum.bloodPressure)), - MedicalFileCard( - label: LocaleKeys.weightTracker.tr(context: context), + svgIcon: AppAssets.daily_water_monitor_icon, + iconColor: AppColors.infoColor, + iconSize: 24.w, + ).onPress(() async { + if (getIt.get().isAuthenticated) { + LoaderBottomSheet.showLoader(loadingText: LocaleKeys.fetchingYourWaterIntakeDetails.tr()); + final waterMonitorVM = getIt.get(); + await waterMonitorVM.fetchUserDetailsForMonitoring( + onSuccess: (userDetail) { + LoaderBottomSheet.hideLoader(); + if (userDetail == null) { + waterMonitorVM.populateFromAuthenticatedUser(); + context.navigateWithName(AppRoutes.waterMonitorSettingsPage); + } else { + context.navigateWithName(AppRoutes.waterConsumptionPage); + } + }, + onError: (error) { + LoaderBottomSheet.hideLoader(); + context.navigateWithName(AppRoutes.waterConsumptionPage); + }, + ); + } + }), + // Health Calculators + HealthToolsCard( + label: LocaleKeys.healthCalculatorsServices.tr(context: context), textColor: AppColors.blackColor, - backgroundColor: AppColors.whiteColor, - svgIcon: AppAssets.weight_tracker_icon, - isLargeText: true, - iconSize: 36.w, - ).onPress(() => context.navigateWithName(AppRoutes.healthTrackerDetailPage, arguments: HealthTrackerTypeEnum.weightTracker)), - ], - ).paddingSymmetrical(0.w, 0.0), - SizedBox(height: 16.h), - // Row( - // children: [ - // LocaleKeys.others.tr(context: context).toText16(isBold: true, color: AppColors.textColor), - // ], - // ), - SizedBox(height: 16.h), - GridView( - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 3, - crossAxisSpacing: 10.h, - mainAxisSpacing: 16.w, - mainAxisExtent: 115.h, - ), - physics: NeverScrollableScrollPhysics(), - padding: EdgeInsets.zero, - shrinkWrap: true, - children: [ - // MedicalFileCard( - // label: LocaleKeys.askYourDoctor.tr(context: context), - // textColor: AppColors.blackColor, - // backgroundColor: AppColors.whiteColor, - // svgIcon: AppAssets.ask_doctor_medical_file_icon, - // isLargeText: true, - // iconSize: 36.w, - // ).onPress(() { - // getIt.get().initAskDoctorViewModel(); - // Navigator.of(context).push( - // CustomPageRoute( - // page: AskDoctorPage(), - // ), - // ); - // }), - - // MedicalFileCard( - // label: LocaleKeys.internetPairing.tr(context: context), - // textColor: AppColors.blackColor, - // backgroundColor: AppColors.whiteColor, - // svgIcon: AppAssets.internet_pairing_icon, - // isLargeText: true, - // iconSize: 36.w, - // ).onPress(() {}), + svgIcon: AppAssets.health_calculators_services_icon, + iconColor: AppColors.successColor, + iconSize: 24.w, + ).onPress(() { + context.navigateWithName(AppRoutes.healthCalculatorsPage); + }), + // Health Converters + HealthToolsCard( + label: LocaleKeys.healthConvertersServices.tr(context: context), + textColor: AppColors.blackColor, + svgIcon: AppAssets.health_converters_icon, + iconColor: AppColors.primaryRedColor, + iconSize: 24.w, + ).onPress(() { + context.navigateWithName(AppRoutes.healthConvertersPage); + }), + // Smart Watches + HealthToolsCard( + label: LocaleKeys.smartWatchesServices.tr(context: context), + textColor: AppColors.blackColor, + svgIcon: AppAssets.smartwatch_icon, + iconColor: AppColors.warningColorYellow, + iconSize: 24.w, + ).onPress(() { + if (getIt.get().isAuthenticated) { + context.navigateWithName(AppRoutes.smartWatches); + } + }), ], ).paddingSymmetrical(0.w, 0.0), SizedBox(height: 24.h), @@ -1535,7 +1537,7 @@ class _MedicalFilePageState extends State { child: _buildVitalSignCard( icon: AppAssets.bmiVital, label: LocaleKeys.bmi.tr(context: context), - value: vitalSign.bodyMassIndex?.toString() ?? '--', + value: _formatValue(vitalSign.bodyMassIndex), unit: '', status: vitalSign.bodyMassIndex != null ? _getBMIStatus(vitalSign.bodyMassIndex) : null, // status: "Overweight", @@ -1547,7 +1549,7 @@ class _MedicalFilePageState extends State { child: _buildVitalSignCard( icon: AppAssets.heightVital, label: LocaleKeys.height.tr(context: context), - value: vitalSign.heightCm?.toString() ?? '--', + value: _formatValue(vitalSign.heightCm), unit: 'cm', status: null, onTap: onTap, @@ -1565,7 +1567,7 @@ class _MedicalFilePageState extends State { child: _buildVitalSignCard( icon: AppAssets.weightVital, label: LocaleKeys.weight.tr(context: context), - value: vitalSign.weightKg?.toString() ?? '--', + value: _formatValue(vitalSign.weightKg), unit: 'kg', status: vitalSign.weightKg != null ? _getWeightStatus(vitalSign.bodyMassIndex) : null, onTap: onTap, @@ -1576,12 +1578,18 @@ class _MedicalFilePageState extends State { child: _buildVitalSignCard( icon: AppAssets.bloodPressure, label: LocaleKeys.bloodPressure.tr(context: context), - value: vitalSign.bloodPressureLower != null && vitalSign.bloodPressureHigher != null ? "${vitalSign.bloodPressureHigher}/${vitalSign.bloodPressureLower}" : '--', + value: (vitalSign.bloodPressureLower != null && vitalSign.bloodPressureHigher != null && + vitalSign.bloodPressureLower != 0 && vitalSign.bloodPressureHigher != 0) + ? "${vitalSign.bloodPressureHigher}/${vitalSign.bloodPressureLower}" + : '--', unit: '', - status: _getBloodPressureStatus( - systolic: vitalSign.bloodPressureHigher, - diastolic: vitalSign.bloodPressureLower, - ), + status: (vitalSign.bloodPressureLower != null && vitalSign.bloodPressureHigher != null && + vitalSign.bloodPressureLower != 0 && vitalSign.bloodPressureHigher != 0) + ? _getBloodPressureStatus( + systolic: vitalSign.bloodPressureHigher, + diastolic: vitalSign.bloodPressureLower, + ) + : null, onTap: onTap, ), ), @@ -1590,25 +1598,36 @@ class _MedicalFilePageState extends State { ]; } - String _getBMIStatus(dynamic bmi) { + VitalSignStatus? _getBMIStatus(dynamic bmi) { return VitalSignUiModel.bmiStatus(bmi); } - String? _getWeightStatus(dynamic bmi) { - if (bmi == null) return 'Normal'; - return VitalSignUiModel.bmiStatus(bmi); + VitalSignStatus? _getWeightStatus(dynamic bmi) { + return VitalSignUiModel.weightStatus(bmi); } - String? _getBloodPressureStatus({dynamic systolic, dynamic diastolic}) { + VitalSignStatus? _getBloodPressureStatus({dynamic systolic, dynamic diastolic}) { return VitalSignUiModel.bloodPressureStatus(systolic: systolic, diastolic: diastolic); } + // Helper function to format vital sign values, showing '--' instead of 0 or null + String _formatValue(dynamic value) { + if (value == null) return '--'; + if (value is num && value == 0) return '--'; + if (value is String) { + if (value.trim().isEmpty) return '--'; + final parsed = double.tryParse(value); + if (parsed == null || parsed == 0) return '--'; + } + return value.toString(); + } + Widget _buildVitalSignCard({ required String icon, required String label, required String value, required String unit, - required String? status, + required VitalSignStatus? status, required VoidCallback onTap, }) { final VitalSignUiModel scheme = VitalSignUiModel.scheme(status: status, label: label); @@ -1675,7 +1694,7 @@ class _MedicalFilePageState extends State { isEnglishOnly: true ), ), - if (unit.isNotEmpty) ...[ + if (unit.isNotEmpty && value != '--' && value != '0') ...[ SizedBox(width: 3.w), unit.toText12( color: AppColors.textColor, @@ -1688,7 +1707,7 @@ class _MedicalFilePageState extends State { if (status != null) ...[ SizedBox(width: 4.w), AppCustomChipWidget( - labelText: status, + labelText: VitalSignUiModel.statusToString(status), backgroundColor: scheme.chipBg, textColor: scheme.chipFg, ), diff --git a/lib/presentation/medical_file/patient_sickleaves_list_page.dart b/lib/presentation/medical_file/patient_sickleaves_list_page.dart index dd7942d5..42d94ce7 100644 --- a/lib/presentation/medical_file/patient_sickleaves_list_page.dart +++ b/lib/presentation/medical_file/patient_sickleaves_list_page.dart @@ -169,8 +169,8 @@ class _PatientSickleavesListPageState extends State { richText: Row( mainAxisSize: MainAxisSize.min, children: [ - "${model.patientSickLeaveList[index].sickLeaveDays} ".toText10(isEnglishOnly: true), - LocaleKeys.days.tr(context: context).toText10() + "${model.patientSickLeaveList[index].sickLeaveDays} ".toText10(isBold: true), + LocaleKeys.days.tr(context: context).toText10(isBold: true) ], ), // labelText: "${model.patientSickLeaveList[index].sickLeaveDays} ${LocaleKeys.days.tr(context: context)}", diff --git a/lib/presentation/medical_file/vaccine_list_page.dart b/lib/presentation/medical_file/vaccine_list_page.dart index bf02cb74..06f1a6d5 100644 --- a/lib/presentation/medical_file/vaccine_list_page.dart +++ b/lib/presentation/medical_file/vaccine_list_page.dart @@ -29,11 +29,19 @@ class _VaccineListPageState extends State { @override void initState() { + super.initState(); scheduleMicrotask(() { + medicalFileViewModel = Provider.of(context, listen: false); medicalFileViewModel.setIsPatientVaccineListLoading(true); - medicalFileViewModel.getPatientVaccinesList(); + medicalFileViewModel.getPatientVaccinesList( + onSuccess: (data) { + // print("✅ Vaccine data received: ${medicalFileViewModel.patientVaccineList.length} items"); + }, + onError: (error) { + //print("❌ Vaccine error: $error"); + }, + ); }); - super.initState(); } @override @@ -133,11 +141,18 @@ class _VaccineListPageState extends State { // height: 63.h, // fit: BoxFit.fill, // ).circle(100).toShimmer2(isShow: false), - SizedBox(width: 16.h), + + SizedBox(width: 8.h), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ + AppCustomChipWidget( + backgroundColor: AppColors.lightGreenButtonColor, + textColor: AppColors.textGreenColor, + padding: EdgeInsets.all(0), + labelText: medicalFileVM.patientVaccineList[index].vaccineName).toShimmer2(isShow: false, width: 16.h, ), + SizedBox(height:10.h), (medicalFileVM.patientVaccineList[index].doctorName).toString().toText16(isBold: true).toShimmer2(isShow: false), SizedBox(height: 8.h), Wrap( @@ -148,7 +163,7 @@ class _VaccineListPageState extends State { AppCustomChipWidget( icon: AppAssets.doctor_calendar_icon, labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(medicalFileVM.patientVaccineList[index].vaccinationDate), false)), - AppCustomChipWidget(labelText: medicalFileVM.patientVaccineList[index].vaccineName).toShimmer2(isShow: false, width: 16.h), + AppCustomChipWidget(labelText: medicalFileVM.patientVaccineList[index].clinicName).toShimmer2(isShow: false, width: 16.h), AppCustomChipWidget(labelText: medicalFileVM.patientVaccineList[index].projectName).toShimmer2(isShow: false, width: 16.h), ], diff --git a/lib/presentation/medical_file/widgets/health_tools_card.dart b/lib/presentation/medical_file/widgets/health_tools_card.dart new file mode 100644 index 00000000..a66d2471 --- /dev/null +++ b/lib/presentation/medical_file/widgets/health_tools_card.dart @@ -0,0 +1,75 @@ +import 'package:flutter/material.dart'; +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/theme/colors.dart'; + +class HealthToolsCard extends StatelessWidget { + final String label; + final Color textColor; + final String svgIcon; + final double? iconSize; + final Color? iconColor; + + const HealthToolsCard({ + super.key, + required this.label, + required this.textColor, + required this.svgIcon, + this.iconSize, + this.iconColor, + }); + + @override + Widget build(BuildContext context) { + final iconS = iconSize ?? 24.w; + + return Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 20.r, + hasShadow: false, + ), + padding: EdgeInsets.all(12.w), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Icon container with white background and border + Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 12.r, + side: BorderSide( + color: AppColors.borderOnlyColor.withValues(alpha: 0.1), + width: 1, + ), + ), + height: 48.w, + width: 48.w, + child: Center( + child: Utils.buildSvgWithAssets( + icon: svgIcon, + width: iconS, + height: iconS, + fit: BoxFit.contain, + applyThemeColor: false, + iconColor: iconColor, + ), + ), + ), + SizedBox(height: 6.h), + label.toText13(color: textColor, isBold: true, maxLine: 2), + ], + ), + ], + ), + ); + } +} + diff --git a/lib/presentation/medical_file/widgets/health_tracker_menu_card.dart b/lib/presentation/medical_file/widgets/health_tracker_menu_card.dart new file mode 100644 index 00000000..6284a827 --- /dev/null +++ b/lib/presentation/medical_file/widgets/health_tracker_menu_card.dart @@ -0,0 +1,86 @@ +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/app_state.dart'; +import 'package:hmg_patient_app_new/core/dependencies.dart'; +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/theme/colors.dart'; + +class HealthTrackerMenuCard extends StatelessWidget { + final String label; + final Color textColor; + final Color backgroundColor; + final String svgIcon; + final double? iconSize; + final Color? iconColor; + final Color iconBgColor; + + const HealthTrackerMenuCard({ + super.key, + required this.label, + required this.textColor, + required this.backgroundColor, + required this.svgIcon, + required this.iconBgColor, + this.iconSize, + this.iconColor, + }); + + @override + Widget build(BuildContext context) { + final iconS = iconSize ?? 22.w; + return Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: backgroundColor, + borderRadius: 20.r, + hasShadow: false + ), + padding: EdgeInsets.all(16.w), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: iconBgColor, + borderRadius: 10.r + ), + height: 40.w, + width: 40.w, + child: Utils.buildSvgWithAssets( + icon: svgIcon, + fit: BoxFit.none, + height: iconS, + width: iconS, + applyThemeColor: false, + iconColor: iconColor, + ), + ), + SizedBox(width: 12.w), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + label.toText14(color: textColor, isBold: true), + ], + ), + ), + SizedBox(width: 12.w), + Transform.flip( + flipX: getIt.get().isArabic(), + child: Utils.buildSvgWithAssets( + icon: AppAssets.arrowRight, + width: 24.w, + height: 24.h, + fit: BoxFit.contain, + iconColor: AppColors.textColor, + ), + ), + ], + ), + ); + } +} + diff --git a/lib/presentation/medical_file/widgets/medical_file_appointment_card.dart b/lib/presentation/medical_file/widgets/medical_file_appointment_card.dart index 51e0cc15..13ea39dd 100644 --- a/lib/presentation/medical_file/widgets/medical_file_appointment_card.dart +++ b/lib/presentation/medical_file/widgets/medical_file_appointment_card.dart @@ -108,7 +108,7 @@ class MedicalFileAppointmentCard extends StatelessWidget { borderColor: AppointmentType.getNextActionButtonColor(patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.01), textColor: AppointmentType.getNextActionTextColor(patientAppointmentHistoryResponseModel.nextAction), fontSize: 14.f, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w600, borderRadius: 12.r, padding: EdgeInsets.symmetric(horizontal: 10.w), height: 40.h, @@ -118,8 +118,13 @@ class MedicalFileAppointmentCard extends StatelessWidget { ).toShimmer2(isShow: myAppointmentsViewModel.isMyAppointmentsLoading), ), SizedBox(width: 8.w), - Expanded( - flex: 2, + ((((patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false) || + (patientAppointmentHistoryResponseModel.isExecludeDoctor ?? false) || + !Utils.isClinicAllowedForRebook(patientAppointmentHistoryResponseModel.clinicID ?? 0))) && + AppointmentType.isArrived(patientAppointmentHistoryResponseModel)) + ? SizedBox.shrink() + : Expanded( + flex: 2, child: Container( height: 40.h, width: 40.w, @@ -163,40 +168,75 @@ class MedicalFileAppointmentCard extends StatelessWidget { } Widget getArrivedAppointmentButton(BuildContext context) { - return DateTime.now().difference(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate)).inDays <= 15 - ? CustomButton( - text: LocaleKeys.askDoctor.tr(context: context), - onPressed: () { - onAskDoctorTap(); - }, - backgroundColor: AppColors.secondaryLightRedColor, - borderColor: AppColors.secondaryLightRedColor, - textColor: AppColors.primaryRedColor, - fontSize: 14.f, - fontWeight: FontWeight.w600, - borderRadius: 12.r, - padding: EdgeInsets.symmetric(horizontal: 10.w), - height: 40.h, - icon: AppAssets.ask_doctor_icon, - iconColor: AppColors.primaryRedColor, - iconSize: 16.h, + if ((((patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false) || + (patientAppointmentHistoryResponseModel.isExecludeDoctor ?? false) || + !Utils.isClinicAllowedForRebook(patientAppointmentHistoryResponseModel.clinicID ?? 0))) && + AppointmentType.isArrived(patientAppointmentHistoryResponseModel)) { + return CustomButton( + text: LocaleKeys.viewDetails.tr(context: context), + onPressed: () { + Navigator.of(context) + .push( + CustomPageRoute( + page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel), + ), ) - : CustomButton( - text: LocaleKeys.rebook.tr(context: context), - onPressed: () { - onRescheduleTap(); - }, - backgroundColor: AppColors.greyColor, - borderColor: AppColors.greyColor, - textColor: AppColors.blackColor, - fontSize: 14.f, - fontWeight: FontWeight.w600, - borderRadius: 12.r, - padding: EdgeInsets.symmetric(horizontal: 10.w), - height: 40.h, - icon: AppAssets.rebook_appointment_icon, - iconColor: AppColors.blackColor, - iconSize: 16.h, - ); + .then((_) { + myAppointmentsViewModel.initAppointmentsViewModel(); + myAppointmentsViewModel.getPatientAppointments(true, false); + }); + }, + backgroundColor: AppColors.secondaryLightRedColor, + borderColor: AppColors.secondaryLightRedColor, + textColor: AppColors.primaryRedColor, + fontSize: (isFoldable || isTablet) ? 12.f : 14.f, + fontWeight: FontWeight.w600, + borderRadius: 12.r, + padding: EdgeInsets.symmetric(horizontal: 10.w), + // height: isTablet || isFoldable ? 46.h : 40.h, + height: 40.h, + icon: null, + iconColor: AppColors.primaryRedColor, + iconSize: 16.h, + ); + } else { + return + // DateTime.now().difference(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate)).inDays <= 15 + // ? CustomButton( + // text: LocaleKeys.askDoctor.tr(context: context), + // onPressed: () { + // onAskDoctorTap(); + // }, + // backgroundColor: AppColors.secondaryLightRedColor, + // borderColor: AppColors.secondaryLightRedColor, + // textColor: AppColors.primaryRedColor, + // fontSize: 14.f, + // fontWeight: FontWeight.w600, + // borderRadius: 12.r, + // padding: EdgeInsets.symmetric(horizontal: 10.w), + // height: 40.h, + // icon: AppAssets.ask_doctor_icon, + // iconColor: AppColors.primaryRedColor, + // iconSize: 16.h, + // ) + // : + CustomButton( + text: LocaleKeys.rebook.tr(context: context), + onPressed: () { + onRescheduleTap(); + }, + backgroundColor: AppColors.greyColor, + borderColor: AppColors.greyColor, + textColor: AppColors.blackColor, + fontSize: 14.f, + fontWeight: FontWeight.w600, + borderRadius: 12.r, + padding: EdgeInsets.symmetric(horizontal: 10.w), + height: 40.h, + icon: AppAssets.rebook_appointment_icon, + iconColor: AppColors.blackColor, + iconSize: 16.h, + ); + } } } diff --git a/lib/presentation/medical_file/widgets/medical_file_card.dart b/lib/presentation/medical_file/widgets/medical_file_card.dart index 40ae17d4..a79d8aa9 100644 --- a/lib/presentation/medical_file/widgets/medical_file_card.dart +++ b/lib/presentation/medical_file/widgets/medical_file_card.dart @@ -64,3 +64,5 @@ class MedicalFileCard extends StatelessWidget { ); } } + + diff --git a/lib/presentation/medical_report/medical_reports_page.dart b/lib/presentation/medical_report/medical_reports_page.dart index b8fe4369..f1b55e02 100644 --- a/lib/presentation/medical_report/medical_reports_page.dart +++ b/lib/presentation/medical_report/medical_reports_page.dart @@ -419,7 +419,7 @@ class _MedicalReportsPageState extends State { callBackFunc: () {}, isFullScreen: false, isCloseButtonVisible: true, - isAutoDismiss: true + isAutoDismiss: false ); } } diff --git a/lib/presentation/prescriptions/prescription_detail_page.dart b/lib/presentation/prescriptions/prescription_detail_page.dart index b3d37664..715b6e4d 100644 --- a/lib/presentation/prescriptions/prescription_detail_page.dart +++ b/lib/presentation/prescriptions/prescription_detail_page.dart @@ -251,6 +251,9 @@ class _PrescriptionDetailPageState extends State { await prescriptionsViewModel.getPrescriptionDetails(widget.prescriptionsResponseModel, onSuccess: (val) { LoaderBottomSheet.hideLoader(); prescriptionsViewModel.initiatePrescriptionDelivery(); + }, onError: (err) { + LoaderBottomSheet.hideLoader(); + print(err); }); } }, diff --git a/lib/presentation/rate_appointment/rate_appointment_doctor.dart b/lib/presentation/rate_appointment/rate_appointment_doctor.dart index 0020624c..9a3925a1 100644 --- a/lib/presentation/rate_appointment/rate_appointment_doctor.dart +++ b/lib/presentation/rate_appointment/rate_appointment_doctor.dart @@ -193,20 +193,15 @@ class _RateAppointmentDoctorState extends State { Expanded( child: CustomButton( text: LocaleKeys.submit.tr(context: context), - onPressed: () async { - // Set up clinic rating and show clinic rating view - // appointmentRatingViewModel!.setTitle(LocaleKeys.rateDoctor.tr(context: context),); - // appointmentRatingViewModel!.setSubTitle(LocaleKeys.howWasYourLastVisitWithDoctor.tr(context: context),); - // appointmentRatingViewModel!.setClinicOrDoctor(true); - - LoaderBottomSheet.showLoader(); - await appointmentRatingViewModel!.submitDoctorRating(docRate: rating, docNote: note); - // await appointmentRatingViewModel!.submitClinicRating(clinicRate: rating, clinicNote: note); - LoaderBottomSheet.hideLoader(); - Navigator.pop(context); - - setState(() {}); - }, + onPressed: rating > 0 + ? () { + LoaderBottomSheet.showLoader(); + appointmentRatingViewModel!.submitDoctorRating(docRate: rating, docNote: note).then((_) { + LoaderBottomSheet.hideLoader(); + Navigator.pop(context); + }); + } + : null, ), ), ], diff --git a/lib/presentation/vital_sign/vital_sign_details_page.dart b/lib/presentation/vital_sign/vital_sign_details_page.dart index ff1daaf9..eb137e47 100644 --- a/lib/presentation/vital_sign/vital_sign_details_page.dart +++ b/lib/presentation/vital_sign/vital_sign_details_page.dart @@ -2,7 +2,9 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:fl_chart/fl_chart.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/common_models/data_points.dart'; +import 'package:hmg_patient_app_new/core/dependencies.dart'; 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'; @@ -119,7 +121,7 @@ class _VitalSignDetailsPageState extends State { required String title, required String icon, required String valueText, - required String? status, + required VitalSignStatus? status, required VitalSignUiModel scheme, required DateTime? latestDate, }) { @@ -168,17 +170,20 @@ class _VitalSignDetailsPageState extends State { ), SizedBox(width: 4.h), if (status != null) - Column( - spacing: 6.h, - children: [ - status.toText10(isBold: true, color: AppColors.greyTextColor), - Utils.buildSvgWithAssets( - icon: AppAssets.lab_result_indicator, - width: 21, - height: 23, - iconColor: scheme.iconFg, - ), - ], + Directionality( + textDirection: ui.TextDirection.ltr, + child: Column( + spacing: 6.h, + children: [ + VitalSignUiModel.statusToString(status).toText10(isBold: true, color: AppColors.greyTextColor), + Utils.buildSvgWithAssets( + icon: AppAssets.lab_result_indicator, + width: 21, + height: 23, + iconColor: scheme.iconFg, + ), + ], + ), ), ], ), @@ -331,6 +336,7 @@ class _VitalSignDetailsPageState extends State { final minY = _minY(history, secondaryHistory: secondaryHistory); final maxY = _maxY(history, secondaryHistory: secondaryHistory); final scheme = VitalSignUiModel.scheme(status: _statusForLatest(null), label: args.title); + final isRTL = getIt.get().isArabic(); return CustomGraph( dataPoints: history, @@ -339,6 +345,7 @@ class _VitalSignDetailsPageState extends State { leftLabelReservedSize: 40, showGridLines: true, showShadow: true, + isRTL: isRTL, leftLabelInterval: _leftInterval(history, secondaryHistory: secondaryHistory), maxY: maxY, minY: minY, @@ -663,7 +670,7 @@ class _VitalSignDetailsPageState extends State { } } - String? _statusForLatest(VitalSignResModel? latest) { + VitalSignStatus? _statusForLatest(VitalSignResModel? latest) { if (latest == null) return null; switch (args.metric) { @@ -674,14 +681,15 @@ class _VitalSignDetailsPageState extends State { case VitalSignMetric.height: return null; case VitalSignMetric.weight: - // return latest.weightKg != null ? 'Normal' : null; - return VitalSignUiModel.bmiStatus(latest.bodyMassIndex); + return VitalSignUiModel.weightStatus(latest.bodyMassIndex); case VitalSignMetric.temperature: return null; case VitalSignMetric.heartRate: - return (latest.heartRate ?? latest.pulseBeatPerMinute) != null ? 'Normal' : null; + final heartRateValue = _toDouble(latest.heartRate ?? latest.pulseBeatPerMinute); + return (heartRateValue != null && heartRateValue > 0) ? VitalSignStatus.normal : null; case VitalSignMetric.respiratoryRate: - return latest.respirationBeatPerMinute != null ? 'Normal' : null; + final respiratoryValue = _toDouble(latest.respirationBeatPerMinute); + return (respiratoryValue != null && respiratoryValue > 0) ? VitalSignStatus.normal : null; } } @@ -756,9 +764,9 @@ class _VitalSignDetailsPageState extends State { Widget _bottomLabel(String label, {bool isLast = false}) { return Padding( - padding: EdgeInsets.only( + padding: EdgeInsetsDirectional.only( top: 8.0, - right: isLast ? 16.h : 0, + end: isLast ? 16.h : 0, ), child: label.toText10(isBold: true, isEnglishOnly: true), ); diff --git a/lib/presentation/vital_sign/vital_sign_page.dart b/lib/presentation/vital_sign/vital_sign_page.dart index 2e6e98ed..f712aa77 100644 --- a/lib/presentation/vital_sign/vital_sign_page.dart +++ b/lib/presentation/vital_sign/vital_sign_page.dart @@ -41,6 +41,18 @@ class _VitalSignPageState extends State { return false; } + // Helper function to format vital sign values, showing '--' instead of 0 or null + String _formatValue(dynamic value) { + if (value == null) return '--'; + if (value is num && value == 0) return '--'; + if (value is String) { + if (value.trim().isEmpty) return '--'; + final parsed = double.tryParse(value); + if (parsed == null || parsed == 0) return '--'; + } + return value.toString(); + } + void _openDetails(VitalSignDetailsArgs args) { Navigator.of(context).push( CustomPageRoute( @@ -100,7 +112,7 @@ class _VitalSignPageState extends State { _buildVitalSignCard( icon: AppAssets.bmiVital, label: LocaleKeys.bmi.tr(context: context), - value: latestVitalSign?.bodyMassIndex?.toString() ?? '--', + value: _formatValue(latestVitalSign?.bodyMassIndex), unit: '', status: VitalSignUiModel.bmiStatus(latestVitalSign?.bodyMassIndex), onTap: () => _openDetails( @@ -118,7 +130,7 @@ class _VitalSignPageState extends State { _buildVitalSignCard( icon: AppAssets.heightVital, label: LocaleKeys.height.tr(context: context), - value: latestVitalSign?.heightCm?.toString() ?? '--', + value: _formatValue(latestVitalSign?.heightCm), unit: 'cm', status: null, onTap: () => _openDetails( @@ -136,10 +148,10 @@ class _VitalSignPageState extends State { _buildVitalSignCard( icon: AppAssets.weightVital, label: LocaleKeys.weight.tr(context: context), - value: latestVitalSign?.weightKg?.toString() ?? '--', + value: _formatValue(latestVitalSign?.weightKg), unit: 'kg', status: (latestVitalSign?.weightKg != null) - ? VitalSignUiModel.bmiStatus(latestVitalSign?.bodyMassIndex) + ? VitalSignUiModel.weightStatus(latestVitalSign?.bodyMassIndex) : null, onTap: () => _openDetails( VitalSignDetailsArgs( @@ -162,10 +174,14 @@ class _VitalSignPageState extends State { ? '${latestVitalSign.bloodPressureHigher}/${latestVitalSign.bloodPressureLower}' : '--', unit: '', - status: VitalSignUiModel.bloodPressureStatus( - systolic: latestVitalSign?.bloodPressureHigher, - diastolic: latestVitalSign?.bloodPressureLower, - ), + status: (latestVitalSign != null && + _isValidValue(latestVitalSign.bloodPressureHigher) && + _isValidValue(latestVitalSign.bloodPressureLower)) + ? VitalSignUiModel.bloodPressureStatus( + systolic: latestVitalSign.bloodPressureHigher, + diastolic: latestVitalSign.bloodPressureLower, + ) + : null, onTap: () => _openDetails( VitalSignDetailsArgs( metric: VitalSignMetric.bloodPressure, @@ -183,7 +199,7 @@ class _VitalSignPageState extends State { _buildVitalSignCard( icon: AppAssets.temperature, label: LocaleKeys.temperature.tr(context: context), - value: latestVitalSign?.temperatureCelcius?.toString() ?? '--', + value: _formatValue(latestVitalSign?.temperatureCelcius), unit: '°C', status: null, onTap: () => _openDetails( @@ -258,9 +274,9 @@ class _VitalSignPageState extends State { child: _buildVitalSignCard( icon: AppAssets.heart, label: LocaleKeys.heart.tr(context: context), - value: latestVitalSign?.heartRate?.toString() ?? latestVitalSign?.pulseBeatPerMinute?.toString() ?? '--', + value: _formatValue(latestVitalSign?.heartRate ?? latestVitalSign?.pulseBeatPerMinute), unit: 'bpm', - status: 'Normal', + status: _isValidValue(latestVitalSign?.heartRate ?? latestVitalSign?.pulseBeatPerMinute) ? VitalSignStatus.normal : null, onTap: () => _openDetails( VitalSignDetailsArgs( metric: VitalSignMetric.heartRate, @@ -282,9 +298,9 @@ class _VitalSignPageState extends State { _buildVitalSignCard( icon: AppAssets.respRate, label: LocaleKeys.respirationRate.tr(context: context), - value: latestVitalSign?.respirationBeatPerMinute?.toString() ?? '--', + value: _formatValue(latestVitalSign?.respirationBeatPerMinute), unit: 'bpm', - status: 'Normal', + status: _isValidValue(latestVitalSign?.respirationBeatPerMinute) ? VitalSignStatus.normal : null, onTap: () => _openDetails( VitalSignDetailsArgs( metric: VitalSignMetric.respiratoryRate, @@ -318,7 +334,7 @@ class _VitalSignPageState extends State { required String label, required String value, required String unit, - required String? status, + required VitalSignStatus? status, required VoidCallback onTap, }) { final VitalSignUiModel scheme = VitalSignUiModel.scheme(status: status, label: label); @@ -387,7 +403,7 @@ class _VitalSignPageState extends State { color: AppColors.textColor, isEnglishOnly: true ), - if (unit.isNotEmpty) ...[ + if (unit.isNotEmpty && value != '--' && value != '0') ...[ SizedBox(width: 3.w), unit.toText12( color: AppColors.textColor, @@ -398,7 +414,7 @@ class _VitalSignPageState extends State { ), if (status != null) AppCustomChipWidget( - labelText: status, + labelText: VitalSignUiModel.statusToString(status), backgroundColor: scheme.chipBg, textColor: scheme.chipFg, diff --git a/lib/widgets/bottomsheet/generic_bottom_sheet.dart b/lib/widgets/bottomsheet/generic_bottom_sheet.dart index 21cc076f..a161c35f 100644 --- a/lib/widgets/bottomsheet/generic_bottom_sheet.dart +++ b/lib/widgets/bottomsheet/generic_bottom_sheet.dart @@ -1,4 +1,5 @@ import 'dart:io'; +import 'dart:ui' as ui; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; @@ -82,7 +83,7 @@ class GenericBottomSheetState extends State { FocusScope.of(context).unfocus(); }, child: Directionality( - textDirection: Directionality.of(context), + textDirection: ui.TextDirection.ltr, child: Container( padding: EdgeInsets.all(24.h), decoration: RoundedRectangleBorder().toSmoothCornerDecoration( diff --git a/lib/widgets/buttons/custom_button.dart b/lib/widgets/buttons/custom_button.dart index ae1d3643..d2534587 100644 --- a/lib/widgets/buttons/custom_button.dart +++ b/lib/widgets/buttons/custom_button.dart @@ -8,7 +8,7 @@ import 'package:hmg_patient_app_new/theme/colors.dart'; class CustomButton extends StatelessWidget { final String text; final String? icon; - final VoidCallback onPressed; + final VoidCallback? onPressed; final Color backgroundColor; final Color borderColor; final Color textColor; @@ -58,8 +58,10 @@ class CustomButton extends StatelessWidget { final radius = borderRadius ?? (12.r); final iconS = iconSize ?? (24.h); final fontS = fontSize ?? (16.f); + final isButtonDisabled = isDisabled || onPressed == null; + return GestureDetector( - onTap: isDisabled ? null : onPressed, + onTap: isButtonDisabled ? null : onPressed, child: Container( height: height == null ? 56.h @@ -69,10 +71,10 @@ class CustomButton extends StatelessWidget { width: width, padding: padding, decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: isDisabled ? AppColors.inputLabelTextColor.withValues(alpha: 0.3) : backgroundColor, + color: isButtonDisabled ? AppColors.inputLabelTextColor.withValues(alpha: 0.3) : backgroundColor, borderRadius: radius, customBorder: BorderRadius.circular(radius), - side: borderSide ?? BorderSide(width: borderWidth.h, color: isDisabled ? Colors.transparent : borderColor)), + side: borderSide ?? BorderSide(width: borderWidth.h, color: isButtonDisabled ? Colors.transparent : borderColor)), child: Row( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, @@ -80,9 +82,24 @@ class CustomButton extends StatelessWidget { if (icon != null) Padding( padding: text.isNotEmpty ? EdgeInsets.only(right: 4.w, left: 4.w) : EdgeInsets.zero, - child: Utils.buildSvgWithAssets(icon: icon!, iconColor: iconColor, isDisabled: isDisabled, width: iconS, height: iconS, applyThemeColor: applyThemeColor), + child: Utils.buildSvgWithAssets(icon: icon!, iconColor: iconColor, isDisabled: isButtonDisabled, width: iconS, height: iconS, applyThemeColor: applyThemeColor), + ), + if (text.isNotEmpty && icon != null) + Flexible( + child: Text( + text, + overflow: textOverflow ?? TextOverflow.ellipsis, + maxLines: 1, + textAlign: TextAlign.center, + style: context.dynamicTextStyle( + fontSize: fontS, + color: isButtonDisabled ? AppColors.greyTextColor : textColor, + letterSpacing: 0, + fontWeight: isBold ? FontWeight.w700 : fontWeight, + ), + ), ), - if (text.isNotEmpty) + if (text.isNotEmpty && icon == null) Text( text, overflow: textOverflow ?? TextOverflow.ellipsis, @@ -90,7 +107,7 @@ class CustomButton extends StatelessWidget { textAlign: TextAlign.center, style: context.dynamicTextStyle( fontSize: fontS, - color: isDisabled ? AppColors.greyTextColor : textColor, + color: isButtonDisabled ? AppColors.greyTextColor : textColor, letterSpacing: 0, fontWeight: isBold ? FontWeight.w700 : fontWeight, ),