diff --git a/assets/images/svg/covid_19.svg b/assets/images/svg/covid_19.svg
new file mode 100644
index 0000000..f3aa128
--- /dev/null
+++ b/assets/images/svg/covid_19.svg
@@ -0,0 +1,7 @@
+
diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json
index 787a718..b8fc7eb 100644
--- a/assets/langs/ar-SA.json
+++ b/assets/langs/ar-SA.json
@@ -876,5 +876,6 @@
"endDate": "تاريخ الانتهاء",
"walkin": "زيارة بدون موعد",
"laserClinic": "عيادة الليزر",
- "continueString": "يكمل"
+ "continueString": "يكمل",
+ "covid_info": "تجري مستشفيات د. سليمان الحبيب فحص فيروس كورونا المستجد وتصدر شهادات السفر على مدار الساعة، طوال أيام الأسبوع، وبسرعة ودقة عالية. يمكن للراغبين في الاستفادة من هذه الخدمة زيارة أحد فروع مستشفيات د. سليمان الحبيب وإجراء فحص كورونا خلال بضع دقائق والحصول على النتائج خلال عدة ساعات خدمة فحص فيروس كورونا Covid 19 بتقنية PCR للكشف عن الفيروس وفقاً لأعلى المعايير العالمية وبأحدث أجهزة RT-PCR عالية الدقة (GeneXpert الأمريكي وغيره)، وهي طرق معتمدة من قبل هيئة الغذاء والدواء وكذلك من قبل المركز السعودي للوقاية من الأمراض المُعدية"
}
\ No newline at end of file
diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json
index 7b8c4b8..7839083 100644
--- a/assets/langs/en-US.json
+++ b/assets/langs/en-US.json
@@ -872,6 +872,6 @@
"searchClinic": "Search Clinic",
"walkin": "Walk In",
"continueString": "Continue",
- "laserClinic": "Laser Clinic"
-
+ "laserClinic": "Laser Clinic",
+ "covid_info" :"Dr. Sulaiman Al Habib hospitals are conducting a test for the emerging corona virus and issuing travel certificates 24/7 in a short time and with high accuracy. Those wishing to benefit from this service can visit one of Dr. Sulaiman Al Habib branches to conduct a corona test within few minutes, and obtain the result within several hours. Corona Virus Covid 19 testing service with PCR technology to detect the virus according to the highest international standards and with the latest high-precision RT-PCR devices (American GeneXpert and others), That is approved by the Food and Drug Authority as well as by the Saudi Center for Infectious Diseases Prevention."
}
\ No newline at end of file
diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart
index a822e22..0febdbe 100644
--- a/lib/core/api_consts.dart
+++ b/lib/core/api_consts.dart
@@ -334,6 +334,8 @@ var GET_PATIENT_SHARE_FOR_WALKIN_APPOINTMENT = 'Services/Doctors.svc/REST/GetChe
var CAN_PAY_FOR_FOR_WALKIN_APPOINTMENT = 'Services/Doctors.svc/REST/CanPayForWalkinAppointment';
+var GET_APPOINTMENT_NEAREST_GATE = 'Services/OUTPs.svc/REST/getGateByProjectIDandClinicID';
+
//URL to get medicine and pharmacies list
var CHANNEL = 3;
var GENERAL_ID = 'Cs2020@2016\$2958';
diff --git a/lib/core/app_assets.dart b/lib/core/app_assets.dart
index 9713ef8..4d5535f 100644
--- a/lib/core/app_assets.dart
+++ b/lib/core/app_assets.dart
@@ -282,6 +282,8 @@ class AppAssets {
static const String pregnancyDayAccordion = '$svgBasePath/pregnancy_test_day_accordion.svg';
static const String pregnancyDueDateAccordion = '$svgBasePath/due_date_accordion.svg';
+ static const String covid19icon = '$svgBasePath/covid_19.svg';
+
// PNGS //
static const String hmgLogo = '$pngBasePath/hmg_logo.png';
static const String liveCareService = '$pngBasePath/livecare_service.png';
@@ -307,6 +309,9 @@ class AppAssets {
static const String fullBodyFront = '$pngBasePath/full_body_front.png';
static const String fullBodyBack = '$pngBasePath/full_body_back.png';
+
+
+
}
class AppAnimations {
@@ -327,4 +332,6 @@ class AppAnimations {
static const String ambulance = '$lottieBasePath/ambulance.json';
static const String ambulanceAlert = '$lottieBasePath/ambulance_alert.json';
static const String rrtAmbulance = '$lottieBasePath/rrt_ambulance.json';
+
+
}
diff --git a/lib/features/blood_donation/blood_donation_view_model.dart b/lib/features/blood_donation/blood_donation_view_model.dart
index f345359..b8f0e9c 100644
--- a/lib/features/blood_donation/blood_donation_view_model.dart
+++ b/lib/features/blood_donation/blood_donation_view_model.dart
@@ -1,6 +1,7 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
+import 'package:hmg_patient_app_new/extensions/string_extensions.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_list_model.dart';
import 'package:hmg_patient_app_new/features/blood_donation/models/blood_group_response_model.dart';
@@ -31,7 +32,7 @@ class BloodDonationViewModel extends ChangeNotifier {
List genderList = [
BloodGroupListModel(LocaleKeys.malE.tr(), 1),
- BloodGroupListModel(LocaleKeys.female.tr(), 2),
+ BloodGroupListModel("Female".needTranslation.tr(), 2),
];
late CitiesModel selectedCity;
diff --git a/lib/features/book_appointments/book_appointments_repo.dart b/lib/features/book_appointments/book_appointments_repo.dart
index f6e83ff..cfd473e 100644
--- a/lib/features/book_appointments/book_appointments_repo.dart
+++ b/lib/features/book_appointments/book_appointments_repo.dart
@@ -5,6 +5,7 @@ import 'package:hmg_patient_app_new/core/api_consts.dart';
import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart';
import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
+import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/appointment_nearest_gate_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/dental_chief_complaints_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctor_profile_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_model.dart';
@@ -102,6 +103,8 @@ abstract class BookAppointmentsRepo {
required int userAge,
Function(dynamic)? onSuccess,
Function(String)? onError});
+
+ Future>> getAppointmentNearestGate({required int projectID, required int clinicID});
}
class BookAppointmentsRepoImp implements BookAppointmentsRepo {
@@ -1046,4 +1049,40 @@ class BookAppointmentsRepoImp implements BookAppointmentsRepo {
return Left(UnknownFailure(e.toString()));
}
}
+
+ @override
+ Future>> getAppointmentNearestGate({required int projectID, required int clinicID}) async {
+ Map mapRequest = {"ProjectID": projectID, "ClinicID": clinicID};
+
+ try {
+ GenericApiModel? apiResponse;
+ Failure? failure;
+ await apiClient.post(
+ GET_APPOINTMENT_NEAREST_GATE,
+ body: mapRequest,
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ try {
+ final nearestGateResponse = AppointmentNearestGateResponseModel.fromJson(response['getGateByProjectIDandClinicIDList'][0]);
+
+ apiResponse = GenericApiModel(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: null,
+ data: nearestGateResponse,
+ );
+ } catch (e) {
+ failure = DataParsingFailure(e.toString());
+ }
+ },
+ );
+ if (failure != null) return Left(failure!);
+ if (apiResponse == null) return Left(ServerFailure("Unknown error"));
+ return Right(apiResponse!);
+ } catch (e) {
+ return Left(UnknownFailure(e.toString()));
+ }
+ }
}
diff --git a/lib/features/book_appointments/book_appointments_view_model.dart b/lib/features/book_appointments/book_appointments_view_model.dart
index 16dd70c..cbed940 100644
--- a/lib/features/book_appointments/book_appointments_view_model.dart
+++ b/lib/features/book_appointments/book_appointments_view_model.dart
@@ -14,6 +14,7 @@ import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_repo.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/LaserCategoryType.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/free_slot.dart';
+import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/appointment_nearest_gate_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/dental_chief_complaints_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctor_profile_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_model.dart';
@@ -44,6 +45,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
bool isDoctorsListLoading = false;
bool isDoctorProfileLoading = false;
bool isDoctorSearchByNameStarted = false;
+ bool isAppointmentNearestGateLoading = false;
bool isLiveCareSchedule = false;
bool isGetDocForHealthCal = false;
@@ -66,6 +68,11 @@ class BookAppointmentsViewModel extends ChangeNotifier {
List doctorsList = [];
List filteredDoctorList = [];
+ // Grouped doctors lists
+ List> doctorsListByClinic = [];
+ List> doctorsListByHospital = [];
+ List> doctorsListGrouped = [];
+
List liveCareDoctorsList = [];
List patientDentalPlanEstimationList = [];
@@ -127,6 +134,8 @@ class BookAppointmentsViewModel extends ChangeNotifier {
PatientAppointmentShareResponseModel? patientWalkInAppointmentShareResponseModel;
+ AppointmentNearestGateResponseModel? appointmentNearestGateResponseModel;
+
///variables for laser clinic
List femaleLaserCategory = [
LaserCategoryType(1, 'bodyString'),
@@ -151,14 +160,28 @@ class BookAppointmentsViewModel extends ChangeNotifier {
setIsSortByClinic(bool value) {
isSortByClinic = value;
- if (isSortByClinic) {
- doctorsList.sort((a, b) => a.clinicName!.compareTo(b.clinicName!));
- } else {
- doctorsList.sort((a, b) => a.projectName!.compareTo(b.projectName!));
- }
+ doctorsListGrouped = isSortByClinic ? doctorsListByClinic : doctorsListByHospital;
notifyListeners();
}
+ // Group doctors by clinic and hospital
+ void _groupDoctorsList() {
+ final clinicMap = >{};
+ final hospitalMap = >{};
+
+ for (var doctor in doctorsList) {
+ final clinicKey = (doctor.clinicName ?? 'Unknown').trim();
+ clinicMap.putIfAbsent(clinicKey, () => []).add(doctor);
+
+ final hospitalKey = (doctor.projectName ?? 'Unknown').trim();
+ hospitalMap.putIfAbsent(hospitalKey, () => []).add(doctor);
+ }
+
+ doctorsListByClinic = clinicMap.values.toList();
+ doctorsListByHospital = hospitalMap.values.toList();
+ doctorsListGrouped = isSortByClinic ? doctorsListByClinic : doctorsListByHospital;
+ }
+
BookAppointmentsViewModel(
{required this.bookAppointmentsRepo,
required this.errorHandlerService,
@@ -419,6 +442,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
initializeFilteredList();
clearSearchFilters();
getFiltersFromDoctorList();
+ _groupDoctorsList();
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
@@ -450,6 +474,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
initializeFilteredList();
clearSearchFilters();
getFiltersFromDoctorList();
+ _groupDoctorsList();
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
@@ -1117,6 +1142,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
// initializeFilteredList();
// clearSearchFilters();
// getFiltersFromDoctorList();
+ _groupDoctorsList();
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
@@ -1321,4 +1347,32 @@ class BookAppointmentsViewModel extends ChangeNotifier {
},
);
}
+
+ Future getAppointmentNearestGate({required int projectID, required int clinicID, Function(dynamic)? onSuccess, Function(String)? onError}) async {
+ isAppointmentNearestGateLoading = true;
+ notifyListeners();
+
+ final result = await bookAppointmentsRepo.getAppointmentNearestGate(projectID: projectID, clinicID: clinicID);
+
+ result.fold(
+ (failure) async {
+ if (onError != null) {
+ onError(failure.message);
+ }
+ },
+ (apiResponse) {
+ if (apiResponse.messageStatus == 2) {
+ onError!(apiResponse.errorMessage!);
+ // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
+ } else if (apiResponse.messageStatus == 1) {
+ appointmentNearestGateResponseModel = apiResponse.data!;
+ isAppointmentNearestGateLoading = false;
+ notifyListeners();
+ if (onSuccess != null) {
+ onSuccess(apiResponse);
+ }
+ }
+ },
+ );
+ }
}
diff --git a/lib/features/book_appointments/models/resp_models/appointment_nearest_gate_response_model.dart b/lib/features/book_appointments/models/resp_models/appointment_nearest_gate_response_model.dart
new file mode 100644
index 0000000..bdaa4e2
--- /dev/null
+++ b/lib/features/book_appointments/models/resp_models/appointment_nearest_gate_response_model.dart
@@ -0,0 +1,64 @@
+class AppointmentNearestGateResponseModel {
+ String? clinicDescription;
+ String? clinicDescriptionN;
+ int? clinicID;
+ String? clinicLocation;
+ String? clinicLocationN;
+ int? gender;
+ int? iD;
+ String? nearestGateNumber;
+ String? nearestGateNumberN;
+ int? projectID;
+ String? projectName;
+ String? projectNameN;
+ int? rowID;
+
+ AppointmentNearestGateResponseModel(
+ {this.clinicDescription,
+ this.clinicDescriptionN,
+ this.clinicID,
+ this.clinicLocation,
+ this.clinicLocationN,
+ this.gender,
+ this.iD,
+ this.nearestGateNumber,
+ this.nearestGateNumberN,
+ this.projectID,
+ this.projectName,
+ this.projectNameN,
+ this.rowID});
+
+ AppointmentNearestGateResponseModel.fromJson(Map json) {
+ clinicDescription = json['ClinicDescription'];
+ clinicDescriptionN = json['ClinicDescriptionN'];
+ clinicID = json['ClinicID'];
+ clinicLocation = json['ClinicLocation'];
+ clinicLocationN = json['ClinicLocationN'];
+ gender = json['Gender'];
+ iD = json['ID'];
+ nearestGateNumber = json['NearestGateNumber'];
+ nearestGateNumberN = json['NearestGateNumberN'];
+ projectID = json['ProjectID'];
+ projectName = json['ProjectName'];
+ projectNameN = json['ProjectNameN'];
+ rowID = json['RowID'];
+ }
+
+ Map toJson() {
+ final Map data = Map();
+ data['ClinicDescription'] = clinicDescription;
+ data['ClinicDescriptionN'] = clinicDescriptionN;
+ data['ClinicID'] = clinicID;
+ data['ClinicLocation'] = clinicLocation;
+ data['ClinicLocationN'] = clinicLocationN;
+ data['Gender'] = gender;
+ data['ID'] = iD;
+ data['NearestGateNumber'] = nearestGateNumber;
+ data['NearestGateNumberN'] = nearestGateNumberN;
+ data['ProjectID'] = projectID;
+ data['ProjectName'] = projectName;
+ data['ProjectNameN'] = projectNameN;
+ data['RowID'] = rowID;
+ return data;
+ }
+}
diff --git a/lib/features/hmg_services/hmg_services_view_model.dart b/lib/features/hmg_services/hmg_services_view_model.dart
index b1a7757..5da0860 100644
--- a/lib/features/hmg_services/hmg_services_view_model.dart
+++ b/lib/features/hmg_services/hmg_services_view_model.dart
@@ -1,3 +1,5 @@
+import 'dart:convert';
+
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart';
import 'package:hmg_patient_app_new/core/enums.dart';
@@ -18,6 +20,7 @@ import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'models/req_models/check_activation_e_referral_req_model.dart';
import 'models/resq_models/relationship_type_resp_mode.dart';
+import 'models/ui_models/covid_questionnare_model.dart';
class HmgServicesViewModel extends ChangeNotifier {
final HmgServicesRepo hmgServicesRepo;
@@ -60,6 +63,8 @@ class HmgServicesViewModel extends ChangeNotifier {
Future getOrdersList() async {}
+
+
// HHC multiple services selection
List selectedHhcServices = [];
@@ -766,4 +771,16 @@ class HmgServicesViewModel extends ChangeNotifier {
},
);
}
+ List getQuestionsFromJson() {
+ final String questionsJson = '''[ { "id": 1, "questionEN": "Is the test intended for travel?", "questionAR": "هل تجري التحليل بغرض السفر؟", "ans": 2 }, { "id": 2, "questionEN": "Coming from outside KSA within last 2 weeks?", "questionAR": "هل قدمت من خارج المملكة خلال الأسبوعين الماضيين؟", "ans": 2 }, { "id": 3, "questionEN": "Do you currently have fever?", "questionAR": "هل تعاني حاليا من حرارة؟", "ans": 2 }, { "id": 4, "questionEN": "Did you have fever in last 2 weeks?", "questionAR": "هل عانيت من حرارة في الأسبوعين الماضيين؟", "ans": 2 }, { "id": 5, "questionEN": "Do you have a sore throat?", "questionAR": "هل لديك التهاب في الحلق؟", "ans": 2 }, { "id": 6, "questionEN": "Do you have a runny nose?", "questionAR": "هل لديك سيلان بالأنف؟" }, { "id": 7, "questionEN": "Do you have a cough?", "questionAR": "هل لديك سعال؟", "ans": 2 }, { "id": 8, "questionEN": "Do you have shortness of breath?", "questionAR": "هل تعاني من ضيق في التنفس؟", "ans": 2 }, { "id": 9, "questionEN": "Do you have nausea?", "questionAR": "هل تعاني من غثيان؟", "ans": 2 }, { "id": 10, "questionEN": "Do you have vomiting?", "questionAR": "هل تعاني من القيء؟", "ans": 2 }, { "id": 11, "questionEN": "Do you have a headache?", "questionAR": "هل تعاني من صداع في الرأس؟", "ans": 2 }, { "id": 12, "questionEN": "Do you have muscle pain?", "questionAR": "هل تعانين من آلام عضلية؟", "ans": 2 }, { "id": 13, "questionEN": "Do you have joint pain?", "questionAR": "هل تعاني من آلام المفاصل؟", "ans": 2 }, { "id": 14, "questionEN": "Do you have diarrhea?", "questionAR": "هل لديك اسهال؟", "ans": 2 } ]''';
+
+ try {
+ final parsed = json.decode(questionsJson) as List;
+ return parsed
+ .map((e) => CovidQuestionnaireModel.fromJson(Map.from(e)))
+ .toList();
+ } catch (_) {
+ return [];
+ }
+ }
}
diff --git a/lib/features/hmg_services/models/ui_models/covid_questionnare_model.dart b/lib/features/hmg_services/models/ui_models/covid_questionnare_model.dart
new file mode 100644
index 0000000..35b0efe
--- /dev/null
+++ b/lib/features/hmg_services/models/ui_models/covid_questionnare_model.dart
@@ -0,0 +1,33 @@
+import 'dart:convert';
+
+class CovidQuestionnaireModel {
+ int? id;
+ String? questionEn;
+ String? questionAr;
+ int? ans;
+
+ CovidQuestionnaireModel({
+ this.id,
+ this.questionEn,
+ this.questionAr,
+ this.ans,
+ });
+
+ factory CovidQuestionnaireModel.fromRawJson(String str) => CovidQuestionnaireModel.fromJson(json.decode(str));
+
+ String toRawJson() => json.encode(toJson());
+
+ factory CovidQuestionnaireModel.fromJson(Map json) => CovidQuestionnaireModel(
+ id: json["id"],
+ questionEn: json["questionEN"],
+ questionAr: json["questionAR"],
+ ans: json["ans"],
+ );
+
+ Map toJson() => {
+ "id": id,
+ "questionEN": questionEn,
+ "questionAR": questionAr,
+ "ans": ans,
+ };
+}
diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart
index d76422b..ea0bcb8 100644
--- a/lib/generated/locale_keys.g.dart
+++ b/lib/generated/locale_keys.g.dart
@@ -2,7 +2,7 @@
// ignore_for_file: constant_identifier_names
-abstract class LocaleKeys {
+abstract class LocaleKeys {
static const english = 'english';
static const arabic = 'arabic';
static const login = 'login';
@@ -189,7 +189,6 @@ abstract class LocaleKeys {
static const firstName = 'firstName';
static const middleName = 'middleName';
static const lastName = 'lastName';
- static const female = 'female';
static const preferredLanguage = 'preferredLanguage';
static const locationsRegister = 'locationsRegister';
static const ksa = 'ksa';
@@ -801,7 +800,7 @@ abstract class LocaleKeys {
static const fullName = 'fullName';
static const married = 'married';
static const uae = 'uae';
- static const malE = 'male';
+ static const malE = 'malE';
static const loginBy = 'loginBy';
static const loginByOTP = 'loginByOTP';
static const guest = 'guest';
@@ -876,4 +875,6 @@ abstract class LocaleKeys {
static const walkin = 'walkin';
static const laserClinic = 'laserClinic';
static const continueString = 'continueString';
+ static const covid_info = 'covid_info';
+
}
diff --git a/lib/presentation/appointments/appointment_details_page.dart b/lib/presentation/appointments/appointment_details_page.dart
index 7b605b9..dae8d25 100644
--- a/lib/presentation/appointments/appointment_details_page.dart
+++ b/lib/presentation/appointments/appointment_details_page.dart
@@ -4,6 +4,8 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/api_consts.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/calender_utils_new.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
@@ -31,6 +33,7 @@ import 'package:hmg_patient_app_new/presentation/prescriptions/prescription_deta
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
+import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
@@ -132,10 +135,7 @@ class _AppointmentDetailsPageState extends State {
},
onCancelTap: () async {
myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
- var isEventAddedOrRemoved = await CalenderUtilsNew.instance.checkAndRemove( id:"${widget.patientAppointmentHistoryResponseModel.appointmentNo}", );
- setState(() {
- myAppointmentsViewModel.setAppointmentReminder(isEventAddedOrRemoved, widget.patientAppointmentHistoryResponseModel);
- });
+
LoaderBottomSheet.showLoader(loadingText: "Cancelling Appointment, Please Wait...".needTranslation);
await myAppointmentsViewModel.cancelAppointment(
patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel,
@@ -155,6 +155,10 @@ class _AppointmentDetailsPageState extends State {
isFullScreen: false,
);
});
+ var isEventAddedOrRemoved = await CalenderUtilsNew.instance.checkAndRemove( id:"${widget.patientAppointmentHistoryResponseModel.appointmentNo}", );
+ setState(() {
+ myAppointmentsViewModel.setAppointmentReminder(isEventAddedOrRemoved, widget.patientAppointmentHistoryResponseModel);
+ });
},
onRescheduleTap: () async {
openDoctorScheduleCalendar();
@@ -164,90 +168,105 @@ class _AppointmentDetailsPageState extends State {
!AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)
? Column(
children: [
- Container(
- decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
- color: AppColors.whiteColor,
- borderRadius: 20.r,
- hasShadow: false,
- ),
- child: Padding(
- padding: EdgeInsets.all(16.h),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Row(
- children: [
- "Appointment Status".needTranslation.toText16(isBold: true),
- ],
- ),
- SizedBox(height: 4.h),
- (!AppointmentType.isConfirmed(widget.patientAppointmentHistoryResponseModel)
- ? "Not Confirmed".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500)
- : "Confirmed".needTranslation.toText12(color: AppColors.successColor, fontWeight: FontWeight.w500)),
- SizedBox(height: 16.h),
- //TODO Add countdown timer in case of LiveCare Appointment
- widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false
- ? Row(
- children: [
- Utils.buildSvgWithAssets(icon: AppAssets.livecare_clinic_icon, width: 40.h, height: 40.h),
- SizedBox(width: 12.w),
- Expanded(
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- "The doctor will call you once the appointment time approaches."
- .needTranslation
- .toText14(color: AppColors.greyTextColor, weight: FontWeight.w500),
- ],
+ Consumer(builder: (context, bookAppointmentsVM, child) {
+ return Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 20.r,
+ hasShadow: false,
+ ),
+ child: Padding(
+ padding: EdgeInsets.all(16.h),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ children: [
+ "Appointment Status".needTranslation.toText16(isBold: true),
+ ],
+ ),
+ SizedBox(height: 4.h),
+ (!AppointmentType.isConfirmed(widget.patientAppointmentHistoryResponseModel)
+ ? "Not Confirmed".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500)
+ : "Confirmed".needTranslation.toText12(color: AppColors.successColor, fontWeight: FontWeight.w500)),
+ SizedBox(height: 16.h),
+ //TODO Add countdown timer in case of LiveCare Appointment
+ widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false
+ ? Row(
+ children: [
+ Utils.buildSvgWithAssets(icon: AppAssets.livecare_clinic_icon, width: 40.h, height: 40.h),
+ SizedBox(width: 12.w),
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ "The doctor will call you once the appointment time approaches."
+ .needTranslation
+ .toText14(color: AppColors.greyTextColor, weight: FontWeight.w500),
+ ],
+ ),
),
- ),
- ],
- )
- : Stack(
- children: [
- ClipRRect(
- clipBehavior: Clip.hardEdge,
- borderRadius: BorderRadius.circular(24.r),
- // Todo: what is this???? Api Key??? 😲
- child: Image.network(
- "https://maps.googleapis.com/maps/api/staticmap?center=${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&zoom=14&size=350x165&maptype=roadmap&markers=color:red%7C${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&key=${ApiKeyConstants.googleMapsApiKey}",
- fit: BoxFit.contain,
+ ],
+ )
+ : Stack(
+ children: [
+ ClipRRect(
+ clipBehavior: Clip.hardEdge,
+ borderRadius: BorderRadius.circular(24.r),
+ // Todo: what is this???? Api Key??? 😲
+ child: Image.network(
+ "https://maps.googleapis.com/maps/api/staticmap?center=${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&zoom=14&size=350x165&maptype=roadmap&markers=color:red%7C${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&key=${ApiKeyConstants.googleMapsApiKey}",
+ fit: BoxFit.contain,
+ ),
),
- ),
- Positioned(
- bottom: 0,
- child: SizedBox(
- width: MediaQuery.of(context).size.width * 0.785,
- child: CustomButton(
- text: "Get Directions".needTranslation,
- onPressed: () {
- MapsLauncher.launchCoordinates(
- double.parse(widget.patientAppointmentHistoryResponseModel.latitude!),
- double.parse(widget.patientAppointmentHistoryResponseModel.longitude!),
- widget.patientAppointmentHistoryResponseModel.projectName);
- },
- backgroundColor: AppColors.textColor.withValues(alpha: 0.8),
- borderColor: AppointmentType.getNextActionButtonColor(
- widget.patientAppointmentHistoryResponseModel.nextAction)
- .withValues(alpha: 0.01),
- textColor: AppColors.whiteColor,
- fontSize: 14.f,
- fontWeight: FontWeight.w500,
- borderRadius: 12.r,
- padding: EdgeInsets.symmetric(horizontal: 10.w),
- height: 40.h,
- icon: AppAssets.directions_icon,
- iconColor: AppColors.whiteColor,
- iconSize: 14.h,
- ).paddingAll(12.h),
+ Positioned(
+ bottom: 0,
+ child: SizedBox(
+ width: MediaQuery.of(context).size.width * 0.785,
+ child: CustomButton(
+ text: "Get Directions".needTranslation,
+ onPressed: () {
+ MapsLauncher.launchCoordinates(double.parse(widget.patientAppointmentHistoryResponseModel.latitude!),
+ double.parse(widget.patientAppointmentHistoryResponseModel.longitude!), widget.patientAppointmentHistoryResponseModel.projectName);
+ },
+ backgroundColor: AppColors.textColor.withValues(alpha: 0.8),
+ borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withValues(alpha: 0.01),
+ textColor: AppColors.whiteColor,
+ fontSize: 14.f,
+ fontWeight: FontWeight.w500,
+ borderRadius: 12.r,
+ padding: EdgeInsets.symmetric(horizontal: 10.w),
+ height: 40.h,
+ icon: AppAssets.directions_icon,
+ iconColor: AppColors.whiteColor,
+ iconSize: 14.h,
+ ).paddingAll(12.h),
+ ),
),
- ),
- ],
- ),
- ],
+ ],
+ ),
+ SizedBox(height: 8.h),
+ Wrap(
+ direction: Axis.horizontal,
+ spacing: 8.w,
+ runSpacing: 8.h,
+ children: [
+ AppCustomChipWidget(
+ labelText: bookAppointmentsVM.isAppointmentNearestGateLoading
+ ? "Floor: Ground Floor"
+ : "Floor: ${getIt.get().isArabic() ? bookAppointmentsViewModel.appointmentNearestGateResponseModel!.clinicLocationN : bookAppointmentsViewModel.appointmentNearestGateResponseModel!.clinicLocation}",
+ ).toShimmer2(isShow: bookAppointmentsVM.isAppointmentNearestGateLoading),
+ AppCustomChipWidget(
+ labelText:
+ "Nearest Gate: ${getIt.get().isArabic() ? bookAppointmentsVM.appointmentNearestGateResponseModel!.nearestGateNumberN : bookAppointmentsVM.appointmentNearestGateResponseModel!.nearestGateNumber}")
+ .toShimmer2(isShow: bookAppointmentsVM.isAppointmentNearestGateLoading),
+ ],
+ ),
+ ],
+ ),
),
- ),
- ),
+ );
+ }),
SizedBox(height: 16.h),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
diff --git a/lib/presentation/appointments/my_appointments_page.dart b/lib/presentation/appointments/my_appointments_page.dart
index abf2464..eb72bc6 100644
--- a/lib/presentation/appointments/my_appointments_page.dart
+++ b/lib/presentation/appointments/my_appointments_page.dart
@@ -22,6 +22,7 @@ import 'package:hmg_patient_app_new/presentation/book_appointment/book_appointme
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
+import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart';
import 'package:hmg_patient_app_new/widgets/date_range_selector/date_range_calender.dart';
import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_view_model.dart';
@@ -187,18 +188,9 @@ class _MyAppointmentsPageState extends State {
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
- CustomButton(
- text: "${myAppointmentsVM.patientAppointmentsViewList[index].patientDoctorAppointmentList!.length} Appointments",
- onPressed: () {},
- backgroundColor: AppColors.greyColor,
- borderColor: AppColors.greyColor,
- textColor: AppColors.blackColor,
- fontSize: 10,
- fontWeight: FontWeight.w500,
- borderRadius: 8,
- padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
- height: 30.h,
- ),
+ AppCustomChipWidget(
+ labelText:
+ "${myAppointmentsVM.patientAppointmentsViewList[index].patientDoctorAppointmentList!.length} Appointments"),
Icon(isExpanded ? Icons.expand_less : Icons.expand_more),
],
),
@@ -224,7 +216,7 @@ class _MyAppointmentsPageState extends State {
child: isExpanded
? Container(
key: ValueKey(index),
- padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 8.h),
+ padding: EdgeInsets.symmetric(horizontal: 0.w, vertical: 0.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -232,63 +224,6 @@ class _MyAppointmentsPageState extends State {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
- Row(
- mainAxisSize: MainAxisSize.min,
- children: [
- Image.network(
- appointment.doctorImageURL!,
- width: 24.h,
- height: 24.h,
- fit: BoxFit.fill,
- ).circle(100),
- SizedBox(width: 8.h),
- Expanded(child: appointment.doctorNameObj!.toText14(weight: FontWeight.w500)),
- ],
- ),
- SizedBox(height: 8.h),
- Row(
- children: [
- CustomButton(
- text: DateUtil.formatDateToDate(DateUtil.convertStringToDate(appointment.appointmentDate), false),
- onPressed: () {},
- backgroundColor: AppColors.greyColor,
- borderColor: AppColors.greyColor,
- textColor: AppColors.blackColor,
- fontSize: 10,
- fontWeight: FontWeight.w500,
- borderRadius: 8,
- padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
- height: 24.h,
- ),
- SizedBox(width: 8.h),
- CustomButton(
- text: myAppointmentsVM.isAppointmentsSortByClinic ? appointment.projectName! : appointment.clinicName!,
- onPressed: () {},
- backgroundColor: AppColors.greyColor,
- borderColor: AppColors.greyColor,
- textColor: AppColors.blackColor,
- fontSize: 10,
- fontWeight: FontWeight.w500,
- borderRadius: 8,
- padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
- height: 24.h,
- ),
- SizedBox(width: 8.h),
- CustomButton(
- text: appointment.statusDesc ?? "",
- onPressed: () {},
- backgroundColor: AppColors.greyColor,
- borderColor: AppColors.greyColor,
- textColor: AppColors.blackColor,
- fontSize: 10,
- fontWeight: FontWeight.w500,
- borderRadius: 8,
- padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
- height: 24.h,
- ),
- ],
- ),
- SizedBox(height: 8.h),
AppointmentCard(
patientAppointmentHistoryResponseModel: appointment,
myAppointmentsViewModel: myAppointmentsViewModel,
@@ -296,9 +231,9 @@ class _MyAppointmentsPageState extends State {
isLoading: false,
isFromHomePage: false,
),
- SizedBox(height: 12.h),
- Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.05), height: 1.h),
- SizedBox(height: 12.h),
+ SizedBox(height: 8.h),
+ Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.05), height: 1.h).paddingSymmetrical(16.w, 0.h),
+ SizedBox(height: 8.h),
],
);
}),
diff --git a/lib/presentation/appointments/widgets/appointment_card.dart b/lib/presentation/appointments/widgets/appointment_card.dart
index 57e19ca..6849253 100644
--- a/lib/presentation/appointments/widgets/appointment_card.dart
+++ b/lib/presentation/appointments/widgets/appointment_card.dart
@@ -372,6 +372,7 @@ class AppointmentCard extends StatelessWidget {
),
);
} else {
+ bookAppointmentsViewModel.getAppointmentNearestGate(projectID: patientAppointmentHistoryResponseModel.projectID, clinicID: patientAppointmentHistoryResponseModel.clinicID);
Navigator.of(context)
.push(
CustomPageRoute(
diff --git a/lib/presentation/book_appointment/laser/laser_appointment.dart b/lib/presentation/book_appointment/laser/laser_appointment.dart
index cde929a..144c261 100644
--- a/lib/presentation/book_appointment/laser/laser_appointment.dart
+++ b/lib/presentation/book_appointment/laser/laser_appointment.dart
@@ -1,6 +1,7 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
+import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/laser_body_parts.dart';
@@ -81,7 +82,7 @@ class LaserAppointment extends StatelessWidget {
activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1),
tabs: [
CustomTabBarModel(null,LocaleKeys.malE.tr()),
- CustomTabBarModel(null,LocaleKeys.female.tr()),
+ CustomTabBarModel(null, "Female".needTranslation),
],
onTabChange: (index) {
var viewmodel = context.read();
diff --git a/lib/presentation/book_appointment/review_appointment_page.dart b/lib/presentation/book_appointment/review_appointment_page.dart
index 40abd40..0ee7a33 100644
--- a/lib/presentation/book_appointment/review_appointment_page.dart
+++ b/lib/presentation/book_appointment/review_appointment_page.dart
@@ -50,146 +50,168 @@ class _ReviewAppointmentPageState extends State {
Expanded(
child: CollapsingListView(
title: LocaleKeys.reviewAppointment.tr(context: context),
- child: SingleChildScrollView(
- padding: EdgeInsets.symmetric(horizontal: 24.h),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- SizedBox(height: 24.h),
- LocaleKeys.docInfo.tr(context: context).toText16(isBold: true),
- SizedBox(height: 16.h),
- Container(
- decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
- color: AppColors.whiteColor,
- borderRadius: 24.h,
- hasShadow: false,
- ),
- child: Padding(
- padding: EdgeInsets.all(16.h),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Row(
- children: [
- Image.network(
- bookAppointmentsViewModel.selectedDoctor.doctorImageURL!,
- width: 50.h,
- height: 50.h,
- fit: BoxFit.cover,
- ).circle(100),
- SizedBox(width: 8.h),
- Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Row(
- children: [
- SizedBox(
- width: MediaQuery.of(context).size.width * 0.49,
- child:
- "${bookAppointmentsViewModel.selectedDoctor.doctorTitle} ${bookAppointmentsViewModel.selectedDoctor.name}".toString().toText16(isBold: true, maxlines: 1),
- ),
- Image.network(
- bookAppointmentsViewModel.selectedDoctor.nationalityFlagURL!,
- width: 20.h,
- height: 15.h,
- fit: BoxFit.cover,
- ),
- ],
- ),
- SizedBox(height: 2.h),
- (bookAppointmentsViewModel.selectedDoctor.speciality!.isNotEmpty ? bookAppointmentsViewModel.selectedDoctor.speciality!.first : "")
- .toString()
- .toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor, maxLine: 1),
- ],
- ),
- ],
- ),
- SizedBox(height: 12.h),
- Wrap(
- direction: Axis.horizontal,
- spacing: 8.h,
- runSpacing: 8.h,
- children: [
- AppCustomChipWidget(
- labelText: "${LocaleKeys.clinic.tr(context: context)}: ${bookAppointmentsViewModel.selectedDoctor.clinicName}".needTranslation,
- ),
- AppCustomChipWidget(
- labelText: "${LocaleKeys.branch.tr(context: context)} ${bookAppointmentsViewModel.selectedDoctor.projectName}".needTranslation,
- ),
- AppCustomChipWidget(
- labelText:
- "${LocaleKeys.date.tr(context: context)}: ${bookAppointmentsViewModel.isWaitingAppointmentSelected ? DateUtil.formatDateToDate(DateTime.now(), false) : bookAppointmentsViewModel.selectedAppointmentDate}"
- .needTranslation,
- ),
- AppCustomChipWidget(
- labelText:
- "${LocaleKeys.time.tr(context: context)}: ${bookAppointmentsViewModel.isWaitingAppointmentSelected ? "Waiting Appointment".needTranslation : bookAppointmentsViewModel.selectedAppointmentTime}"
- .needTranslation,
- ),
- ],
- ),
- ],
+ child: Consumer(builder: (context, bookAppointmentsVM, child) {
+ return SingleChildScrollView(
+ padding: EdgeInsets.symmetric(horizontal: 24.h),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ SizedBox(height: 24.h),
+ LocaleKeys.docInfo.tr(context: context).toText16(isBold: true),
+ SizedBox(height: 16.h),
+ Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 24.r,
+ hasShadow: false,
),
- ),
- ),
- SizedBox(height: 24.h),
- LocaleKeys.patientInfo.tr(context: context).toText16(isBold: true),
- SizedBox(height: 16.h),
- Container(
- decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
- color: AppColors.whiteColor,
- borderRadius: 24.h,
- hasShadow: false,
- ),
- child: Padding(
- padding: EdgeInsets.all(16.h),
- child: Row(
- children: [
- Image.asset(
- appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.maleImg : AppAssets.femaleImg,
- width: 52.h,
- height: 52.h,
- ),
- SizedBox(width: 8.h),
- Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}".toText16(isBold: true),
- SizedBox(height: 8.h),
- AppCustomChipWidget(labelText: "${appState.getAuthenticatedUser()!.age} Years Old"),
- ],
- ),
- ],
+ child: Padding(
+ padding: EdgeInsets.all(16.h),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ children: [
+ Image.network(
+ bookAppointmentsViewModel.selectedDoctor.doctorImageURL!,
+ width: 50.h,
+ height: 50.h,
+ fit: BoxFit.cover,
+ ).circle(100),
+ SizedBox(width: 8.h),
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ children: [
+ SizedBox(
+ width: MediaQuery.of(context).size.width * 0.49,
+ child: "${bookAppointmentsViewModel.selectedDoctor.doctorTitle} ${bookAppointmentsViewModel.selectedDoctor.name}"
+ .toString()
+ .toText16(isBold: true, maxlines: 1),
+ ),
+ Image.network(
+ bookAppointmentsViewModel.selectedDoctor.nationalityFlagURL!,
+ width: 20.h,
+ height: 15.h,
+ fit: BoxFit.cover,
+ ),
+ ],
+ ),
+ SizedBox(height: 2.h),
+ (bookAppointmentsViewModel.selectedDoctor.speciality!.isNotEmpty ? bookAppointmentsViewModel.selectedDoctor.speciality!.first : "")
+ .toString()
+ .toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor, maxLine: 1),
+ ],
+ ),
+ ],
+ ),
+ SizedBox(height: 12.h),
+ Wrap(
+ direction: Axis.horizontal,
+ spacing: 8.h,
+ runSpacing: 8.h,
+ children: [
+ AppCustomChipWidget(
+ labelText: "${LocaleKeys.clinic.tr(context: context)}: ${bookAppointmentsViewModel.selectedDoctor.clinicName}".needTranslation,
+ ),
+ AppCustomChipWidget(
+ labelText: "${LocaleKeys.branch.tr(context: context)} ${bookAppointmentsViewModel.selectedDoctor.projectName}".needTranslation,
+ ),
+ AppCustomChipWidget(
+ labelText:
+ "${LocaleKeys.date.tr(context: context)}: ${bookAppointmentsViewModel.isWaitingAppointmentSelected ? DateUtil.formatDateToDate(DateTime.now(), false) : bookAppointmentsViewModel.selectedAppointmentDate}"
+ .needTranslation,
+ ),
+ AppCustomChipWidget(
+ labelText:
+ "${LocaleKeys.time.tr(context: context)}: ${bookAppointmentsViewModel.isWaitingAppointmentSelected ? "Waiting Appointment".needTranslation : bookAppointmentsViewModel.selectedAppointmentTime}"
+ .needTranslation,
+ ),
+ ],
+ ),
+ ],
+ ),
),
),
- ),
- SizedBox(height: 24.h),
- "Hospital Information".needTranslation.toText16(isBold: true),
- SizedBox(height: 16.h),
- Container(
- decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
- color: AppColors.whiteColor,
- borderRadius: 12.h,
- hasShadow: false,
+ SizedBox(height: 24.h),
+ LocaleKeys.patientInfo.tr(context: context).toText16(isBold: true),
+ SizedBox(height: 16.h),
+ Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 24.r,
+ hasShadow: false,
+ ),
+ child: Padding(
+ padding: EdgeInsets.all(16.h),
+ child: Row(
+ children: [
+ Image.asset(
+ appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.maleImg : AppAssets.femaleImg,
+ width: 52.h,
+ height: 52.h,
+ ),
+ SizedBox(width: 8.h),
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}".toText16(isBold: true),
+ SizedBox(height: 8.h),
+ AppCustomChipWidget(labelText: "${appState.getAuthenticatedUser()!.age} Years Old"),
+ ],
+ ),
+ ],
+ ),
+ ),
),
- child: Padding(
- padding: EdgeInsets.all(16.h),
- child: Row(
- children: [
- bookAppointmentsViewModel.selectedDoctor.projectName!.toText16(isBold: true),
- ],
+ SizedBox(height: 24.h),
+ "Hospital Information".needTranslation.toText16(isBold: true),
+ SizedBox(height: 16.h),
+ Container(
+ width: double.infinity,
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 24.r,
+ hasShadow: false,
+ ),
+ child: Padding(
+ padding: EdgeInsets.all(16.h),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ bookAppointmentsViewModel.selectedDoctor.projectName!.toText16(isBold: true),
+ SizedBox(height: 8.h),
+ Wrap(
+ direction: Axis.horizontal,
+ spacing: 8.w,
+ runSpacing: 8.h,
+ children: [
+ AppCustomChipWidget(
+ labelText: bookAppointmentsVM.isAppointmentNearestGateLoading
+ ? "Floor: Ground Floor"
+ : "Floor: ${getIt.get().isArabic() ? bookAppointmentsViewModel.appointmentNearestGateResponseModel!.clinicLocationN : bookAppointmentsViewModel.appointmentNearestGateResponseModel!.clinicLocation}",
+ ).toShimmer2(isShow: bookAppointmentsVM.isAppointmentNearestGateLoading),
+ AppCustomChipWidget(
+ labelText:
+ "Nearest Gate: ${getIt.get().isArabic() ? bookAppointmentsVM.appointmentNearestGateResponseModel!.nearestGateNumberN : bookAppointmentsVM.appointmentNearestGateResponseModel!.nearestGateNumber}")
+ .toShimmer2(isShow: bookAppointmentsVM.isAppointmentNearestGateLoading),
+ ],
+ ),
+ ],
+ ),
),
),
- ),
- ],
- ),
- ),
+ ],
+ ),
+ );
+ }),
),
),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
- borderRadius: 24.h,
+ borderRadius: 24.r,
hasShadow: true,
),
child: CustomButton(
diff --git a/lib/presentation/book_appointment/select_doctor_page.dart b/lib/presentation/book_appointment/select_doctor_page.dart
index 6045d79..d77e152 100644
--- a/lib/presentation/book_appointment/select_doctor_page.dart
+++ b/lib/presentation/book_appointment/select_doctor_page.dart
@@ -40,8 +40,14 @@ class _SelectDoctorPageState extends State {
late AppState appState;
late BookAppointmentsViewModel bookAppointmentsViewModel;
+ // Scroll controller to control page scrolling when a group expands
+ late ScrollController _scrollController;
+ // Map of keys for each item to allow scrolling to them
+ final Map _itemKeys = {};
+
@override
void initState() {
+ _scrollController = ScrollController();
scheduleMicrotask(() {
bookAppointmentsViewModel.setIsNearestAppointmentSelected(false);
if (bookAppointmentsViewModel.isLiveCareSchedule) {
@@ -59,6 +65,12 @@ class _SelectDoctorPageState extends State {
super.initState();
}
+ @override
+ void dispose() {
+ _scrollController.dispose();
+ super.dispose();
+ }
+
@override
Widget build(BuildContext context) {
bookAppointmentsViewModel = Provider.of(context, listen: false);
@@ -68,6 +80,7 @@ class _SelectDoctorPageState extends State {
body: CollapsingListView(
title: "Choose Doctor".needTranslation,
child: SingleChildScrollView(
+ controller: _scrollController,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 24.h),
child: Consumer(builder: (context, bookAppointmentsVM, child) {
@@ -174,9 +187,7 @@ class _SelectDoctorPageState extends State {
physics: NeverScrollableScrollPhysics(),
itemCount: bookAppointmentsVM.isDoctorsListLoading
? 5
- : (bookAppointmentsVM.isLiveCareSchedule
- ? (bookAppointmentsVM.liveCareDoctorsList.isNotEmpty ? bookAppointmentsVM.liveCareDoctorsList.length : 1)
- : (bookAppointmentsVM.doctorsList.isNotEmpty ? bookAppointmentsVM.doctorsList.length : 1)),
+ : (bookAppointmentsVM.doctorsListGrouped.isNotEmpty ? bookAppointmentsVM.doctorsListGrouped.length : 1),
itemBuilder: (context, index) {
final isExpanded = expandedIndex == index;
return bookAppointmentsVM.isDoctorsListLoading
@@ -185,7 +196,7 @@ class _SelectDoctorPageState extends State {
isLoading: true,
bookAppointmentsViewModel: bookAppointmentsViewModel,
)
- : checkIsDoctorsListEmpty()
+ : bookAppointmentsVM.doctorsListGrouped.isEmpty
? Utils.getNoDataWidget(context, noDataText: "No Doctor found for selected criteria...".needTranslation)
: AnimationConfiguration.staggeredList(
position: index,
@@ -194,6 +205,7 @@ class _SelectDoctorPageState extends State {
verticalOffset: 100.0,
child: FadeInAnimation(
child: AnimatedContainer(
+ key: _itemKeys.putIfAbsent(index, () => GlobalKey()),
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
@@ -202,34 +214,97 @@ class _SelectDoctorPageState extends State {
setState(() {
expandedIndex = isExpanded ? null : index;
});
+ // After rebuild, ensure the expanded item is visible
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ final key = _itemKeys[index];
+ if (key != null && key.currentContext != null && expandedIndex == index) {
+ Scrollable.ensureVisible(
+ key.currentContext!,
+ duration: Duration(milliseconds: 350),
+ curve: Curves.easeInOut,
+ alignment: 0.1,
+ );
+ }
+ });
},
- child: DoctorCard(
- doctorsListResponseModel: bookAppointmentsVM.isLiveCareSchedule ? bookAppointmentsVM.liveCareDoctorsList[index] : bookAppointmentsVM.doctorsList[index],
- isLoading: false,
- bookAppointmentsViewModel: bookAppointmentsViewModel,
- ).onPress(() async {
- bookAppointmentsVM
- .setSelectedDoctor(bookAppointmentsVM.isLiveCareSchedule ? bookAppointmentsVM.liveCareDoctorsList[index] : bookAppointmentsVM.doctorsList[index]);
- // bookAppointmentsVM.setSelectedDoctor(DoctorsListResponseModel());
- LoaderBottomSheet.showLoader();
- await bookAppointmentsVM.getDoctorProfile(onSuccess: (dynamic respData) {
- LoaderBottomSheet.hideLoader();
- Navigator.of(context).push(
- CustomPageRoute(
- page: DoctorProfilePage(),
+ child: Padding(
+ padding: EdgeInsets.all(16.h),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ // Header row with count badge and expand/collapse icon
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ CustomButton(
+ text: "${bookAppointmentsVM.doctorsListGrouped[index].length} ${'doctors'.needTranslation}",
+ onPressed: () {},
+ backgroundColor: AppColors.greyColor,
+ borderColor: AppColors.greyColor,
+ textColor: AppColors.blackColor,
+ fontSize: 10,
+ fontWeight: FontWeight.w500,
+ borderRadius: 8,
+ padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
+ height: 30.h,
+ ),
+ Icon(isExpanded ? Icons.expand_less : Icons.expand_more),
+ ],
),
- );
- }, onError: (err) {
- LoaderBottomSheet.hideLoader();
- showCommonBottomSheetWithoutHeight(
- context,
- child: Utils.getErrorWidget(loadingText: err),
- callBackFunc: () {},
- isFullScreen: false,
- isCloseButtonVisible: true,
- );
- });
- }),
+ SizedBox(height: 8.h),
+ // Clinic/Hospital name as group title
+ Text(
+ bookAppointmentsVM.isSortByClinic
+ ? (bookAppointmentsVM.doctorsListGrouped[index].first.clinicName ?? 'Unknown')
+ : (bookAppointmentsVM.doctorsListGrouped[index].first.projectName ?? 'Unknown'),
+ style: TextStyle(fontSize: 16.h, fontWeight: FontWeight.w600),
+ overflow: TextOverflow.ellipsis,
+ ),
+ // Expanded content - list of doctors in this group
+ AnimatedSwitcher(
+ duration: Duration(milliseconds: 400),
+ child: isExpanded
+ ? Container(
+ key: ValueKey(index),
+ padding: EdgeInsets.only(top: 12.h),
+ child: Column(
+ children: bookAppointmentsVM.doctorsListGrouped[index].map((doctor) {
+ return Container(
+ margin: EdgeInsets.only(bottom: 12.h),
+ child: DoctorCard(
+ doctorsListResponseModel: doctor,
+ isLoading: false,
+ bookAppointmentsViewModel: bookAppointmentsViewModel,
+ ).onPress(() async {
+ bookAppointmentsVM.setSelectedDoctor(doctor);
+ LoaderBottomSheet.showLoader();
+ await bookAppointmentsVM.getDoctorProfile(onSuccess: (dynamic respData) {
+ LoaderBottomSheet.hideLoader();
+ Navigator.of(context).push(
+ CustomPageRoute(
+ page: DoctorProfilePage(),
+ ),
+ );
+ }, onError: (err) {
+ LoaderBottomSheet.hideLoader();
+ showCommonBottomSheetWithoutHeight(
+ context,
+ child: Utils.getErrorWidget(loadingText: err),
+ callBackFunc: () {},
+ isFullScreen: false,
+ isCloseButtonVisible: true,
+ );
+ });
+ }),
+ );
+ }).toList(),
+ ),
+ )
+ : SizedBox.shrink(),
+ ),
+ ],
+ ),
+ ),
),
),
),
diff --git a/lib/presentation/book_appointment/waiting_appointment/waiting_appointment_online_checkin_sheet.dart b/lib/presentation/book_appointment/waiting_appointment/waiting_appointment_online_checkin_sheet.dart
index 8a04843..4a2a304 100644
--- a/lib/presentation/book_appointment/waiting_appointment/waiting_appointment_online_checkin_sheet.dart
+++ b/lib/presentation/book_appointment/waiting_appointment/waiting_appointment_online_checkin_sheet.dart
@@ -142,6 +142,7 @@ class WaitingAppointmentOnlineCheckinSheet extends StatelessWidget {
bookAppointmentsViewModel.waitingAppointmentProjectID,
onSuccess: (value) {
LoaderBottomSheet.hideLoader();
+ bookAppointmentsViewModel.getAppointmentNearestGate(projectID: bookAppointmentsViewModel.waitingAppointmentProjectID, clinicID: bookAppointmentsViewModel.waitingAppointmentDoctor!.clinicID!);
bookAppointmentsViewModel.setIsWaitingAppointmentSelected(true);
Navigator.of(context).push(
CustomPageRoute(
diff --git a/lib/presentation/book_appointment/widgets/appointment_calendar.dart b/lib/presentation/book_appointment/widgets/appointment_calendar.dart
index b2666b6..3a9f57d 100644
--- a/lib/presentation/book_appointment/widgets/appointment_calendar.dart
+++ b/lib/presentation/book_appointment/widgets/appointment_calendar.dart
@@ -185,6 +185,7 @@ class _AppointmentCalendarState extends State {
),
);
} else {
+ bookAppointmentsViewModel.getAppointmentNearestGate(projectID: bookAppointmentsViewModel.selectedDoctor.projectID!, clinicID: bookAppointmentsViewModel.selectedDoctor.clinicID!);
bookAppointmentsViewModel.setSelectedAppointmentDateTime(selectedDate, selectedTime);
Navigator.of(context).pop();
Navigator.of(context).push(
diff --git a/lib/presentation/covid19test/covid19_landing_page.dart b/lib/presentation/covid19test/covid19_landing_page.dart
new file mode 100644
index 0000000..62bd651
--- /dev/null
+++ b/lib/presentation/covid19test/covid19_landing_page.dart
@@ -0,0 +1,283 @@
+import 'dart:async';
+
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
+import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
+import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
+import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart';
+import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
+import 'package:hmg_patient_app_new/theme/colors.dart';
+import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
+import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
+import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
+import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
+import 'package:provider/provider.dart';
+
+import 'covid_19_questionnaire.dart';
+
+class Covid19LandingPage extends StatefulWidget {
+const Covid19LandingPage({super.key});
+
+@override
+State createState() => _Covid19LandingPageState();
+}
+
+class _Covid19LandingPageState extends State {
+
+ late HabibWalletViewModel habibWalletVM;
+ int? _selectedBranchIndex;
+
+ @override
+ void initState() {
+ habibWalletVM = Provider.of(context, listen: false);
+ scheduleMicrotask(() {
+ getProjectList();
+ });
+ super.initState();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+
+ return Scaffold(
+ backgroundColor: AppColors.bgScaffoldColor,
+ body: Column(
+ children: [
+ Expanded(
+ child: CollapsingListView(
+ title: "COVID-19",
+ child: Padding(
+ padding: EdgeInsets.all(24.w),
+ child: SingleChildScrollView(
+ child: Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 24.r,
+ hasShadow: false,
+ ),
+ child: Padding(
+ padding: EdgeInsets.all(20.h),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ "Get the results in Few Hours"
+ .toText18(
+ color: AppColors.textColor,
+ weight: FontWeight.w600,
+ ),
+ SizedBox(height: 16.h),
+ LocaleKeys.covid_info
+ .tr()
+ .toText14(
+ color: AppColors.greyTextColor,
+ weight: FontWeight.w400,
+ height: 1.6,
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ // borderRadius: 24.r,
+ hasShadow: true,
+ customBorder: BorderRadius.only(topLeft:Radius.circular(24.r) , topRight:Radius.circular(24.r))
+ ),
+ child: SizedBox(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ CustomButton(
+ text: "SelectLocation".needTranslation,
+ onPressed: () {
+ _showBranchBottomSheet(context);
+ },
+ backgroundColor: AppColors.primaryRedColor,
+ borderColor: AppColors.primaryRedColor,
+ textColor: AppColors.whiteColor,
+ fontSize: 16.f,
+ fontWeight: FontWeight.w500,
+ borderRadius: 12.r,
+ padding: EdgeInsets.symmetric(horizontal: 10.w),
+ height: 50.h,
+ iconSize: 18.h,
+ ).paddingSymmetrical(16.h, 24.w),
+ ],
+ ),
+ ),
+ ),
+ ],
+ ));
+ }
+ void _showBranchBottomSheet(BuildContext context, ) {
+ // Set first branch as selected by default
+ setState(() {
+ _selectedBranchIndex = 0;
+ });
+
+ showCommonBottomSheet(
+
+ context,
+ title: "Select Branch".needTranslation,
+ height: ResponsiveExtension.screenHeight * 0.651,
+ child: StatefulBuilder(
+ builder: (context, setBottomSheetState) {
+ return Consumer(
+ builder: (context, habibWalletVM, child) {
+
+ final hospitals = habibWalletVM.advancePaymentHospitals;
+ if (hospitals.isEmpty) {
+ return const Center(
+ child: Padding(
+ padding: EdgeInsets.all(16.0),
+ child: Text('No branches available'),
+ ),
+ );
+ }
+
+ return Column(
+ children: [
+ Expanded(
+ child:Container(
+ margin: EdgeInsets.only(left: 16.w, right: 16.w, top: 12.h, bottom: 24.h),
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ hasShadow: true,
+ customBorder: BorderRadius.only(
+ topLeft: Radius.circular(24.r),
+ topRight: Radius.circular(24.r),
+ ),
+ ), child: ListView.separated(
+ shrinkWrap: true,
+ physics: const BouncingScrollPhysics(),
+ padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h),
+ itemBuilder: (context, index) {
+ final branch = hospitals[index];
+ final isSelected = _selectedBranchIndex == index;
+ return GestureDetector(
+ onTap: () {
+ setBottomSheetState(() {
+ _selectedBranchIndex = index;
+ });
+ setState(() {
+ _selectedBranchIndex = index;
+ });
+ },
+ child: Container(
+
+ padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 16.h),
+ child: Row(
+ children: [
+ // Radio button
+ Container(
+ width: 20.w,
+ height: 20.h,
+ decoration: BoxDecoration(
+ shape: BoxShape.circle,
+ border: Border.all(
+ color: isSelected
+ ? AppColors.primaryRedColor
+ : AppColors.greyTextColor.withValues(alpha: 0.3),
+ width: 2,
+ ),
+ ),
+ child: isSelected
+ ? Center(
+ child: Container(
+ width: 10.w,
+ height: 10.h,
+ decoration: BoxDecoration(
+ shape: BoxShape.circle,
+ color: AppColors.primaryRedColor,
+ ),
+ ),
+ )
+ : null,
+ ),
+ SizedBox(width: 12.w),
+ // Branch details
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+
+ SizedBox(height: 4.h),
+ (branch.desciption ?? 'Unknown').toText14(
+ color: AppColors.textColor,
+ weight: FontWeight.w600,
+ ),
+ ],
+ ),
+ ),
+ // Location icon
+
+ ],
+ ),
+ ),
+ );
+ },
+ separatorBuilder: (context, index) => SizedBox(height: 12.h),
+ itemCount: hospitals.length,
+ )),
+ ),
+ // Next button
+ Container(
+ padding: EdgeInsets.symmetric(horizontal: 24.w, vertical: 16.h),
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ hasShadow: true,
+ customBorder: BorderRadius.only(
+ topLeft: Radius.circular(24.r),
+ topRight: Radius.circular(24.r),
+ ),
+ ),
+ child: SafeArea(
+ top: false,
+ child: CustomButton(
+ text: "Next".needTranslation,
+ onPressed: (){
+
+ Navigator.of(context)
+ .push(
+ CustomPageRoute(
+ page: Covid19Questionnaire(selectedHospital: hospitals[_selectedBranchIndex!],),
+ ),
+ );
+
+ },
+ backgroundColor: _selectedBranchIndex != null
+ ? AppColors.primaryRedColor
+ : AppColors.greyTextColor.withValues(alpha: 0.3),
+ borderColor: _selectedBranchIndex != null
+ ? AppColors.primaryRedColor
+ : AppColors.greyTextColor.withValues(alpha: 0.3),
+ textColor: AppColors.whiteColor,
+ fontSize: 16.f,
+ fontWeight: FontWeight.w600,
+ borderRadius: 12.r,
+ height: 56.h,
+ ),
+ ),
+ ),
+ ],
+ );
+ },
+ );
+ },
+ ),
+
+ );
+ }
+
+ getProjectList() async{
+ await habibWalletVM.getProjectsList();
+ }
+}
+
+
diff --git a/lib/presentation/covid19test/covid_19_questionnaire.dart b/lib/presentation/covid19test/covid_19_questionnaire.dart
new file mode 100644
index 0000000..8d7e8bd
--- /dev/null
+++ b/lib/presentation/covid19test/covid_19_questionnaire.dart
@@ -0,0 +1,132 @@
+import 'dart:async';
+
+import 'package:flutter/material.dart';
+import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
+import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
+import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
+import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart';
+import 'package:hmg_patient_app_new/features/hmg_services/models/ui_models/covid_questionnare_model.dart';
+import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart';
+import 'package:hmg_patient_app_new/theme/colors.dart';
+import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
+import 'package:hmg_patient_app_new/widgets/CustomSwitch.dart';
+import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
+
+import 'package:provider/provider.dart';
+
+class Covid19Questionnaire extends StatefulWidget {
+ final HospitalsModel selectedHospital;
+ const Covid19Questionnaire({super.key, required this.selectedHospital});
+
+ @override
+ State createState() => _Covid19QuestionnaireState();
+}
+
+class _Covid19QuestionnaireState extends State {
+ late HmgServicesViewModel hmgServicesViewModel;
+ List qaList = [];
+
+
+ @override
+ void initState() {
+ hmgServicesViewModel = Provider.of(context, listen: false);
+ scheduleMicrotask(() {
+ setState(() {
+ qaList = hmgServicesViewModel.getQuestionsFromJson();
+ });
+ });
+ super.initState();
+ }
+
+ void _toggleAnswer(int index, bool value) {
+ setState(() {
+ qaList[index].ans = value ? 1 : 0;
+ });
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ backgroundColor: AppColors.bgScaffoldColor,
+ body: Column(children: [
+ Expanded(
+ child: CollapsingListView(
+ title: "COVID-19",
+ child: Padding(
+ padding: EdgeInsets.all(24.w),
+ child: Column(
+ children: [
+ Expanded(
+ child: SingleChildScrollView(
+ child: Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 24.r,
+ hasShadow: false,
+ ),
+ child: Padding(
+ padding: EdgeInsets.all(20.h),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ "Please answer below questionnaire:".toText14(
+ color: AppColors.textColor,
+ weight: FontWeight.w500,
+ ),
+ SizedBox(height: 20.h),
+ // Question list
+ ListView.separated(
+ shrinkWrap: true,
+ physics: const NeverScrollableScrollPhysics(),
+ itemCount: qaList.length,
+ separatorBuilder: (context, index) => SizedBox(height: 16.h),
+ itemBuilder: (context, index) {
+ final question = qaList[index];
+ final isAnswerYes = question.ans == 1;
+
+ return Row(
+ children: [
+ Expanded(
+ child: (question.questionEn ?? '').toText14(
+ color: AppColors.textColor,
+ weight: FontWeight.w400,
+ ),
+ ),
+ SizedBox(width: 12.w),
+ CustomSwitch(
+ value: isAnswerYes,
+ onChanged: (value) => _toggleAnswer(index, value),
+ ),
+ ],
+ );
+ },
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ ),
+ SizedBox(height: 16.h),
+ // Next button
+ CustomButton(
+ text: "Next".needTranslation,
+ onPressed: () {
+ // Handle next action
+ },
+ backgroundColor: AppColors.primaryRedColor,
+ borderColor: AppColors.primaryRedColor,
+ textColor: AppColors.whiteColor,
+ fontSize: 16.f,
+ fontWeight: FontWeight.w600,
+ borderRadius: 12.r,
+ height: 56.h,
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ ]));
+ }
+}
diff --git a/lib/presentation/e_referral/widget/e_referral_other_details.dart b/lib/presentation/e_referral/widget/e_referral_other_details.dart
index dfde8e7..6efd063 100644
--- a/lib/presentation/e_referral/widget/e_referral_other_details.dart
+++ b/lib/presentation/e_referral/widget/e_referral_other_details.dart
@@ -232,7 +232,6 @@ class _OtherDetailsStepState extends State {
}
void _showBranchBottomSheet(BuildContext context, ReferralFormManager formManager) {
- final habibWalletVM = context.read();
showCommonBottomSheetWithoutHeight(
context,
diff --git a/lib/presentation/health_calculators/health_calculator_result.dart b/lib/presentation/health_calculators/health_calculator_result.dart
deleted file mode 100644
index e69de29..0000000
diff --git a/lib/presentation/health_calculators_and_converts/health_calculator_view_model.dart b/lib/presentation/health_calculators_and_converts/health_calculator_view_model.dart
index 4a78549..c085ac8 100644
--- a/lib/presentation/health_calculators_and_converts/health_calculator_view_model.dart
+++ b/lib/presentation/health_calculators_and_converts/health_calculator_view_model.dart
@@ -49,28 +49,41 @@ class HealthCalcualtorViewModel extends ChangeNotifier {
String? get mmolValue => _mmolValue;
+ // Store the original entered value and which field it was entered in
+ String? _originalValue;
+ String _originalUnit = 'mg/dL'; // which field the original value was entered in
+ bool _isSwapped = false; // toggle state for switch
+
String _activeUnit = 'mg/dL'; // current source unit
String get activeUnit => _activeUnit;
-
// ================== BLOOD CHOLESTEROL ==================
String? _cholMgdlValue;
String? _cholMmolValue;
+ String? _cholOriginalValue;
+ String _cholOriginalUnit = 'mg/dL';
+ bool _cholIsSwapped = false;
String _cholActiveUnit = 'mg/dL';
String? get cholMgdlValue => _cholMgdlValue;
+
String? get cholMmolValue => _cholMmolValue;
+
String get cholActiveUnit => _cholActiveUnit;
// ================== TRIGLYCERIDES ==================
String? _triMgdlValue;
String? _triMmolValue;
+ String? _triOriginalValue;
+ String _triOriginalUnit = 'mg/dL';
+ bool _triIsSwapped = false;
String _triActiveUnit = 'mg/dL';
String? get triMgdlValue => _triMgdlValue;
+
String? get triMmolValue => _triMmolValue;
- String get triActiveUnit => _triActiveUnit;
+ String get triActiveUnit => _triActiveUnit;
// Generic helpers
@@ -726,13 +739,10 @@ class HealthCalcualtorViewModel extends ChangeNotifier {
final DateTime firstTrimesterStart = lmp;
final DateTime firstTrimesterEnd = lmp.add(const Duration(days: 83));
- final DateTime secondTrimesterStart =
- firstTrimesterEnd.add(const Duration(days: 1));
- final DateTime secondTrimesterEnd =
- lmp.add(const Duration(days: 195));
+ final DateTime secondTrimesterStart = firstTrimesterEnd.add(const Duration(days: 1));
+ final DateTime secondTrimesterEnd = lmp.add(const Duration(days: 195));
- final DateTime thirdTrimesterStart =
- secondTrimesterEnd.add(const Duration(days: 1));
+ final DateTime thirdTrimesterStart = secondTrimesterEnd.add(const Duration(days: 1));
final DateTime thirdTrimesterEnd = dueDate;
deliveryResult = {
@@ -741,7 +751,7 @@ class HealthCalcualtorViewModel extends ChangeNotifier {
// Raw DateTime (useful for logic/UI)
'lmpDateTime': lmp,
'dueDateTime': dueDate,
- 'dueDateDay' : _formatDateWithDayName(dueDate),
+ 'dueDateDay': _formatDateWithDayName(dueDate),
// Trimester info
'firstTrimester': {
'start': _formatDate(firstTrimesterStart),
@@ -769,7 +779,6 @@ class HealthCalcualtorViewModel extends ChangeNotifier {
notifyListeners();
}
-
// Blood sugar conversions
void calculateBloodSugar({required String valueText, required String unit}) {
if (valueText.trim().isEmpty) {
@@ -838,8 +847,6 @@ class HealthCalcualtorViewModel extends ChangeNotifier {
'dietType': dietType
};
-
-
void onBloodSugarChanged(String value, String fromUnit) {
_activeUnit = fromUnit;
@@ -855,47 +862,105 @@ class HealthCalcualtorViewModel extends ChangeNotifier {
if (fromUnit == 'mg/dL') {
_mgdlValue = value;
- _mmolValue = (parsed / 18.0182).toStringAsFixed(1);
+ _mmolValue = (parsed / 18.0182).toStringAsFixed(3);
} else {
_mmolValue = value;
- _mgdlValue = (parsed * 18.0182).toStringAsFixed(0);
+ _mgdlValue = (parsed * 18.0182).toStringAsFixed(3);
}
notifyListeners();
}
- void switchBloodSugarValues() {
- if (_activeUnit == 'mg/dL') {
- final mmol = double.tryParse(_mmolValue ?? '');
- if (mmol == null) return;
+ void onBloodSugarMgdlChanged(String value) {
+ _mgdlValue = value;
+ _originalValue = value;
+ _originalUnit = 'mg/dL';
+ _isSwapped = false;
+ _activeUnit = 'mg/dL';
+
+ if (value.isEmpty) {
+ _mmolValue = null;
+ _originalValue = null;
+ notifyListeners();
+ return;
+ }
- _activeUnit = 'mmol/L';
+ final parsed = double.tryParse(value);
+ if (parsed == null) return;
- // mmol stays as source
- _mmolValue = mmol.toStringAsFixed(1);
- _mgdlValue = (mmol * 18.0182).toStringAsFixed(0);
- } else {
- final mgdl = double.tryParse(_mgdlValue ?? '');
- if (mgdl == null) return;
+ // Convert mg/dL to mmol/L
+ _mmolValue = (parsed / 18.0182).toStringAsFixed(3);
+ notifyListeners();
+ }
- _activeUnit = 'mg/dL';
+ void onBloodSugarMmolChanged(String value) {
+ _mmolValue = value;
+ _originalValue = value;
+ _originalUnit = 'mmol/L';
+ _isSwapped = false;
+ _activeUnit = 'mmol/L';
- // mg/dL stays as source
- _mgdlValue = mgdl.toStringAsFixed(0);
- _mmolValue = (mgdl / 18.0182).toStringAsFixed(1);
+ if (value.isEmpty) {
+ _mgdlValue = null;
+ _originalValue = null;
+ notifyListeners();
+ return;
}
+ final parsed = double.tryParse(value);
+ if (parsed == null) return;
+
+ // Convert mmol/L to mg/dL
+ _mgdlValue = (parsed * 18.0182).toStringAsFixed(3);
notifyListeners();
}
+
+ void switchBloodSugarValues() {
+ // Toggle between two states using the original entered value
+ if (_originalValue == null || _originalValue!.isEmpty) return;
+
+ final originalParsed = double.tryParse(_originalValue!);
+ if (originalParsed == null) return;
+
+ _isSwapped = !_isSwapped;
+
+ if (_originalUnit == 'mg/dL') {
+ if (_isSwapped) {
+ // Original was in mg/dL, now treat it as mmol/L
+ _mmolValue = _originalValue; // Keep original format
+ _mgdlValue = (originalParsed * 18.0182).toStringAsFixed(3);
+ } else {
+ // Back to original: value in mg/dL
+ _mgdlValue = _originalValue; // Keep original format
+ _mmolValue = (originalParsed / 18.0182).toStringAsFixed(3);
+ }
+ } else {
+ // Original was in mmol/L
+ if (_isSwapped) {
+ // Now treat it as mg/dL
+ _mgdlValue = _originalValue; // Keep original format
+ _mmolValue = (originalParsed / 18.0182).toStringAsFixed(3);
+ } else {
+ // Back to original: value in mmol/L
+ _mmolValue = _originalValue; // Keep original format
+ _mgdlValue = (originalParsed * 18.0182).toStringAsFixed(3);
+ }
+ }
+
+ notifyListeners();
+ }
+
// --- NEW: Method to clear the values ---
void clearBloodSugar() {
_mgdlValue = null;
_mmolValue = null;
+ _originalValue = null;
+ _originalUnit = 'mg/dL';
+ _isSwapped = false;
+ _activeUnit = 'mg/dL';
notifyListeners();
}
-
-
void onBloodCholesterolChanged(String value, String fromUnit) {
_cholActiveUnit = fromUnit;
@@ -911,47 +976,104 @@ class HealthCalcualtorViewModel extends ChangeNotifier {
if (fromUnit == 'mg/dL') {
_cholMgdlValue = value;
- _cholMmolValue = (parsed / 38.67).toStringAsFixed(2);
+ _cholMmolValue = (parsed / 38.67).toStringAsFixed(3);
} else {
_cholMmolValue = value;
- _cholMgdlValue = (parsed * 38.67).toStringAsFixed(0);
+ _cholMgdlValue = (parsed * 38.67).toStringAsFixed(3);
}
notifyListeners();
}
- void switchBloodCholesterolValues() {
- if (_cholActiveUnit == 'mg/dL') {
- final mmol = double.tryParse(_cholMmolValue ?? '');
- if (mmol == null) return;
+ void onCholesterolMgdlChanged(String value) {
+ _cholMgdlValue = value;
+ _cholOriginalValue = value;
+ _cholOriginalUnit = 'mg/dL';
+ _cholIsSwapped = false;
+ _cholActiveUnit = 'mg/dL';
+
+ if (value.isEmpty) {
+ _cholMmolValue = null;
+ _cholOriginalValue = null;
+ notifyListeners();
+ return;
+ }
- _cholActiveUnit = 'mmol/L';
+ final parsed = double.tryParse(value);
+ if (parsed == null) return;
- _cholMmolValue = mmol.toStringAsFixed(2);
- _cholMgdlValue = (mmol * 38.67).toStringAsFixed(0);
- } else {
- final mgdl = double.tryParse(_cholMgdlValue ?? '');
- if (mgdl == null) return;
+ // Convert mg/dL to mmol/L
+ _cholMmolValue = (parsed / 38.67).toStringAsFixed(3);
+ notifyListeners();
+ }
- _cholActiveUnit = 'mg/dL';
+ void onCholesterolMmolChanged(String value) {
+ _cholMmolValue = value;
+ _cholOriginalValue = value;
+ _cholOriginalUnit = 'mmol/L';
+ _cholIsSwapped = false;
+ _cholActiveUnit = 'mmol/L';
- _cholMgdlValue = mgdl.toStringAsFixed(0);
- _cholMmolValue = (mgdl / 38.67).toStringAsFixed(2);
+ if (value.isEmpty) {
+ _cholMgdlValue = null;
+ _cholOriginalValue = null;
+ notifyListeners();
+ return;
}
+ final parsed = double.tryParse(value);
+ if (parsed == null) return;
+
+ // Convert mmol/L to mg/dL
+ _cholMgdlValue = (parsed * 38.67).toStringAsFixed(3);
notifyListeners();
}
+ void switchBloodCholesterolValues() {
+ // Toggle between two states using the original entered value
+ if (_cholOriginalValue == null || _cholOriginalValue!.isEmpty) return;
+
+ final originalParsed = double.tryParse(_cholOriginalValue!);
+ if (originalParsed == null) return;
+
+ _cholIsSwapped = !_cholIsSwapped;
+
+ if (_cholOriginalUnit == 'mg/dL') {
+ if (_cholIsSwapped) {
+ // Original was in mg/dL, now treat it as mmol/L
+ _cholMmolValue = _cholOriginalValue; // Keep original format
+ _cholMgdlValue = (originalParsed * 38.67).toStringAsFixed(3);
+ } else {
+ // Back to original: value in mg/dL
+ _cholMgdlValue = _cholOriginalValue; // Keep original format
+ _cholMmolValue = (originalParsed / 38.67).toStringAsFixed(3);
+ }
+ } else {
+ // Original was in mmol/L
+ if (_cholIsSwapped) {
+ // Now treat it as mg/dL
+ _cholMgdlValue = _cholOriginalValue; // Keep original format
+ _cholMmolValue = (originalParsed / 38.67).toStringAsFixed(3);
+ } else {
+ // Back to original: value in mmol/L
+ _cholMmolValue = _cholOriginalValue; // Keep original format
+ _cholMgdlValue = (originalParsed * 38.67).toStringAsFixed(3);
+ }
+ }
+
+ notifyListeners();
+ }
void clearBloodCholesterol() {
_cholMgdlValue = null;
_cholMmolValue = null;
+ _cholOriginalValue = null;
+ _cholOriginalUnit = 'mg/dL';
+ _cholIsSwapped = false;
_cholActiveUnit = 'mg/dL';
notifyListeners();
}
-
-
void onTriglyceridesChanged(String value, String fromUnit) {
_triActiveUnit = fromUnit;
@@ -967,30 +1089,89 @@ class HealthCalcualtorViewModel extends ChangeNotifier {
if (fromUnit == 'mg/dL') {
_triMgdlValue = value;
- _triMmolValue = (parsed / 88.57).toStringAsFixed(2);
+ _triMmolValue = (parsed / 88.57).toStringAsFixed(3);
} else {
_triMmolValue = value;
- _triMgdlValue = (parsed * 88.57).toStringAsFixed(0);
+ _triMgdlValue = (parsed * 88.57).toStringAsFixed(3);
}
notifyListeners();
}
+ void onTriglyceridesMgdlChanged(String value) {
+ _triMgdlValue = value;
+ _triOriginalValue = value;
+ _triOriginalUnit = 'mg/dL';
+ _triIsSwapped = false;
+ _triActiveUnit = 'mg/dL';
+
+ if (value.isEmpty) {
+ _triMmolValue = null;
+ _triOriginalValue = null;
+ notifyListeners();
+ return;
+ }
+
+ final parsed = double.tryParse(value);
+ if (parsed == null) return;
+
+ // Convert mg/dL to mmol/L
+ _triMmolValue = (parsed / 88.57).toStringAsFixed(3);
+ notifyListeners();
+ }
+
+ void onTriglyceridesMmolChanged(String value) {
+ _triMmolValue = value;
+ _triOriginalValue = value;
+ _triOriginalUnit = 'mmol/L';
+ _triIsSwapped = false;
+ _triActiveUnit = 'mmol/L';
+
+ if (value.isEmpty) {
+ _triMgdlValue = null;
+ _triOriginalValue = null;
+ notifyListeners();
+ return;
+ }
+
+ final parsed = double.tryParse(value);
+ if (parsed == null) return;
+
+ // Convert mmol/L to mg/dL
+ _triMgdlValue = (parsed * 88.57).toStringAsFixed(3);
+ notifyListeners();
+ }
+
void switchTriglyceridesValues() {
- if (_triActiveUnit == 'mg/dL') {
- final mmol = double.tryParse(_triMmolValue ?? '');
- if (mmol == null) return;
+ // Toggle between two states using the original entered value
+ if (_triOriginalValue == null || _triOriginalValue!.isEmpty) return;
- _triActiveUnit = 'mmol/L';
- _triMmolValue = mmol.toStringAsFixed(2);
- _triMgdlValue = (mmol * 88.57).toStringAsFixed(0);
- } else {
- final mgdl = double.tryParse(_triMgdlValue ?? '');
- if (mgdl == null) return;
+ final originalParsed = double.tryParse(_triOriginalValue!);
+ if (originalParsed == null) return;
+
+ _triIsSwapped = !_triIsSwapped;
- _triActiveUnit = 'mg/dL';
- _triMgdlValue = mgdl.toStringAsFixed(0);
- _triMmolValue = (mgdl / 88.57).toStringAsFixed(2);
+ if (_triOriginalUnit == 'mg/dL') {
+ if (_triIsSwapped) {
+ // Original was in mg/dL, now treat it as mmol/L
+ _triMmolValue = _triOriginalValue; // Keep original format
+ _triMgdlValue = (originalParsed * 88.57).toStringAsFixed(3);
+ } else {
+ // Back to original: value in mg/dL
+ _triMgdlValue = _triOriginalValue; // Keep original format
+ _triMmolValue = (originalParsed / 88.57).toStringAsFixed(3);
+ }
+ } else {
+ // Original was in mmol/L
+ if (_triIsSwapped) {
+ // Now treat it as mg/dL
+ _triMgdlValue = _triOriginalValue; // Keep original format
+ _triMmolValue = (originalParsed / 88.57).toStringAsFixed(3);
+ } else {
+ // Back to original: value in mmol/L
+ _triMmolValue = _triOriginalValue; // Keep original format
+ _triMgdlValue = (originalParsed * 88.57).toStringAsFixed(3);
+ }
}
notifyListeners();
@@ -999,10 +1180,10 @@ class HealthCalcualtorViewModel extends ChangeNotifier {
void clearTriglycerides() {
_triMgdlValue = null;
_triMmolValue = null;
+ _triOriginalValue = null;
+ _triOriginalUnit = 'mg/dL';
+ _triIsSwapped = false;
_triActiveUnit = 'mg/dL';
notifyListeners();
}
-
-
-
}
diff --git a/lib/presentation/health_calculators_and_converts/widgets/blood_cholesterol.dart b/lib/presentation/health_calculators_and_converts/widgets/blood_cholesterol.dart
index 161f318..e3bc7ff 100644
--- a/lib/presentation/health_calculators_and_converts/widgets/blood_cholesterol.dart
+++ b/lib/presentation/health_calculators_and_converts/widgets/blood_cholesterol.dart
@@ -52,14 +52,15 @@ class _BloodCholesterolWidgetState extends State {
final mgdlText = provider.cholMgdlValue ?? '';
final mmolText = provider.cholMmolValue ?? '';
- if (_mgdlController.text != mgdlText) {
+ // Only update if focus is not on this field (to avoid cursor jumping)
+ if (!_mgdlFocus.hasFocus && _mgdlController.text != mgdlText) {
_mgdlController.text = mgdlText;
_mgdlController.selection = TextSelection.fromPosition(
TextPosition(offset: _mgdlController.text.length),
);
}
- if (_mmolController.text != mmolText) {
+ if (!_mmolFocus.hasFocus && _mmolController.text != mmolText) {
_mmolController.text = mmolText;
_mmolController.selection = TextSelection.fromPosition(
TextPosition(offset: _mmolController.text.length),
@@ -81,7 +82,7 @@ class _BloodCholesterolWidgetState extends State {
focusNode: _mgdlFocus,
onChanged: (value) {
if (_isProgrammaticChange) return;
- provider.onBloodCholesterolChanged(value, 'mg/dL');
+ provider.onCholesterolMgdlChanged(value);
},
).paddingOnly(top: 16.h),
Row(
@@ -96,24 +97,21 @@ class _BloodCholesterolWidgetState extends State {
width: 40.h,
height: 40.h,
).onPress(() {
+ // Unfocus both fields before switching
+ _mgdlFocus.unfocus();
+ _mmolFocus.unfocus();
provider.switchBloodCholesterolValues();
- if (provider.cholActiveUnit == 'mg/dL') {
- _mgdlFocus.requestFocus();
- } else {
- _mmolFocus.requestFocus();
- }
}),
],
),
_buildInputField(
label: "MMOL/L",
- hint: "6.7",
+ hint: "3.1",
controller: _mmolController,
focusNode: _mmolFocus,
onChanged: (value) {
if (_isProgrammaticChange) return;
-
- provider.onBloodCholesterolChanged(value, 'mmol/L');
+ provider.onCholesterolMmolChanged(value);
},
).paddingOnly(bottom: 16.h),
const Divider(height: 1, color: Color(0xFFEEEEEE)),
diff --git a/lib/presentation/health_calculators_and_converts/widgets/blood_sugar.dart b/lib/presentation/health_calculators_and_converts/widgets/blood_sugar.dart
index 86b4c1e..cf7cc61 100644
--- a/lib/presentation/health_calculators_and_converts/widgets/blood_sugar.dart
+++ b/lib/presentation/health_calculators_and_converts/widgets/blood_sugar.dart
@@ -51,14 +51,16 @@ class _BloodSugarWidgetState extends State {
_isProgrammaticChange = true;
final mgdlText = provider.mgdlValue ?? '';
final mmolText = provider.mmolValue ?? '';
- if (_mgdlController.text != mgdlText) {
+
+ // Only update if focus is not on this field (to avoid cursor jumping)
+ if (!_mgdlFocus.hasFocus && _mgdlController.text != mgdlText) {
_mgdlController.text = mgdlText;
_mgdlController.selection = TextSelection.fromPosition(
TextPosition(offset: _mgdlController.text.length),
);
}
- if (_mmolController.text != mmolText) {
+ if (!_mmolFocus.hasFocus && _mmolController.text != mmolText) {
_mmolController.text = mmolText;
_mmolController.selection = TextSelection.fromPosition(
TextPosition(offset: _mmolController.text.length),
@@ -84,7 +86,7 @@ class _BloodSugarWidgetState extends State {
focusNode: _mgdlFocus,
onChanged: (value) {
if (_isProgrammaticChange) return;
- provider.onBloodSugarChanged(value, 'mg/dL');
+ provider.onBloodSugarMgdlChanged(value);
},
).paddingOnly(top: 16.h),
Row(
@@ -99,12 +101,10 @@ class _BloodSugarWidgetState extends State {
width: 40.h,
height: 40.h,
).onPress(() {
+ // Unfocus both fields before switching
+ _mgdlFocus.unfocus();
+ _mmolFocus.unfocus();
provider.switchBloodSugarValues();
- if (provider.activeUnit == 'mg/dL') {
- _mgdlFocus.requestFocus();
- } else {
- _mmolFocus.requestFocus();
- }
}),
],
),
@@ -115,8 +115,7 @@ class _BloodSugarWidgetState extends State {
focusNode: _mmolFocus,
onChanged: (value) {
if (_isProgrammaticChange) return;
-
- provider.onBloodSugarChanged(value, 'mmol/L');
+ provider.onBloodSugarMmolChanged(value);
},
).paddingOnly(bottom: 16.h),
const Divider(height: 1, color: Color(0xFFEEEEEE)),
diff --git a/lib/presentation/health_calculators_and_converts/widgets/triglycerides.dart b/lib/presentation/health_calculators_and_converts/widgets/triglycerides.dart
index 17ad1f9..1c8c99e 100644
--- a/lib/presentation/health_calculators_and_converts/widgets/triglycerides.dart
+++ b/lib/presentation/health_calculators_and_converts/widgets/triglycerides.dart
@@ -53,14 +53,15 @@ class _TriglyceridesWidgetState extends State {
final mgdlText = provider.triMgdlValue ?? '';
final mmolText = provider.triMmolValue ?? '';
- if (_mgdlController.text != mgdlText) {
+ // Only update if focus is not on this field (to avoid cursor jumping)
+ if (!_mgdlFocus.hasFocus && _mgdlController.text != mgdlText) {
_mgdlController.text = mgdlText;
_mgdlController.selection = TextSelection.fromPosition(
TextPosition(offset: _mgdlController.text.length),
);
}
- if (_mmolController.text != mmolText) {
+ if (!_mmolFocus.hasFocus && _mmolController.text != mmolText) {
_mmolController.text = mmolText;
_mmolController.selection = TextSelection.fromPosition(
TextPosition(offset: _mmolController.text.length),
@@ -85,7 +86,7 @@ class _TriglyceridesWidgetState extends State {
focusNode: _mgdlFocus,
onChanged: (value) {
if (_isProgrammaticChange) return;
- provider.onTriglyceridesChanged(value, 'mg/dL');
+ provider.onTriglyceridesMgdlChanged(value);
},
).paddingOnly(top: 16.h),
@@ -101,13 +102,10 @@ class _TriglyceridesWidgetState extends State {
width: 40.h,
height: 40.h,
).onPress(() {
+ // Unfocus both fields before switching
+ _mgdlFocus.unfocus();
+ _mmolFocus.unfocus();
provider.switchTriglyceridesValues();
-
- if (provider.triActiveUnit == 'mg/dL') {
- _mgdlFocus.requestFocus();
- } else {
- _mmolFocus.requestFocus();
- }
}),
],
),
@@ -119,7 +117,7 @@ class _TriglyceridesWidgetState extends State {
focusNode: _mmolFocus,
onChanged: (value) {
if (_isProgrammaticChange) return;
- provider.onTriglyceridesChanged(value, 'mmol/L');
+ provider.onTriglyceridesMmolChanged(value);
},
).paddingOnly(bottom: 16.h),
@@ -170,7 +168,6 @@ class _TriglyceridesWidgetState extends State {
child: TextField(
controller: controller,
focusNode: focusNode,
-
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
onChanged: onChanged,
@@ -185,6 +182,7 @@ class _TriglyceridesWidgetState extends State {
style: TextStyle(
fontSize: 32.f,
fontWeight: FontWeight.bold,
+ color: Colors.black87,
),
),
),
diff --git a/lib/presentation/hmg_services/services_page.dart b/lib/presentation/hmg_services/services_page.dart
index e8a095d..bf0b7f5 100644
--- a/lib/presentation/hmg_services/services_page.dart
+++ b/lib/presentation/hmg_services/services_page.dart
@@ -100,6 +100,16 @@ class ServicesPage extends StatelessWidget {
LoaderBottomSheet.hideLoader();
});
}),
+ HmgServicesComponentModel(
+ 11,
+ "Covid 19 Test".needTranslation,
+ "".needTranslation,
+ AppAssets.covid19icon,
+ bgColor: AppColors.covid29Color,
+ true,
+ route: AppRoutes.covid19Test,
+ )
+
// HmgServicesComponentModel(
// 3,
// "Home Health Care".needTranslation,
diff --git a/lib/presentation/lab/alphabeticScroll.dart b/lib/presentation/lab/alphabeticScroll.dart
index 5537347..29ded69 100644
--- a/lib/presentation/lab/alphabeticScroll.dart
+++ b/lib/presentation/lab/alphabeticScroll.dart
@@ -120,9 +120,7 @@ class _AlphabetScrollPageState extends State {
@override
Widget build(BuildContext context) {
- return
-
- SizedBox(
+ return SizedBox(
width: MediaQuery.sizeOf(context).width,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start, // Add this
diff --git a/lib/presentation/lab/lab_orders_page.dart b/lib/presentation/lab/lab_orders_page.dart
index 93275f3..90651f1 100644
--- a/lib/presentation/lab/lab_orders_page.dart
+++ b/lib/presentation/lab/lab_orders_page.dart
@@ -1 +1 @@
-import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.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/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart';
import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_result_via_clinic/LabResultByClinic.dart';
import 'package:hmg_patient_app_new/presentation/lab/search_lab_report.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_toolbar.dart';
import 'package:hmg_patient_app_new/widgets/chip/custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart';
import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_view_model.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:provider/provider.dart';
import 'alphabeticScroll.dart';
class LabOrdersPage extends StatefulWidget {
const LabOrdersPage({super.key});
@override
State createState() => _LabOrdersPageState();
}
class _LabOrdersPageState extends State {
late LabViewModel labProvider;
late DateRangeSelectorRangeViewModel rangeViewModel;
late AppState _appState;
List?> labSuggestions = [];
int? expandedIndex;
String? selectedFilterText = '';
int activeIndex = 0;
// Scroll controller to control page scrolling when a group expands
late ScrollController _scrollController;
// Map of keys for each group to allow scrolling to them
final Map _groupKeys = {};
@override
void initState() {
_scrollController = ScrollController();
scheduleMicrotask(() {
labProvider.initLabProvider();
});
super.initState();
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
labProvider = Provider.of(context, listen: false);
rangeViewModel = Provider.of(context);
_appState = getIt();
return CollapsingToolbar(
title: LocaleKeys.labResults.tr(),
search: () async {
final lavVM = Provider.of(context, listen: false);
if (lavVM.isLabOrdersLoading) {
return;
} else {
String? value = await Navigator.of(context).push(
CustomPageRoute(
page: SearchLabResultsContent(labSuggestionsList: lavVM.labSuggestions),
fullScreenDialog: true,
direction: AxisDirection.down,
),
);
if (value != null) {
selectedFilterText = value;
lavVM.filterLabReports(value);
}
}
},
child: SingleChildScrollView(
controller: _scrollController,
padding: EdgeInsets.all(24.h),
physics: AlwaysScrollableScrollPhysics(),
child: Consumer(
builder: (context, model, child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: CustomTabBar(
activeTextColor: Color(0xffED1C2B),
activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1),
tabs: [
CustomTabBarModel(null, "By Visit".needTranslation),
CustomTabBarModel(null, "By Test".needTranslation),
// CustomTabBarModel(null, "Completed".needTranslation),
],
onTabChange: (index) {
activeIndex = index;
setState(() {});
},
),
),
],
),
// Place Clinic/Hospital buttons under the tabs, styled like prescriptions
if (activeIndex == 0)
Padding(
padding: EdgeInsets.symmetric(vertical: 10.h),
child: Row(
children: [
CustomButton(
text: LocaleKeys.byClinic.tr(context: context),
onPressed: () {
model.setIsSortByClinic(true);
},
backgroundColor: model.isSortByClinic ? AppColors.bgRedLightColor : AppColors.whiteColor,
borderColor: model.isSortByClinic ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2),
textColor: model.isSortByClinic ? AppColors.primaryRedColor : AppColors.blackColor,
fontSize: 12,
fontWeight: FontWeight.w500,
borderRadius: 10,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
),
SizedBox(width: 8.h),
CustomButton(
text: LocaleKeys.byHospital.tr(context: context),
onPressed: () {
model.setIsSortByClinic(false);
},
backgroundColor: model.isSortByClinic ? AppColors.whiteColor : AppColors.bgRedLightColor,
borderColor: model.isSortByClinic ? AppColors.textColor.withValues(alpha: 0.2) : AppColors.primaryRedColor,
textColor: model.isSortByClinic ? AppColors.blackColor : AppColors.primaryRedColor,
fontSize: 12,
fontWeight: FontWeight.w500,
borderRadius: 10,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
),
],
),
),
SizedBox(height: 8.h),
selectedFilterText!.isNotEmpty
? CustomChipWidget(
chipText: selectedFilterText!,
chipType: ChipTypeEnum.alert,
isSelected: true,
)
: SizedBox(),
activeIndex == 0
? // By Visit - show grouped view when available
model.isLabOrdersLoading
? ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero,
itemCount: 5,
itemBuilder: (context, index) => LabResultItemView(
onTap: () {},
labOrder: null,
index: index,
isLoading: true,
),
)
: (model.patientLabOrdersViewList.isNotEmpty
? ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero,
itemCount: model.patientLabOrdersViewList.length,
itemBuilder: (context, index) {
final group = model.patientLabOrdersViewList[index];
return AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 400),
child: SlideAnimation(
verticalOffset: 50.0,
child: FadeInAnimation(
child: AnimatedContainer(
key: _groupKeys.putIfAbsent(index, () => GlobalKey()),
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
margin: EdgeInsets.symmetric(vertical: 8.h),
decoration: RoundedRectangleBorder()
.toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 20.h, hasShadow: true),
child: InkWell(
onTap: () {
setState(() {
expandedIndex = expandedIndex == index ? null : index;
});
// After rebuild, ensure the expanded group is visible
WidgetsBinding.instance.addPostFrameCallback((_) {
final key = _groupKeys[index];
if (key != null && key.currentContext != null && expandedIndex == index) {
Scrollable.ensureVisible(
key.currentContext!,
duration: Duration(milliseconds: 350),
curve: Curves.easeInOut,
alignment: 0.1,
);
}
});
},
child: Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
CustomButton(
text: "${group.length} ${'results'.needTranslation}",
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
),
Icon(expandedIndex == index ? Icons.expand_less : Icons.expand_more, ),
],
),
SizedBox(height: 8.h),
Text(
model.isSortByClinic ? (group.first.clinicDescription ?? 'Unknown') : (group.first.projectName ?? 'Unknown'),
style: TextStyle(fontSize: 16.h, fontWeight: FontWeight.w600),
overflow: TextOverflow.ellipsis,
),
// Expanded list of orders when group is expanded
AnimatedSwitcher(
duration: Duration(milliseconds: 400),
child: expandedIndex == index
? Container(
key: ValueKey(index),
padding: EdgeInsets.only(top: 12.h),
child: Column(
children: group.map((order) {
return Container(
margin: EdgeInsets.only(bottom: 12.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 12.h,
hasShadow: true,
),
child: Padding(
padding: EdgeInsets.all(14.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.network(
order.doctorImageURL ?? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png",
width: 24.h,
height: 24.h,
fit: BoxFit.cover,
).circle(100),
SizedBox(width: 8.h),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
(order.doctorName ?? order.doctorNameEnglish ?? "").toString().toText14(weight: FontWeight.w500),
SizedBox(height: 6.h),
],
),
),
],
),
SizedBox(height: 8.h),
Row(
children: [
CustomButton(
text: ("Order No: ".needTranslation + order.orderNo!),
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 24.h,
),
SizedBox(width: 8.h),
CustomButton(
text: DateUtil.formatDateToDate(DateUtil.convertStringToDate(order.orderDate ?? ""), false),
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 24.h,
),
SizedBox(width: 8.h),
],
),
SizedBox(height: 8.h),
Row(
children: [
CustomButton(
text: model.isSortByClinic ? (order.clinicDescription ?? "") : (order.projectName ?? ""),
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 24.h,
),
// SizedBox(height: 8.h),
// Expanded(
// flex: 6,
// child: CustomButton(
// text: LocaleKeys.view.tr(context: context),
// onPressed: () async {
// model.currentlySelectedPatientOrder = order;
// labProvider.getPatientLabResultByHospital(order);
// labProvider.getPatientSpecialResult(order);
// Navigator.push(
// context,
// CustomPageRoute(
// page: LabResultByClinic(labOrder: order),
// ),
// );
// },
// backgroundColor: AppColors.bgRedLightColor,
// borderColor: AppColors.primaryRedColor,
// textColor: AppColors.primaryRedColor,
// fontSize: 14,
// fontWeight: FontWeight.w500,
// borderRadius: 12,
// padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
// height: 40.h,
// ),
// ),
],
),
Row(
children: [
Expanded(flex:6, child: SizedBox()),
Expanded(
flex: 1,
child: Container(
height: 40.h,
width: 40.w,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.textColor,
borderRadius: 12,
),
child: Padding(
padding: EdgeInsets.all(12.h),
child: Transform.flip(
flipX: _appState.isArabic(),
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon_small,
iconColor: AppColors.whiteColor,
fit: BoxFit.contain,
),
),
),
).onPress(() {
model.currentlySelectedPatientOrder = order;
labProvider.getPatientLabResultByHospital(order);
labProvider.getPatientSpecialResult(order);
Navigator.of(context).push(
CustomPageRoute(page: LabResultByClinic(labOrder: order)),
);
}),
)
],
),
])),
);
}).toList(),
),
)
: SizedBox.shrink(),
),
],
),
),
),
),
),
),
);
},
)
: Utils.getNoDataWidget(context, noDataText: "You don't have any lab results yet.".needTranslation))
: // By Test or other tabs keep existing behavior
(model.isLabOrdersLoading)
? Column(
children: List.generate(
5,
(index) => LabResultItemView(
onTap: () {},
labOrder: null,
index: index,
isLoading: true,
)),
)
: AlphabeticScroll(
alpahbetsAvailable: model.indexedCharacterForUniqueTest,
details: model.uniqueTestsList,
labViewModel: model,
rangeViewModel: rangeViewModel,
appState: _appState,
)
],
);
},
),
),
);
}
Color getLabOrderStatusColor(num status) {
switch (status) {
case 44:
return AppColors.warningColorYellow;
case 45:
return AppColors.warningColorYellow;
case 16:
return AppColors.successColor;
case 17:
return AppColors.successColor;
default:
return AppColors.greyColor;
}
}
String getLabOrderStatusText(num status) {
switch (status) {
case 44:
return LocaleKeys.resultsPending.tr(context: context);
case 45:
return LocaleKeys.resultsPending.tr(context: context);
case 16:
return LocaleKeys.resultsAvailable.tr(context: context);
case 17:
return LocaleKeys.resultsAvailable.tr(context: context);
default:
return "";
}
}
getLabSuggestions(LabViewModel model) {
if (model.patientLabOrders.isEmpty) {
return [];
}
return model.patientLabOrders.map((m) => m.testDetails).toList();
}
}
\ No newline at end of file
+import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.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/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart';
import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_result_via_clinic/LabResultByClinic.dart';
import 'package:hmg_patient_app_new/presentation/lab/search_lab_report.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_toolbar.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/chip/custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart';
import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_view_model.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:provider/provider.dart';
import 'alphabeticScroll.dart';
class LabOrdersPage extends StatefulWidget {
const LabOrdersPage({super.key});
@override
State createState() => _LabOrdersPageState();
}
class _LabOrdersPageState extends State {
late LabViewModel labProvider;
late DateRangeSelectorRangeViewModel rangeViewModel;
late AppState _appState;
List?> labSuggestions = [];
int? expandedIndex;
String? selectedFilterText = '';
int activeIndex = 0;
@override
void initState() {
scheduleMicrotask(() {
labProvider.initLabProvider();
});
super.initState();
}
@override
Widget build(BuildContext context) {
labProvider = Provider.of(context, listen: false);
rangeViewModel = Provider.of(context);
_appState = getIt();
return CollapsingToolbar(
title: LocaleKeys.labResults.tr(),
search: () async {
final lavVM = Provider.of(context, listen: false);
if (lavVM.isLabOrdersLoading) {
return;
} else {
String? value = await Navigator.of(context).push(
CustomPageRoute(
page: SearchLabResultsContent(labSuggestionsList: lavVM.labSuggestions),
fullScreenDialog: true,
direction: AxisDirection.down,
),
);
if (value != null) {
selectedFilterText = value;
lavVM.filterLabReports(value);
}
}
},
child: Consumer(
builder: (context, model, child) {
return SingleChildScrollView(
physics: AlwaysScrollableScrollPhysics(),
padding: EdgeInsets.all(24.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: CustomTabBar(
activeTextColor: Color(0xffED1C2B),
activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1),
tabs: [
CustomTabBarModel(null, "By Visit".needTranslation),
CustomTabBarModel(null, "By Test".needTranslation),
// CustomTabBarModel(null, "Completed".needTranslation),
],
onTabChange: (index) {
activeIndex = index;
setState(() {});
},
),
),
],
),
if (activeIndex == 0)
Padding(
padding: EdgeInsets.symmetric(vertical: 10.h),
child: Row(
children: [
CustomButton(
text: LocaleKeys.byClinic.tr(context: context),
onPressed: () {
model.setIsSortByClinic(true);
},
backgroundColor: model.isSortByClinic ? AppColors.bgRedLightColor : AppColors.whiteColor,
borderColor: model.isSortByClinic ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2),
textColor: model.isSortByClinic ? AppColors.primaryRedColor : AppColors.blackColor,
fontSize: 12,
fontWeight: FontWeight.w500,
borderRadius: 10,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
),
SizedBox(width: 8.h),
CustomButton(
text: LocaleKeys.byHospital.tr(context: context),
onPressed: () {
model.setIsSortByClinic(false);
},
backgroundColor: model.isSortByClinic ? AppColors.whiteColor : AppColors.bgRedLightColor,
borderColor: model.isSortByClinic ? AppColors.textColor.withValues(alpha: 0.2) : AppColors.primaryRedColor,
textColor: model.isSortByClinic ? AppColors.blackColor : AppColors.primaryRedColor,
fontSize: 12,
fontWeight: FontWeight.w500,
borderRadius: 10,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
),
],
),
),
SizedBox(height: 8.h),
selectedFilterText!.isNotEmpty
? CustomChipWidget(
chipText: selectedFilterText!,
chipType: ChipTypeEnum.alert,
isSelected: true,
)
: SizedBox(),
activeIndex == 0
? // By Visit - show grouped view when available
model.isLabOrdersLoading
? ListView.builder(
shrinkWrap: true,
physics: AlwaysScrollableScrollPhysics(),
padding: EdgeInsets.zero,
itemCount: 5,
itemBuilder: (context, index) => LabResultItemView(
onTap: () {},
labOrder: null,
index: index,
isLoading: true,
),
)
: (model.patientLabOrdersViewList.isNotEmpty
? ListView.builder(
shrinkWrap: true,
physics: AlwaysScrollableScrollPhysics(),
padding: EdgeInsets.zero,
itemCount: model.patientLabOrdersViewList.length,
itemBuilder: (context, index) {
final group = model.patientLabOrdersViewList[index];
final isExpanded = expandedIndex == index;
return AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
margin: EdgeInsets.symmetric(vertical: 8.h),
decoration: RoundedRectangleBorder()
.toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 20.h, hasShadow: true),
child: InkWell(
onTap: () {
setState(() {
expandedIndex = isExpanded ? null : index;
});
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
AppCustomChipWidget(labelText: "${group.length} ${'results'.needTranslation}"),
Icon(isExpanded ? Icons.expand_less : Icons.expand_more),
],
),
SizedBox(height: 8.h),
Text(
model.isSortByClinic ? (group.first.clinicDescription ?? 'Unknown') : (group.first.projectName ?? 'Unknown'),
style: TextStyle(fontSize: 16.h, fontWeight: FontWeight.w600),
overflow: TextOverflow.ellipsis,
),
],
),
),
AnimatedSwitcher(
duration: Duration(milliseconds: 500),
switchInCurve: Curves.easeIn,
switchOutCurve: Curves.easeOut,
transitionBuilder: (Widget child, Animation animation) {
return FadeTransition(
opacity: animation,
child: SizeTransition(
sizeFactor: animation,
axisAlignment: 0.0,
child: child,
),
);
},
child: isExpanded
? Container(
key: ValueKey(index),
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 0.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
...group.map((order) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisSize: MainAxisSize.min,
children: [
Image.network(
order.doctorImageURL ?? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png",
width: 24.w,
height: 24.h,
fit: BoxFit.cover,
).circle(100),
SizedBox(width: 8.h),
Expanded(child: (order.doctorName ?? order.doctorNameEnglish ?? "").toString().toText14(weight: FontWeight.w500)),
],
),
SizedBox(height: 8.h),
Wrap(
direction: Axis.horizontal,
spacing: 4.h,
runSpacing: 4.h,
children: [
AppCustomChipWidget(
labelText: ("Order No: ".needTranslation + order.orderNo!),
),
AppCustomChipWidget(
labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(order.orderDate ?? ""), false),
),
AppCustomChipWidget(
labelText: model.isSortByClinic ? (order.clinicDescription ?? "") : (order.projectName ?? ""),
),
],
),
// Row(
// children: [
// CustomButton(
// text: ("Order No: ".needTranslation + order.orderNo!),
// onPressed: () {},
// backgroundColor: AppColors.greyColor,
// borderColor: AppColors.greyColor,
// textColor: AppColors.blackColor,
// fontSize: 10,
// fontWeight: FontWeight.w500,
// borderRadius: 8,
// padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
// height: 24.h,
// ),
// SizedBox(width: 8.h),
// CustomButton(
// text: DateUtil.formatDateToDate(DateUtil.convertStringToDate(order.orderDate ?? ""), false),
// onPressed: () {},
// backgroundColor: AppColors.greyColor,
// borderColor: AppColors.greyColor,
// textColor: AppColors.blackColor,
// fontSize: 10,
// fontWeight: FontWeight.w500,
// borderRadius: 8,
// padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
// height: 24.h,
// ),
// ],
// ),
// SizedBox(height: 8.h),
// Row(
// children: [
// CustomButton(
// text: model.isSortByClinic ? (order.clinicDescription ?? "") : (order.projectName ?? ""),
// onPressed: () {},
// backgroundColor: AppColors.greyColor,
// borderColor: AppColors.greyColor,
// textColor: AppColors.blackColor,
// fontSize: 10,
// fontWeight: FontWeight.w500,
// borderRadius: 8,
// padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
// height: 24.h,
// ),
// ],
// ),
SizedBox(height: 12.h),
Row(
children: [
Expanded(flex: 2, child: SizedBox()),
// Expanded(
// flex: 1,
// child: Container(
// height: 40.h,
// width: 40.w,
// decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
// color: AppColors.textColor,
// borderRadius: 12,
// ),
// child: Padding(
// padding: EdgeInsets.all(12.h),
// child: Transform.flip(
// flipX: _appState.isArabic(),
// child: Utils.buildSvgWithAssets(
// icon: AppAssets.forward_arrow_icon_small,
// iconColor: AppColors.whiteColor,
// fit: BoxFit.contain,
// ),
// ),
// ),
// ).onPress(() {
// model.currentlySelectedPatientOrder = order;
// labProvider.getPatientLabResultByHospital(order);
// labProvider.getPatientSpecialResult(order);
// Navigator.of(context).push(
// CustomPageRoute(page: LabResultByClinic(labOrder: order)),
// );
// }),
// )
Expanded(
flex:2,
child: CustomButton(
icon: AppAssets.view_report_icon,
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
text: "View Results".needTranslation,
onPressed: () {
model.currentlySelectedPatientOrder = order;
labProvider.getPatientLabResultByHospital(order);
labProvider.getPatientSpecialResult(order);
Navigator.of(context).push(
CustomPageRoute(page: LabResultByClinic(labOrder: order)),
);
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
),
)
],
),
SizedBox(height: 12.h),
Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.05), height: 1.h),
SizedBox(height: 12.h),
],
);
}).toList(),
],
),
)
: SizedBox.shrink(),
),
],
),
),
),
),
));
},
)
: Utils.getNoDataWidget(context, noDataText: "You don't have any lab results yet.".needTranslation))
: // By Test or other tabs keep existing behavior
(model.isLabOrdersLoading)
? Column(
children: List.generate(
5,
(index) => LabResultItemView(
onTap: () {},
labOrder: null,
index: index,
isLoading: true,
)),
)
: AlphabeticScroll(
alpahbetsAvailable: model.indexedCharacterForUniqueTest,
details: model.uniqueTestsList,
labViewModel: model,
rangeViewModel: rangeViewModel,
appState: _appState,
)
],
) );
},
),
);
}
Color getLabOrderStatusColor(num status) {
switch (status) {
case 44:
return AppColors.warningColorYellow;
case 45:
return AppColors.warningColorYellow;
case 16:
return AppColors.successColor;
case 17:
return AppColors.successColor;
default:
return AppColors.greyColor;
}
}
String getLabOrderStatusText(num status) {
switch (status) {
case 44:
return LocaleKeys.resultsPending.tr(context: context);
case 45:
return LocaleKeys.resultsPending.tr(context: context);
case 16:
return LocaleKeys.resultsAvailable.tr(context: context);
case 17:
return LocaleKeys.resultsAvailable.tr(context: context);
default:
return "";
}
}
getLabSuggestions(LabViewModel model) {
if (model.patientLabOrders.isEmpty) {
return [];
}
return model.patientLabOrders.map((m) => m.testDetails).toList();
}
}
\ No newline at end of file
diff --git a/lib/presentation/medical_report/medical_reports_page.dart b/lib/presentation/medical_report/medical_reports_page.dart
index b15266c..cb78479 100644
--- a/lib/presentation/medical_report/medical_reports_page.dart
+++ b/lib/presentation/medical_report/medical_reports_page.dart
@@ -51,39 +51,39 @@ class _MedicalReportsPageState extends State {
children: [
SizedBox(height: 16.h),
// Status filter tabs
- Row(
- children: [
- CustomButton(
- text: LocaleKeys.byClinic.tr(context: context),
- onPressed: () {
- medicalFileVM.setIsMedicalReportsSortByClinic(true);
- },
- backgroundColor: medicalFileVM.isMedicalReportsSortByClinic ? AppColors.bgRedLightColor : AppColors.whiteColor,
- borderColor: medicalFileVM.isMedicalReportsSortByClinic ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2),
- textColor: medicalFileVM.isMedicalReportsSortByClinic ? AppColors.primaryRedColor : AppColors.blackColor,
- fontSize: 12,
- fontWeight: FontWeight.w500,
- borderRadius: 10,
- padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
- height: 40.h,
- ),
- SizedBox(width: 8.h),
- CustomButton(
- text: LocaleKeys.byHospital.tr(context: context),
- onPressed: () {
- medicalFileVM.setIsMedicalReportsSortByClinic(false);
- },
- backgroundColor: medicalFileVM.isMedicalReportsSortByClinic ? AppColors.whiteColor : AppColors.bgRedLightColor,
- borderColor: medicalFileVM.isMedicalReportsSortByClinic ? AppColors.textColor.withValues(alpha: 0.2) : AppColors.primaryRedColor,
- textColor: medicalFileVM.isMedicalReportsSortByClinic ? AppColors.blackColor : AppColors.primaryRedColor,
- fontSize: 12,
- fontWeight: FontWeight.w500,
- borderRadius: 10,
- padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
- height: 40.h,
- ),
- ],
- ).paddingSymmetrical(24.h, 0.h),
+ // Row(
+ // children: [
+ // CustomButton(
+ // text: LocaleKeys.byClinic.tr(context: context),
+ // onPressed: () {
+ // medicalFileVM.setIsMedicalReportsSortByClinic(true);
+ // },
+ // backgroundColor: medicalFileVM.isMedicalReportsSortByClinic ? AppColors.bgRedLightColor : AppColors.whiteColor,
+ // borderColor: medicalFileVM.isMedicalReportsSortByClinic ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2),
+ // textColor: medicalFileVM.isMedicalReportsSortByClinic ? AppColors.primaryRedColor : AppColors.blackColor,
+ // fontSize: 12,
+ // fontWeight: FontWeight.w500,
+ // borderRadius: 10,
+ // padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
+ // height: 40.h,
+ // ),
+ // SizedBox(width: 8.h),
+ // CustomButton(
+ // text: LocaleKeys.byHospital.tr(context: context),
+ // onPressed: () {
+ // medicalFileVM.setIsMedicalReportsSortByClinic(false);
+ // },
+ // backgroundColor: medicalFileVM.isMedicalReportsSortByClinic ? AppColors.whiteColor : AppColors.bgRedLightColor,
+ // borderColor: medicalFileVM.isMedicalReportsSortByClinic ? AppColors.textColor.withValues(alpha: 0.2) : AppColors.primaryRedColor,
+ // textColor: medicalFileVM.isMedicalReportsSortByClinic ? AppColors.blackColor : AppColors.primaryRedColor,
+ // fontSize: 12,
+ // fontWeight: FontWeight.w500,
+ // borderRadius: 10,
+ // padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
+ // height: 40.h,
+ // ),
+ // ],
+ // ).paddingSymmetrical(24.h, 0.h),
SizedBox(height: 20.h),
Row(
children: [
diff --git a/lib/routes/app_routes.dart b/lib/routes/app_routes.dart
index 9b045dc..f0acd5b 100644
--- a/lib/routes/app_routes.dart
+++ b/lib/routes/app_routes.dart
@@ -26,6 +26,8 @@ import 'package:hmg_patient_app_new/presentation/water_monitor/water_consumption
import 'package:hmg_patient_app_new/presentation/water_monitor/water_monitor_settings_screen.dart';
import 'package:hmg_patient_app_new/splashPage.dart';
+import '../presentation/covid19test/covid19_landing_page.dart';
+
class AppRoutes {
static const String initialRoute = '/initialRoute';
static const String loginScreen = '/loginScreen';
@@ -42,7 +44,7 @@ class AppRoutes {
static const String bloodDonationPage = '/bloodDonationPage';
static const String smartWatches = '/smartWatches';
static const String huaweiHealthExample = '/huaweiHealthExample';
-
+ static const String covid19Test = '/covid19Test';
//appointments
static const String bookAppointmentPage = '/bookAppointmentPage';
@@ -85,6 +87,8 @@ class AppRoutes {
userInfoFlowManager: (context) => UserInfoFlowManager(),
smartWatches: (context) => SmartwatchInstructionsPage(),
huaweiHealthExample: (context) => HuaweiHealthExample(),
+ covid19Test: (context) => Covid19LandingPage(),
+ //
waterConsumptionScreen: (context) => WaterConsumptionScreen(),
waterMonitorSettingsScreen: (context) => WaterMonitorSettingsScreen(),
healthCalculatorsPage: (context) => HealthCalculatorsPage(type: HealthCalConEnum.calculator),
diff --git a/lib/theme/colors.dart b/lib/theme/colors.dart
index 6dfb207..c631f5b 100644
--- a/lib/theme/colors.dart
+++ b/lib/theme/colors.dart
@@ -108,4 +108,5 @@ class AppColors {
// Shimmer
static const Color shimmerBaseColor = Color(0xFFE0E0E0);
static const Color shimmerHighlightColor = Color(0xFFF5F5F5);
+ static const Color covid29Color = Color(0xff2563EB);
}
diff --git a/lib/widgets/common_bottom_sheet.dart b/lib/widgets/common_bottom_sheet.dart
index 9312519..6ff5cc5 100644
--- a/lib/widgets/common_bottom_sheet.dart
+++ b/lib/widgets/common_bottom_sheet.dart
@@ -108,11 +108,11 @@ class ButtonSheetContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
- crossAxisAlignment: CrossAxisAlignment.center,
+ crossAxisAlignment: CrossAxisAlignment.start,
children: [
- // SizedBox(
- // height: 20.h,
- // ),
+ SizedBox(
+ height: 20.h,
+ ),
// Center(
// child: Container(
// margin: const EdgeInsets.only(top: 18, bottom: 12),
@@ -127,10 +127,9 @@ class ButtonSheetContent extends StatelessWidget {
// Close button
isCloseButtonVisible && isFullScreen
- ? Column(children: [
- SizedBox(
- height: 40.h,
- ),
+ ? Row(
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [
Padding(
padding: EdgeInsets.symmetric(
horizontal: 16,
@@ -145,7 +144,7 @@ class ButtonSheetContent extends StatelessWidget {
isFullScreen
? Column(
children: [
- SizedBox(height: 20.h),
+ // SizedBox(height: 20.h),
Padding(padding: EdgeInsets.symmetric(horizontal: 16.h), child: title.toText24(isBold: true)),
SizedBox(height: 16.h),
],