diff --git a/assets/images/png/cc_ar.png b/assets/images/png/cc_ar.png
new file mode 100644
index 0000000..e4388ba
Binary files /dev/null and b/assets/images/png/cc_ar.png differ
diff --git a/assets/images/png/cc_en.png b/assets/images/png/cc_en.png
new file mode 100644
index 0000000..c11cf5e
Binary files /dev/null and b/assets/images/png/cc_en.png differ
diff --git a/assets/images/svg/comprehensive_checkup.svg b/assets/images/svg/comprehensive_checkup.svg
new file mode 100644
index 0000000..885d9a7
--- /dev/null
+++ b/assets/images/svg/comprehensive_checkup.svg
@@ -0,0 +1,6 @@
+
diff --git a/assets/images/svg/e-referral.svg b/assets/images/svg/e-referral.svg
new file mode 100644
index 0000000..3262779
--- /dev/null
+++ b/assets/images/svg/e-referral.svg
@@ -0,0 +1,7 @@
+
diff --git a/lib/core/api/api_client.dart b/lib/core/api/api_client.dart
index ebd0d34..3fb1470 100644
--- a/lib/core/api/api_client.dart
+++ b/lib/core/api/api_client.dart
@@ -103,7 +103,7 @@ class ApiClientImp implements ApiClient {
url = endPoint;
} else {
if (isRCService) {
- url = RC_BASE_URL + endPoint;
+ url = ApiConsts.rcBaseUrl + endPoint;
} else {
url = ApiConsts.baseUrl + endPoint;
}
@@ -161,11 +161,10 @@ class ApiClientImp implements ApiClient {
// body['VersionID'] = ApiConsts.appVersionID.toString();
if (!isExternal) {
- body['VersionID'] = "50.0";
- body['Channel'] = ApiConsts.appChannelId.toString();
+ body['VersionID'] = ApiConsts.appVersionID;
+ body['Channel'] = ApiConsts.appChannelId;
body['IPAdress'] = ApiConsts.appIpAddress;
body['generalid'] = ApiConsts.appGeneralId;
-
body['LanguageID'] = _appState.getLanguageID().toString();
body['Latitude'] = _appState.userLat.toString();
body['Longitude'] = _appState.userLong.toString();
@@ -184,9 +183,6 @@ class ApiClientImp implements ApiClient {
}
body.removeWhere((key, value) => value == null);
- log("uri: ${Uri.parse(url.trim())}");
-
- log("body: ${json.encode(body)}");
final bool networkStatus = await Utils.checkConnection(bypassConnectionCheck: bypassConnectionCheck);
@@ -202,6 +198,9 @@ class ApiClientImp implements ApiClient {
final response = await http.post(Uri.parse(url.trim()), body: json.encode(body), headers: headers);
final int statusCode = response.statusCode;
+ log("uri: ${Uri.parse(url.trim())}");
+ log("body: ${json.encode(body)}");
+ log("response.body: ${response.body}");
// log("response.body: ${response.body}");
if (statusCode < 200 || statusCode >= 400) {
onFailure('Error While Fetching data', statusCode, failureType: StatusCodeFailure("Error While Fetching data"));
@@ -213,13 +212,14 @@ class ApiClientImp implements ApiClient {
onSuccess(parsed, statusCode, messageStatus: 1, errorMessage: "");
} else {
onSuccess(parsed, statusCode,
- messageStatus: parsed.contains('MessageStatus') ? parsed['MessageStatus'] : 1,
- errorMessage: parsed.contains('ErrorEndUserMessage') ? parsed['ErrorEndUserMessage'] : "");
+ messageStatus: (parsed is Map && parsed.containsKey('MessageStatus')) ? parsed['MessageStatus'] : 1,
+ errorMessage: (parsed is Map && parsed.containsKey('ErrorEndUserMessage')) ? parsed['ErrorEndUserMessage'] : "");
}
} else {
if (parsed['Response_Message'] != null) {
onSuccess(parsed, statusCode,
- messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
+ messageStatus: (parsed is Map && parsed.containsKey('MessageStatus')) ? parsed['MessageStatus'] : 1,
+ errorMessage: (parsed is Map && parsed.containsKey('ErrorEndUserMessage')) ? parsed['ErrorEndUserMessage'] : "");
} else {
if (parsed['ErrorType'] == 4) {
//TODO : handle app update
@@ -352,9 +352,9 @@ class ApiClientImp implements ApiClient {
url = endPoint;
} else {
if (isRCService) {
- url = RC_BASE_URL + endPoint;
+ url = ApiConsts.rcBaseUrl + endPoint;
} else {
- url = BASE_URL + endPoint;
+ url = ApiConsts.baseUrl + endPoint;
}
}
if (queryParams != null) {
diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart
index 982e1ca..64d9d28 100644
--- a/lib/core/api_consts.dart
+++ b/lib/core/api_consts.dart
@@ -14,8 +14,8 @@ var PACKAGES_ORDERS = '/api/orders';
var PACKAGES_ORDER_HISTORY = '/api/orders/items';
var PACKAGES_TAMARA_OPT = '/api/orders/paymentoptions/tamara';
// var BASE_URL = 'http://10.50.100.198:2018/';
-// var BASE_URL = 'https://uat.hmgwebservices.com/';
-var BASE_URL = 'https://hmgwebservices.com/';
+var BASE_URL = 'https://uat.hmgwebservices.com/';
+// var BASE_URL = 'https://hmgwebservices.com/';
// var BASE_URL = 'http://10.201.204.103/';
// var BASE_URL = 'https://orash.cloudsolutions.com.sa/';
// var BASE_URL = 'https://vidauat.cloudsolutions.com.sa/';
@@ -46,8 +46,6 @@ var PHARMACY_REDIRECT_URL = 'https://bit.ly/AlhabibPharmacy';
// RC API URL
// var RC_BASE_URL = 'https://rc.hmg.com/';
-var RC_BASE_URL = 'https://rc.hmg.com/uat/';
-
// var RC_BASE_URL = 'https://ms.hmg.com/rc/';
var PING_SERVICE = 'Services/Weather.svc/REST/CheckConnectivity';
@@ -521,12 +519,6 @@ var ADD_HHC_ORDER_RC = "api/HHC/add";
var GET_ALL_HHC_ORDERS_RC = 'api/hhc/list';
var UPDATE_HHC_ORDER_RC = 'api/hhc/update';
-// CMC RC SERVICES
-var GET_ALL_CMC_SERVICES_RC = 'api/cmc/getallcmc';
-var ADD_CMC_ORDER_RC = 'api/cmc/add';
-var GET_ALL_CMC_ORDERS_RC = 'api/cmc/list';
-var UPDATE_CMC_ORDER_RC = 'api/cmc/update';
-
// RRT RC SERVICES
var ADD_RRT_ORDER_RC = "api/rrt/add";
var GET_ALL_RRT_ORDERS_RC = "api/rrt/list";
@@ -725,7 +717,7 @@ class ApiConsts {
static String baseUrl = 'https://hmgwebservices.com/'; // HIS API URL PROD
- static String RCBaseUrl = 'https://rc.hmg.com/'; // RC API URL PROD
+ static String rcBaseUrl = 'https://rc.hmg.com/'; // RC API URL PROD
static var payFortEnvironment = FortEnvironment.production;
static var applePayMerchantId = "merchant.com.hmgwebservices";
@@ -752,7 +744,7 @@ class ApiConsts {
TAMARA_URL = "https://mdlaboratories.com/tamaralive/Home/Checkout";
GET_TAMARA_INSTALLMENTS_URL = "https://mdlaboratories.com/tamaralive/Home/GetInstallments";
GET_TAMARA_PAYMENT_STATUS = 'https://mdlaboratories.com/tamaralive/api/OnlineTamara/order_status?orderid=';
- RCBaseUrl = 'https://rc.hmg.com/';
+ rcBaseUrl = 'https://rc.hmg.com/';
break;
case AppEnvironmentTypeEnum.dev:
baseUrl = "https://uat.hmgwebservices.com/";
@@ -762,7 +754,7 @@ class ApiConsts {
TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout";
GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments";
GET_TAMARA_PAYMENT_STATUS = 'https://epharmacy.hmg.com/tamara/api/OnlineTamara/order_status?orderid=';
- RCBaseUrl = 'https://rc.hmg.com/test/';
+ rcBaseUrl = 'https://rc.hmg.com/';
break;
case AppEnvironmentTypeEnum.uat:
baseUrl = "https://uat.hmgwebservices.com/";
@@ -772,7 +764,7 @@ class ApiConsts {
TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout";
GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments";
GET_TAMARA_PAYMENT_STATUS = 'https://epharmacy.hmg.com/tamara/api/OnlineTamara/order_status?orderid=';
- RCBaseUrl = 'https://rc.hmg.com/test/';
+ rcBaseUrl = 'https://rc.hmg.com/';
break;
case AppEnvironmentTypeEnum.preProd:
baseUrl = "https://webservices.hmg.com/";
@@ -782,7 +774,7 @@ class ApiConsts {
TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout";
GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments";
GET_TAMARA_PAYMENT_STATUS = 'https://epharmacy.hmg.com/tamara/api/OnlineTamara/order_status?orderid=';
- RCBaseUrl = 'https://rc.hmg.com/';
+ rcBaseUrl = 'https://rc.hmg.com/';
break;
case AppEnvironmentTypeEnum.qa:
baseUrl = "https://uat.hmgwebservices.com/";
@@ -792,7 +784,7 @@ class ApiConsts {
TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout";
GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments";
GET_TAMARA_PAYMENT_STATUS = 'https://epharmacy.hmg.com/tamara/api/OnlineTamara/order_status?orderid=';
- RCBaseUrl = 'https://rc.hmg.com/test/';
+ rcBaseUrl = 'https://rc.hmg.com/';
break;
case AppEnvironmentTypeEnum.staging:
baseUrl = "https://uat.hmgwebservices.com/";
@@ -802,7 +794,7 @@ class ApiConsts {
TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout";
GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments";
GET_TAMARA_PAYMENT_STATUS = 'https://epharmacy.hmg.com/tamara/api/OnlineTamara/order_status?orderid=';
- RCBaseUrl = 'https://rc.hmg.com/test/';
+ rcBaseUrl = 'https://rc.hmg.com/';
break;
}
}
@@ -846,8 +838,21 @@ class ApiConsts {
static final String createAdvancePayments = 'Services/Patients.svc/REST/HIS_CreateAdvancePayment';
static final String addAdvanceNumberRequest = 'Services/PayFort_Serv.svc/REST/AddAdvancedNumberRequest';
+ // RC COMPREHENSIVE MEDICAL CHECKUP ServIces
+ static final String allCMCOrdersRc = 'api/cmc/list';
+ static final String allCMCServicesRc = 'api/cmc/getallcmc';
+ static final String updateCMCOrder = 'api/cmc/update';
+ static final String addCMCOrder = 'api/cmc/add';
+ static final String getHospitalsList = 'Services/Lists.svc/REST/GetProject';
+
+ // RC HOME HEALTHCARE ServIces
+ static final String allHHCOrdersRc = 'api/hhc/list';
+ static final String allHHCServicesRc = 'api/HHC/getallhhc';
+ static final String updateHHCOrder = 'api/hhc/update';
+ static final String addHHCOrder = 'api/HHC/add';
+
// ************ static values for Api ****************
- static final double appVersionID = 18.7;
+ static final double appVersionID = 20.0;
static final int appChannelId = 3;
static final String appIpAddress = "10.20.10.20";
static final String appGeneralId = "Cs2020@2016\$2958";
diff --git a/lib/core/app_assets.dart b/lib/core/app_assets.dart
index e5269cc..5fccc6e 100644
--- a/lib/core/app_assets.dart
+++ b/lib/core/app_assets.dart
@@ -171,6 +171,8 @@ class AppAssets {
static const String to_arrow = '$svgBasePath/to_arrow.svg';
static const String dual_arrow = '$svgBasePath/to_arrow.svg';
static const String forward_arrow_medium = '$svgBasePath/forward_arrow_medium.svg';
+ static const String eReferral = '$svgBasePath/e-referral.svg';
+ static const String comprehensiveCheckup = '$svgBasePath/comprehensive_checkup.svg';
static const String all_payment_method = '$svgBasePath/all_payment_method.svg';
static const String ic_rrt_vehicle = '$svgBasePath/ic_rrt_vehicle.svg';
@@ -202,6 +204,8 @@ class AppAssets {
static const String visa = '$pngBasePath/visa.png';
static const String lockIcon = '$pngBasePath/lock-icon.png';
static const String dummy_user = '$pngBasePath/dummy_user.png';
+ static const String comprehensiveCheckupEn = '$pngBasePath/cc_en.png';
+ static const String comprehensiveCheckupAr = '$pngBasePath/cc_er.png';
}
class AppAnimations {
diff --git a/lib/core/dependencies.dart b/lib/core/dependencies.dart
index 37d0cc4..8fa6f89 100644
--- a/lib/core/dependencies.dart
+++ b/lib/core/dependencies.dart
@@ -15,6 +15,8 @@ import 'package:hmg_patient_app_new/features/emergency_services/emergency_servic
import 'package:hmg_patient_app_new/features/emergency_services/emergency_services_view_model.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_repo.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart';
+import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_repo.dart';
+import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_repo.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_view_model.dart';
import 'package:hmg_patient_app_new/features/insurance/insurance_repo.dart';
@@ -104,47 +106,30 @@ class AppDependencies {
getIt.registerLazySingleton(() => InsuranceRepoImp(loggerService: getIt(), apiClient: getIt()));
getIt.registerLazySingleton(() => PayfortRepoImp(loggerService: getIt(), apiClient: getIt()));
getIt.registerLazySingleton(
- () => LocalAuthService(loggerService: getIt(), localAuth: getIt()));
+ () => LocalAuthService(loggerService: getIt(), localAuth: getIt()),
+ );
getIt.registerLazySingleton(() => HabibWalletRepoImp(loggerService: getIt(), apiClient: getIt()));
getIt.registerLazySingleton(() => MedicalFileRepoImp(loggerService: getIt(), apiClient: getIt()));
getIt.registerLazySingleton(() => ImmediateLiveCareRepoImp(loggerService: getIt(), apiClient: getIt()));
getIt.registerLazySingleton(() => EmergencyServicesRepoImp(loggerService: getIt(), apiClient: getIt()));
getIt.registerLazySingleton(() => TodoSectionRepoImp(loggerService: getIt(), apiClient: getIt()));
- getIt.registerLazySingleton(
- () => LocationRepoImpl(apiClient: getIt()));
+ getIt.registerLazySingleton(() => LocationRepoImpl(apiClient: getIt()));
getIt.registerLazySingleton(() => ContactUsRepoImp(loggerService: getIt(), apiClient: getIt()));
+ getIt.registerLazySingleton(() => HmgServicesRepoImp(loggerService: getIt(), apiClient: getIt()));
// ViewModels
// Global/shared VMs → LazySingleton
- getIt.registerLazySingleton(
- () => LabViewModel(labRepo: getIt(), errorHandlerService: getIt(), navigationService: getIt()),
- );
+ getIt.registerLazySingleton(() => LabViewModel(labRepo: getIt(), errorHandlerService: getIt(), navigationService: getIt()));
- getIt.registerLazySingleton(
- () => RadiologyViewModel(
- radiologyRepo: getIt(),
- errorHandlerService: getIt(),
- ),
- );
+ getIt.registerLazySingleton(() => RadiologyViewModel(radiologyRepo: getIt(), errorHandlerService: getIt()));
- getIt.registerLazySingleton(
- () => PrescriptionsViewModel(
- prescriptionsRepo: getIt(),
- errorHandlerService: getIt(),
- ),
- );
+ getIt.registerLazySingleton(() => PrescriptionsViewModel(prescriptionsRepo: getIt(), errorHandlerService: getIt()));
- getIt.registerLazySingleton(
- () => InsuranceViewModel(
- insuranceRepo: getIt(),
- errorHandlerService: getIt(),
- ),
- );
+ getIt.registerLazySingleton(() => InsuranceViewModel(insuranceRepo: getIt(), errorHandlerService: getIt()));
getIt.registerLazySingleton(
- () => MyAppointmentsViewModel(myAppointmentsRepo: getIt(), errorHandlerService: getIt(), appState: getIt()),
- );
+ () => MyAppointmentsViewModel(myAppointmentsRepo: getIt(), errorHandlerService: getIt(), appState: getIt()));
getIt.registerLazySingleton(
() => PayfortViewModel(
@@ -198,53 +183,39 @@ class AppDependencies {
);
getIt.registerLazySingleton(() => ProfileSettingsViewModel());
- getIt.registerLazySingleton(
- () => DateRangeSelectorRangeViewModel(),
- );
+ getIt.registerLazySingleton(() => DateRangeSelectorRangeViewModel());
- getIt.registerLazySingleton(
- () => DoctorFilterViewModel(),
- );
+ getIt.registerLazySingleton(() => DoctorFilterViewModel());
getIt.registerLazySingleton(
- () => AppointmentViaRegionViewmodel(
- navigationService: getIt(),
- appState: getIt(),
- ),
+ () => AppointmentViaRegionViewmodel(navigationService: getIt(), appState: getIt()),
);
getIt.registerLazySingleton(
() => EmergencyServicesViewModel(
- locationUtils: getIt(),
- navServices: getIt(),
- emergencyServicesRepo: getIt(),
- appState: getIt(),
- errorHandlerService: getIt(),
- appointmentRepo: getIt(),
- dialogService: getIt()
- ),
+ locationUtils: getIt(),
+ navServices: getIt(),
+ emergencyServicesRepo: getIt(),
+ appState: getIt(),
+ errorHandlerService: getIt(),
+ appointmentRepo: getIt(),
+ dialogService: getIt()),
);
getIt.registerLazySingleton(
- () => LocationViewModel(
- locationRepo: getIt(),
- errorHandlerService: getIt(),
- ),
+ () => LocationViewModel(locationRepo: getIt(), errorHandlerService: getIt()),
);
getIt.registerLazySingleton(
- () => ContactUsViewModel(
- contactUsRepo: getIt(),
- appState: getIt(),
- errorHandlerService: getIt(),
- ),
+ () => ContactUsViewModel(contactUsRepo: getIt(), appState: getIt(), errorHandlerService: getIt()),
);
getIt.registerLazySingleton(
- () => TodoSectionViewModel(
- todoSectionRepo: getIt(),
- errorHandlerService: getIt(),
- ),
+ () => TodoSectionViewModel(todoSectionRepo: getIt(), errorHandlerService: getIt()),
+ );
+
+ getIt.registerLazySingleton(
+ () => HmgServicesViewModel(bookAppointmentsRepo: getIt(), hmgServicesRepo: getIt(), errorHandlerService: getIt()),
);
// Screen-specific VMs → Factory
diff --git a/lib/core/utils/date_util.dart b/lib/core/utils/date_util.dart
index 00a5aa3..a918706 100644
--- a/lib/core/utils/date_util.dart
+++ b/lib/core/utils/date_util.dart
@@ -11,14 +11,14 @@ class DateUtil {
if (date == null) return DateTime.now();
if (date.isEmpty) return DateTime.now();
- const start = "/Date(";
- const end = "+0300)";
- final startIndex = date.indexOf(start);
- final endIndex = date.indexOf(end, startIndex + start.length);
- return DateTime.fromMillisecondsSinceEpoch(int.parse(
- date.substring(startIndex + start.length, endIndex),
- ));
-
+ const start = "/Date(";
+ const end = "+0300)";
+ final startIndex = date.indexOf(start);
+ final endIndex = date.indexOf(end, startIndex + start.length);
+ return DateTime.fromMillisecondsSinceEpoch(int.parse(
+ date.substring(startIndex + start.length, endIndex),
+ ))
+ ;
}
static DateTime convertStringToDateSaudiTimezone(String date, int projectId) {
@@ -36,10 +36,10 @@ class DateUtil {
// .add(Duration(hours: 4));
// } else {
return DateTime.fromMillisecondsSinceEpoch(
- int.parse(
- date.substring(startIndex + start.length, endIndex),
- ),
- isUtc: true)
+ int.parse(
+ date.substring(startIndex + start.length, endIndex),
+ ),
+ isUtc: true)
.add(Duration(hours: 3));
// }
} else {
@@ -156,7 +156,13 @@ class DateUtil {
static String getDateFormatted(String date) {
DateTime dateObj = DateUtil.convertStringToDate(date);
- return DateUtil.getWeekDay(dateObj.weekday) + ", " + dateObj.day.toString() + " " + DateUtil.getMonth(dateObj.month) + " " + dateObj.year.toString();
+ return DateUtil.getWeekDay(dateObj.weekday) +
+ ", " +
+ dateObj.day.toString() +
+ " " +
+ DateUtil.getMonth(dateObj.month) +
+ " " +
+ dateObj.year.toString();
}
static String getISODateFormat(DateTime dateTime) {
@@ -352,7 +358,13 @@ class DateUtil {
if (dateTime != null) {
return lang == 'en'
? getWeekDayEnglish(dateTime.weekday) + ", " + getMonth(dateTime.month) + " " + dateTime.day.toString() + " " + dateTime.year.toString()
- : getWeekDayArabic(dateTime.weekday) + ", " + dateTime.day.toString() + " " + getMonthArabic(dateTime.month) + " " + dateTime.year.toString();
+ : getWeekDayArabic(dateTime.weekday) +
+ ", " +
+ dateTime.day.toString() +
+ " " +
+ getMonthArabic(dateTime.month) +
+ " " +
+ dateTime.year.toString();
} else {
return "";
}
@@ -381,7 +393,9 @@ class DateUtil {
static String getMonthYearLangDateFormatted(DateTime dateTime, String lang) {
if (dateTime != null) {
- return lang == 'en' ? getMonth(dateTime.month) + " " + dateTime.year.toString() : getMonthArabic(dateTime.month) + " " + dateTime.year.toString();
+ return lang == 'en'
+ ? getMonth(dateTime.month) + " " + dateTime.year.toString()
+ : getMonthArabic(dateTime.month) + " " + dateTime.year.toString();
} else {
return "";
}
@@ -488,10 +502,8 @@ class DateUtil {
}
}
-
-extension OnlyDate on DateTime{
-
- DateTime provideDateOnly(){
+extension OnlyDate on DateTime {
+ DateTime provideDateOnly() {
return DateTime(this.year, month, day);
}
-}
\ No newline at end of file
+}
diff --git a/lib/core/utils/utils.dart b/lib/core/utils/utils.dart
index 6f1dcc6..e3b108f 100644
--- a/lib/core/utils/utils.dart
+++ b/lib/core/utils/utils.dart
@@ -102,8 +102,9 @@ class Utils {
? getMonthArabic(dateTime.month) + " " + dateTime.day.toString() + ", " + dateTime.year.toString()
: getMonth(dateTime.month) + " " + dateTime.day.toString() + ", " + dateTime.year.toString();
}
+
static String getDayMonthYearDateFormatted(DateTime? dateTime) {
- if(dateTime == null ) return "";
+ if (dateTime == null) return "";
return appState.isArabic()
? "${dateTime.day.toString()} ${getMonthArabic(dateTime.month)}, ${dateTime.year.toString()}"
: "${dateTime.day.toString()} ${getMonth(dateTime.month)}, ${dateTime.year.toString()}";
@@ -376,16 +377,21 @@ class Utils {
).center;
}
- static Widget getWarningWidget(
- {String? loadingText, bool isShowActionButtons = false, Widget? bodyWidget, Function? onConfirmTap, Function? onCancelTap}) {
+ static Widget getWarningWidget({
+ String? loadingText,
+ bool isShowActionButtons = false,
+ Widget? bodyWidget,
+ Function? onConfirmTap,
+ Function? onCancelTap,
+ }) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Lottie.asset(AppAnimations.warningAnimation,
- repeat: false, reverse: false, frameRate: FrameRate(60), width: 128.h, height: 128.h, fit: BoxFit.fill),
+ repeat: false, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill),
SizedBox(height: 8.h),
- (loadingText ?? LocaleKeys.loadingText.tr()).toText14(color: AppColors.blackColor, letterSpacing: 0),
+ (loadingText ?? LocaleKeys.loadingText.tr()).toText15(color: AppColors.blackColor, letterSpacing: 0),
SizedBox(height: 16.h),
bodyWidget ?? SizedBox.shrink(),
SizedBox(height: 16.h),
@@ -753,14 +759,15 @@ class Utils {
);
}
- static Widget getPaymentAmountWithSymbol2(num habibWalletAmount,
- {double iconSize = 14,
+ static Widget getPaymentAmountWithSymbol2(
+ num habibWalletAmount, {
+ double iconSize = 14,
double? fontSize,
double? letterSpacing,
FontWeight? fontWeight,
Color iconColor = AppColors.textColor,
- Color textColor = AppColors.blackColor,
- bool isSaudiCurrency = true,
+ Color textColor = AppColors.blackColor,
+ bool isSaudiCurrency = true,
bool isExpanded = true,
}) {
return RichText(
@@ -777,7 +784,7 @@ class Utils {
style: TextStyle(
color: textColor,
fontSize: fontSize ?? 32.f,
- letterSpacing: letterSpacing??-4,
+ letterSpacing: letterSpacing ?? -4,
fontWeight: fontWeight ?? FontWeight.w600,
height: 1),
),
@@ -839,7 +846,12 @@ class Utils {
static PatientDoctorAppointmentList? convertToPatientDoctorAppointmentList(HospitalsModel? hospital) {
if (hospital == null) return null;
return PatientDoctorAppointmentList(
- filterName: hospital.name, distanceInKMs: hospital.distanceInKilometers?.toString(), projectTopName: hospital.name, projectBottomName: hospital.name, model: hospital, isHMC: hospital.isHMC);
+ filterName: hospital.name,
+ distanceInKMs: hospital.distanceInKilometers?.toString(),
+ projectTopName: hospital.name,
+ projectBottomName: hospital.name,
+ model: hospital,
+ isHMC: hospital.isHMC);
}
static bool havePrivilege(int id) {
@@ -853,7 +865,4 @@ class Utils {
}
return isHavePrivilege;
}
-
-
-
}
diff --git a/lib/extensions/widget_extensions.dart b/lib/extensions/widget_extensions.dart
index 424aa88..70f10bb 100644
--- a/lib/extensions/widget_extensions.dart
+++ b/lib/extensions/widget_extensions.dart
@@ -1,9 +1,8 @@
-import 'package:hmg_patient_app_new/core/enums.dart';
-import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:flutter/material.dart';
-import 'package:flutter/widgets.dart';
+import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/extensions/int_extensions.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
+import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:shimmer/shimmer.dart';
import 'package:sizer/sizer.dart';
import 'package:smooth_corner/smooth_corner.dart';
@@ -19,7 +18,8 @@ extension WidgetExtensions on Widget {
Widget paddingAll(double _value) => Padding(padding: EdgeInsets.all(_value), child: this);
- Widget paddingSymmetrical(double horizontal, double vertical) => Padding(padding: EdgeInsets.symmetric(horizontal: horizontal, vertical: vertical), child: this);
+ Widget paddingSymmetrical(double horizontal, double vertical) =>
+ Padding(padding: EdgeInsets.symmetric(horizontal: horizontal, vertical: vertical), child: this);
Widget paddingOnly({double left = 0.0, double right = 0.0, double top = 0.0, double bottom = 0.0}) =>
Padding(padding: EdgeInsetsDirectional.only(start: left, end: right, top: top, bottom: bottom), child: this);
@@ -99,7 +99,7 @@ extension WidgetExtensions on Widget {
bool disablePadding = false,
double radius = 20,
Color? color,
- Color borderColor = AppColors.buttonColor,
+ Color? borderColor,
bool disableWidth = false,
bool isAlignment = false}) {
return Container(
@@ -110,7 +110,7 @@ extension WidgetExtensions on Widget {
),
color: color,
border: Border.all(
- color: borderColor,
+ color: borderColor ?? Colors.transparent,
width: disableWidth ? 2 : 1,
),
),
diff --git a/lib/features/book_appointments/book_appointments_view_model.dart b/lib/features/book_appointments/book_appointments_view_model.dart
index f24766b..a50b683 100644
--- a/lib/features/book_appointments/book_appointments_view_model.dart
+++ b/lib/features/book_appointments/book_appointments_view_model.dart
@@ -3,7 +3,6 @@ import 'dart:async';
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/core/cache_consts.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/location_util.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
@@ -105,8 +104,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
List searchedRegionList = [];
List facilityList = ["hmgHospitals", "hmcMedicalClinic"];
List searchedHospitalList = [];
- List
- searchedPatientDoctorAppointmentHospitalsList = [];
+ List searchedPatientDoctorAppointmentHospitalsList = [];
List searchedClinicList = [];
PatientDoctorAppointmentList? selectedHospitalForFilters;
@@ -114,15 +112,14 @@ class BookAppointmentsViewModel extends ChangeNotifier {
String? selectedClinicForFilters;
bool applyFilters = false;
-
///variables for laser clinic
- List femaleLaserCategory = [
+ List femaleLaserCategory = [
LaserCategoryType(1, 'bodyString'),
LaserCategoryType(2, 'face'),
- LaserCategoryType(10,'bikini'),
+ LaserCategoryType(10, 'bikini'),
LaserCategoryType(11, 'retouch'),
];
- List maleLaserCategory =[
+ List maleLaserCategory = [
LaserCategoryType(1, 'body'),
LaserCategoryType(2, 'face'),
LaserCategoryType(11, 'retouch'),
@@ -136,9 +133,13 @@ class BookAppointmentsViewModel extends ChangeNotifier {
bool isBodyPartsLoading = false;
int duration = 0;
-
BookAppointmentsViewModel(
- {required this.bookAppointmentsRepo, required this.errorHandlerService, required this.navigationService, required this.myAppointmentsViewModel, required this.locationUtils, required this.dialogService }) {
+ {required this.bookAppointmentsRepo,
+ required this.errorHandlerService,
+ required this.navigationService,
+ required this.myAppointmentsViewModel,
+ required this.locationUtils,
+ required this.dialogService}) {
initBookAppointmentViewModel();
}
@@ -287,7 +288,8 @@ class BookAppointmentsViewModel extends ChangeNotifier {
Future getLiveCareScheduleClinics({Function(dynamic)? onSuccess, Function(String)? onError}) async {
liveCareClinicsList.clear();
- final result = await bookAppointmentsRepo.getLiveCareScheduleClinics(_appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!);
+ final result =
+ await bookAppointmentsRepo.getLiveCareScheduleClinics(_appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!);
result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure),
@@ -309,8 +311,9 @@ class BookAppointmentsViewModel extends ChangeNotifier {
Future getLiveCareDoctorsList({Function(dynamic)? onSuccess, Function(String)? onError}) async {
doctorsList.clear();
- final result =
- await bookAppointmentsRepo.getLiveCareDoctorsList(selectedLiveCareClinic.serviceID!, _appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!, onError: onError);
+ final result = await bookAppointmentsRepo.getLiveCareDoctorsList(
+ selectedLiveCareClinic.serviceID!, _appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!,
+ onError: onError);
result.fold(
(failure) async {
@@ -333,10 +336,17 @@ class BookAppointmentsViewModel extends ChangeNotifier {
}
//TODO: Make the API dynamic with parameters for ProjectID, isNearest, languageID, doctorId, doctorName
- Future getDoctorsList({int projectID = 0, bool isNearest = false, int doctorId = 0, String doctorName = "", Function(dynamic)? onSuccess, Function(String)? onError}) async {
+ Future getDoctorsList(
+ {int projectID = 0,
+ bool isNearest = false,
+ int doctorId = 0,
+ String doctorName = "",
+ Function(dynamic)? onSuccess,
+ Function(String)? onError}) async {
doctorsList.clear();
projectID = currentlySelectedHospitalFromRegionFlow != null ? int.parse(currentlySelectedHospitalFromRegionFlow!) : projectID;
- final result = await bookAppointmentsRepo.getDoctorsList(selectedClinic.clinicID ?? 0, projectID, isNearest, doctorId, doctorName, isContinueDentalPlan: isContinueDentalPlan);
+ final result = await bookAppointmentsRepo.getDoctorsList(selectedClinic.clinicID ?? 0, projectID, isNearest, doctorId, doctorName,
+ isContinueDentalPlan: isContinueDentalPlan);
result.fold(
(failure) async {
@@ -365,7 +375,13 @@ class BookAppointmentsViewModel extends ChangeNotifier {
}
Future getMappedDoctors(
- {int projectID = 0, bool isNearest = false, int doctorId = 0, String doctorName = "", isContinueDentalPlan = false, Function(dynamic)? onSuccess, Function(String)? onError}) async {
+ {int projectID = 0,
+ bool isNearest = false,
+ int doctorId = 0,
+ String doctorName = "",
+ isContinueDentalPlan = false,
+ Function(dynamic)? onSuccess,
+ Function(String)? onError}) async {
filteredHospitalList = null;
hospitalList = null;
isRegionListLoading = true;
@@ -374,10 +390,10 @@ class BookAppointmentsViewModel extends ChangeNotifier {
final result = await bookAppointmentsRepo.getDoctorsList(selectedClinic.clinicID ?? 0, projectID, isNearest, doctorId, doctorName);
result.fold(
- (failure) async {
+ (failure) async {
onError?.call("No doctors found for the search criteria".needTranslation);
},
- (apiResponse) async {
+ (apiResponse) async {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
@@ -401,7 +417,8 @@ class BookAppointmentsViewModel extends ChangeNotifier {
}
Future getDoctorProfile({Function(dynamic)? onSuccess, Function(String)? onError}) async {
- final result = await bookAppointmentsRepo.getDoctorProfile(selectedDoctor.clinicID ?? 0, selectedDoctor.projectID ?? 0, selectedDoctor.doctorID ?? 0, onError: onError);
+ final result = await bookAppointmentsRepo
+ .getDoctorProfile(selectedDoctor.clinicID ?? 0, selectedDoctor.projectID ?? 0, selectedDoctor.doctorID ?? 0, onError: onError);
result.fold(
(failure) async {},
@@ -457,7 +474,8 @@ class BookAppointmentsViewModel extends ChangeNotifier {
// :
date = DateUtil.convertStringToDateSaudiTimezone(element, int.parse(selectedDoctor.projectID.toString()));
slotsList.add(FreeSlot(date, ['slot']));
- docFreeSlots.add(TimeSlot(isoTime: formatter.format(date), start: new DateTime(date.year, date.month, date.day, 0, 0, 0, 0), end: date, vidaDate: element));
+ docFreeSlots.add(TimeSlot(
+ isoTime: formatter.format(date), start: new DateTime(date.year, date.month, date.day, 0, 0, 0, 0), end: date, vidaDate: element));
});
notifyListeners();
@@ -476,8 +494,8 @@ class BookAppointmentsViewModel extends ChangeNotifier {
final DateFormat dateFormatter = DateFormat('yyyy-MM-dd');
Map _eventsParsed;
- final result = await bookAppointmentsRepo.getLiveCareDoctorFreeSlots(
- selectedDoctor.clinicID ?? 0, selectedLiveCareClinic.serviceID ?? 0, selectedDoctor.projectID ?? 0, selectedDoctor.doctorID ?? 0, isBookingForLiveCare,
+ final result = await bookAppointmentsRepo.getLiveCareDoctorFreeSlots(selectedDoctor.clinicID ?? 0, selectedLiveCareClinic.serviceID ?? 0,
+ selectedDoctor.projectID ?? 0, selectedDoctor.doctorID ?? 0, isBookingForLiveCare,
onError: onError);
result.fold(
@@ -501,7 +519,8 @@ class BookAppointmentsViewModel extends ChangeNotifier {
// :
date = DateUtil.convertStringToDateSaudiTimezone(element, int.parse(selectedDoctor.projectID.toString()));
slotsList.add(FreeSlot(date, ['slot']));
- docFreeSlots.add(TimeSlot(isoTime: formatter.format(date), start: new DateTime(date.year, date.month, date.day, 0, 0, 0, 0), end: date, vidaDate: element));
+ docFreeSlots.add(TimeSlot(
+ isoTime: formatter.format(date), start: new DateTime(date.year, date.month, date.day, 0, 0, 0, 0), end: date, vidaDate: element));
});
notifyListeners();
@@ -513,7 +532,10 @@ class BookAppointmentsViewModel extends ChangeNotifier {
);
}
- Future cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, Function(dynamic)? onSuccess, Function(String)? onError}) async {
+ Future cancelAppointment(
+ {required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel,
+ Function(dynamic)? onSuccess,
+ Function(String)? onError}) async {
final result = await bookAppointmentsRepo.cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel);
result.fold(
@@ -597,13 +619,15 @@ class BookAppointmentsViewModel extends ChangeNotifier {
await cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel).then((val) async {
navigationService.pop();
Future.delayed(Duration(milliseconds: 50)).then((value) async {});
- LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: false, loadingText: "Booking your appointment...".needTranslation);
+ LoadingUtils.showFullScreenLoader(
+ barrierDismissible: true, isSuccessDialog: false, loadingText: "Booking your appointment...".needTranslation);
await insertSpecificAppointment(
onError: (err) {},
onSuccess: (apiResp) async {
LoadingUtils.hideFullScreenLoader();
await Future.delayed(Duration(milliseconds: 50)).then((value) async {
- LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr());
+ LoadingUtils.showFullScreenLoader(
+ barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr());
await Future.delayed(Duration(milliseconds: 4000)).then((value) {
LoadingUtils.hideFullScreenLoader();
Navigator.pushAndRemoveUntil(
@@ -693,13 +717,15 @@ class BookAppointmentsViewModel extends ChangeNotifier {
await cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel).then((val) async {
navigationService.pop();
Future.delayed(Duration(milliseconds: 50)).then((value) async {});
- LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: false, loadingText: "Booking your appointment...".needTranslation);
+ LoadingUtils.showFullScreenLoader(
+ barrierDismissible: true, isSuccessDialog: false, loadingText: "Booking your appointment...".needTranslation);
await insertSpecificAppointment(
onError: (err) {},
onSuccess: (apiResp) async {
LoadingUtils.hideFullScreenLoader();
await Future.delayed(Duration(milliseconds: 50)).then((value) async {
- LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr());
+ LoadingUtils.showFullScreenLoader(
+ barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr());
await Future.delayed(Duration(milliseconds: 4000)).then((value) {
LoadingUtils.hideFullScreenLoader();
Navigator.pushAndRemoveUntil(
@@ -773,7 +799,9 @@ class BookAppointmentsViewModel extends ChangeNotifier {
} else {
filteredHospitalList = RegionList();
- var list = isHMG ? hospitalList?.registeredDoctorMap![selectedRegionId]!.hmgDoctorList : hospitalList?.registeredDoctorMap![selectedRegionId]!.hmcDoctorList;
+ var list = isHMG
+ ? hospitalList?.registeredDoctorMap![selectedRegionId]!.hmgDoctorList
+ : hospitalList?.registeredDoctorMap![selectedRegionId]!.hmcDoctorList;
if (list != null && list.isEmpty) {
notifyListeners();
@@ -856,12 +884,8 @@ class BookAppointmentsViewModel extends ChangeNotifier {
notifyListeners();
}
- void setSelections(
- List? selectedFacilityForFilters,
- List? selectedRegionForFilters,
- String? selectedClinicForFilters,
- PatientDoctorAppointmentList? selectedHospitalForFilters,
- bool applyFilters) {
+ void setSelections(List? selectedFacilityForFilters, List? selectedRegionForFilters, String? selectedClinicForFilters,
+ PatientDoctorAppointmentList? selectedHospitalForFilters, bool applyFilters) {
this.selectedFacilityForFilters = selectedFacilityForFilters;
this.selectedClinicForFilters = selectedClinicForFilters;
this.selectedHospitalForFilters = selectedHospitalForFilters;
@@ -872,17 +896,14 @@ class BookAppointmentsViewModel extends ChangeNotifier {
void getFiltersFromDoctorList() {
doctorsList.forEach((element) {
- if (!searchedRegionList
- .contains(element.getRegionName(_appState.isArabic()))) {
- searchedRegionList
- .add(element.getRegionName(_appState.isArabic()) ?? "");
+ if (!searchedRegionList.contains(element.getRegionName(_appState.isArabic()))) {
+ searchedRegionList.add(element.getRegionName(_appState.isArabic()) ?? "");
}
if (!searchedHospitalList.contains(element.projectName)) {
- searchedPatientDoctorAppointmentHospitalsList
- .add(PatientDoctorAppointmentList()
- ..filterName = element.projectName
- ..isHMC = element.isHMC
- ..distanceInKMs = "0");
+ searchedPatientDoctorAppointmentHospitalsList.add(PatientDoctorAppointmentList()
+ ..filterName = element.projectName
+ ..isHMC = element.isHMC
+ ..distanceInKMs = "0");
searchedHospitalList.add(element.projectName ?? "");
}
if (!searchedClinicList.contains(element.clinicName)) {
@@ -939,27 +960,15 @@ class BookAppointmentsViewModel extends ChangeNotifier {
return doctorsList;
}
var list = doctorsList.where((element) {
- var isInSelectedRegion = (selectedRegionForFilters?.isEmpty == true)
- ? true
- : selectedRegionForFilters
- ?.any((region) => region == element.getRegionName(isArabic()));
- var shouldApplyFacilityFilter =
- (selectedFacilityForFilters?.isEmpty == true) ? false : true;
- var isHMC = (selectedFacilityForFilters?.isEmpty == true)
- ? true
- : selectedFacilityForFilters?.any((item) => item.contains("hmc"));
- var isInSelectedClinic = (selectedClinicForFilters == null)
- ? true
- : selectedClinicForFilters == element.clinicName;
- var isInSelectedHospital = (selectedHospitalForFilters == null)
- ? true
- : element.projectName == selectedHospitalForFilters?.filterName;
+ var isInSelectedRegion =
+ (selectedRegionForFilters?.isEmpty == true) ? true : selectedRegionForFilters?.any((region) => region == element.getRegionName(isArabic()));
+ var shouldApplyFacilityFilter = (selectedFacilityForFilters?.isEmpty == true) ? false : true;
+ var isHMC = (selectedFacilityForFilters?.isEmpty == true) ? true : selectedFacilityForFilters?.any((item) => item.contains("hmc"));
+ var isInSelectedClinic = (selectedClinicForFilters == null) ? true : selectedClinicForFilters == element.clinicName;
+ var isInSelectedHospital = (selectedHospitalForFilters == null) ? true : element.projectName == selectedHospitalForFilters?.filterName;
var facilityFilter = ((shouldApplyFacilityFilter == true) ? isHMC : true);
- return (isInSelectedRegion ?? true) &&
- (facilityFilter ?? true) &&
- isInSelectedClinic &&
- isInSelectedHospital;
+ return (isInSelectedRegion ?? true) && (facilityFilter ?? true) && isInSelectedClinic && isInSelectedHospital;
}).toList();
return list;
}
@@ -1003,7 +1012,8 @@ class BookAppointmentsViewModel extends ChangeNotifier {
dentalChiefComplaintsList.clear();
notifyListeners();
int patientID = _appState.isAuthenticated ? _appState.getAuthenticatedUser()!.patientId ?? -1 : -1;
- final result = await bookAppointmentsRepo.getDentalChiefComplaintsList(patientID: patientID, projectID: int.parse(currentlySelectedHospitalFromRegionFlow ?? "0"), clinicID: 17);
+ final result = await bookAppointmentsRepo.getDentalChiefComplaintsList(
+ patientID: patientID, projectID: int.parse(currentlySelectedHospitalFromRegionFlow ?? "0"), clinicID: 17);
result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure),
@@ -1051,7 +1061,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
);
}
- setBodyType(int bodyType){
+ setBodyType(int bodyType) {
selectedBodyTypeIndex = bodyType;
selectedCategory = 0;
selectedBodyPartList = [];
@@ -1059,33 +1069,33 @@ class BookAppointmentsViewModel extends ChangeNotifier {
notifyListeners();
}
- FutureOr getLaserClinic() async{
+ FutureOr getLaserClinic() async {
isBodyPartsLoading = true;
int id = bodyTypes[selectedBodyTypeIndex][selectedCategory].laserCategoryID;
int projectID = currentlySelectedHospitalFromRegionFlow != null ? int.parse(currentlySelectedHospitalFromRegionFlow!) : 0;
int languageID = _appState.isArabic() ? 1 : 0;
final result = await bookAppointmentsRepo.getLaserClinics(id, projectID, languageID);
result.fold(
- (failure) {
+ (failure) {
isBodyPartsLoading = false;
notifyListeners();
},
- (apiResponse) {isBodyPartsLoading = false;
+ (apiResponse) {
+ isBodyPartsLoading = false;
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
- List response =apiResponse.data!;
- if(response.first.category == 2 || response.first.category == 10 ) response.remove(response.first);
+ List response = apiResponse.data!;
+ if (response.first.category == 2 || response.first.category == 10) response.remove(response.first);
laserBodyPartsList = response;
}
- notifyListeners();
-
+ notifyListeners();
},
);
}
int getDuration() {
- var duration = 0;
+ var duration = 0;
var lowerUpperLegsList = selectedBodyPartList.where((element) => element.mappingCode == "47" || element.mappingCode == "48")?.toList() ?? [];
var upperLowerArmsList = selectedBodyPartList.where((element) => element.mappingCode == "40" || element.mappingCode == "41")?.toList() ?? [];
@@ -1110,21 +1120,25 @@ class BookAppointmentsViewModel extends ChangeNotifier {
}
void setSelectedBodyPart(LaserBodyPart part) {
- if(selectedBodyPartList.contains(part)){
+ if (selectedBodyPartList.contains(part)) {
selectedBodyPartList.remove(part);
this.duration = getDuration();
notifyListeners();
} else {
- if(this.duration == 90){
- dialogService.showErrorBottomSheet(message: "Duration can not exceed 90 min".needTranslation,);
+ if (this.duration == 90) {
+ dialogService.showErrorBottomSheet(
+ message: "Duration can not exceed 90 min".needTranslation,
+ );
return;
}
selectedBodyPartList.add(part);
var duration = getDuration();
- if(duration > 90){
+ if (duration > 90) {
selectedBodyPartList.remove(part);
- dialogService.showErrorBottomSheet(message: "Duration Exceeds 90 min".needTranslation,);
+ dialogService.showErrorBottomSheet(
+ message: "Duration Exceeds 90 min".needTranslation,
+ );
return;
}
this.duration = duration;
@@ -1137,10 +1151,10 @@ class BookAppointmentsViewModel extends ChangeNotifier {
}
String getLaserProcedureNameWRTLanguage(LaserBodyPart part) {
- if(_appState.isArabic()){
- return part.bodyPartN??"";
- }else {
- return part.bodyPart??"";
+ if (_appState.isArabic()) {
+ return part.bodyPartN ?? "";
+ } else {
+ return part.bodyPart ?? "";
}
}
diff --git a/lib/features/emergency_services/emergency_services_repo.dart b/lib/features/emergency_services/emergency_services_repo.dart
index 4ec06af..c63f0ee 100644
--- a/lib/features/emergency_services/emergency_services_repo.dart
+++ b/lib/features/emergency_services/emergency_services_repo.dart
@@ -46,7 +46,6 @@ abstract class EmergencyServicesRepo {
Future>>> getTransportationMethods({int? id});
-
Future>> submitAmbulanceRequest(PatientER_RC request);
Future>>> getTransportationOrders({int? id});
@@ -158,7 +157,10 @@ class EmergencyServicesRepoImp implements EmergencyServicesRepo {
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response['response']['transportationservices'];
- final proceduresList = list.map((item) => PatientERTransportationMethod.fromJson(item as Map)).toList().cast();
+ final proceduresList = list
+ .map((item) => PatientERTransportationMethod.fromJson(item as Map))
+ .toList()
+ .cast();
apiResponse = GenericApiModel>(
messageStatus: messageStatus,
@@ -257,7 +259,7 @@ class EmergencyServicesRepoImp implements EmergencyServicesRepo {
Failure? failure;
await apiClient.post(
body: {},
- "$GET_ALL_TRANSPORTATIONS_ORDERS?patientID=$id",
+ "$GET_ALL_TRANSPORTATIONS_ORDERS?patientID=$id",
isRCService: true,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
@@ -265,7 +267,8 @@ class EmergencyServicesRepoImp implements EmergencyServicesRepo {
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response['response'];
- final proceduresList = list.map((item) => AmbulanceRequestOrdersModel.fromJson(item as Map)).toList().cast();
+ final proceduresList =
+ list.map((item) => AmbulanceRequestOrdersModel.fromJson(item as Map)).toList().cast();
apiResponse = GenericApiModel>(
messageStatus: messageStatus,
@@ -335,13 +338,11 @@ class EmergencyServicesRepoImp implements EmergencyServicesRepo {
await apiClient.post(
CHECK_PATIENT_ER_ADVANCE_BALANCE,
body: mapDevice,
- onFailure: (error, statusCode, {messageStatus, failureType}) {
- failure = failureType;
- },
+ onFailure: (error, statusCode, {messageStatus, failureType}) => failure = failureType,
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final bool patientHasERBalance = response['BalanceAmount'] > 0;
- print(patientHasERBalance);
+ log(patientHasERBalance.toString());
apiResponse = GenericApiModel(
messageStatus: messageStatus,
statusCode: statusCode,
@@ -361,7 +362,6 @@ class EmergencyServicesRepoImp implements EmergencyServicesRepo {
}
}
-
@override
Future>> checkPatientERPaymentInformation({int? projectID}) async {
Map mapDevice = {"ClinicID": 10, "ProjectID": projectID ?? 0};
diff --git a/lib/features/emergency_services/emergency_services_view_model.dart b/lib/features/emergency_services/emergency_services_view_model.dart
index 47a0ccd..5dbb89d 100644
--- a/lib/features/emergency_services/emergency_services_view_model.dart
+++ b/lib/features/emergency_services/emergency_services_view_model.dart
@@ -25,6 +25,7 @@ import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/
import 'package:hmg_patient_app_new/features/emergency_services/models/AmbulanceCallingPlace.dart';
import 'package:hmg_patient_app_new/features/emergency_services/models/request_model/PatientER_RC.dart';
import 'package:hmg_patient_app_new/features/emergency_services/models/resp_model/AmbulanceRequestOrdersModel.dart';
+import 'package:hmg_patient_app_new/features/emergency_services/models/resp_model/EROnlineCheckInPaymentDetailsResponse.dart';
import 'package:hmg_patient_app_new/features/emergency_services/models/resp_model/PatientERTransportationMethod.dart'
show PatientERTransportationMethod;
import 'package:hmg_patient_app_new/features/emergency_services/models/resp_model/ProjectAvgERWaitingTime.dart';
@@ -32,7 +33,6 @@ import 'package:hmg_patient_app_new/features/emergency_services/models/resp_mode
import 'package:hmg_patient_app_new/features/location/PlaceDetails.dart' show PlaceDetails;
import 'package:hmg_patient_app_new/features/location/PlacePrediction.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/facility_selection.dart';
-import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/doctor_list_api_response.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_repo.dart';
@@ -41,10 +41,10 @@ import 'package:hmg_patient_app_new/presentation/emergency_services/RRT/rrt_map_
import 'package:hmg_patient_app_new/presentation/emergency_services/RRT/rrt_request_type_select.dart';
import 'package:hmg_patient_app_new/presentation/emergency_services/RRT/terms_and_condition.dart';
import 'package:hmg_patient_app_new/presentation/emergency_services/call_ambulance/call_ambulance_page.dart';
-import 'package:hmg_patient_app_new/presentation/emergency_services/er_online_checkin/er_online_checkin_home.dart';
-import 'package:hmg_patient_app_new/presentation/emergency_services/er_online_checkin/er_online_checkin_payment_details_page.dart';
import 'package:hmg_patient_app_new/presentation/emergency_services/call_ambulance/requesting_services_page.dart';
import 'package:hmg_patient_app_new/presentation/emergency_services/call_ambulance/tracking_screen.dart';
+import 'package:hmg_patient_app_new/presentation/emergency_services/er_online_checkin/er_online_checkin_home.dart';
+import 'package:hmg_patient_app_new/presentation/emergency_services/er_online_checkin/er_online_checkin_payment_details_page.dart';
import 'package:hmg_patient_app_new/presentation/emergency_services/nearest_er_page.dart';
import 'package:hmg_patient_app_new/routes/app_routes.dart' show AppRoutes;
import 'package:hmg_patient_app_new/services/dialog_service.dart';
@@ -129,8 +129,7 @@ class EmergencyServicesViewModel extends ChangeNotifier {
notifyListeners();
}
- get isGMSAvailable
- {
+ get isGMSAvailable {
return appState.isGMSAvailable;
}
@@ -212,7 +211,8 @@ class EmergencyServicesViewModel extends ChangeNotifier {
if (query.isEmpty) {
nearestERFilteredList = nearestERList;
} else {
- nearestERFilteredList = nearestERList.where((er) => er.projectName != null && er.projectName!.toLowerCase().contains(query.toLowerCase())).toList();
+ nearestERFilteredList =
+ nearestERList.where((er) => er.projectName != null && er.projectName!.toLowerCase().contains(query.toLowerCase())).toList();
}
notifyListeners();
}
@@ -295,8 +295,7 @@ class EmergencyServicesViewModel extends ChangeNotifier {
onSuccess: (position) {
updateBottomSheetState(BottomSheetType.FIXED);
navServices.push(
- CustomPageRoute(
- page: CallAmbulancePage(), direction: AxisDirection.down),
+ CustomPageRoute(page: CallAmbulancePage(), direction: AxisDirection.down),
);
});
} else {
@@ -304,9 +303,7 @@ class EmergencyServicesViewModel extends ChangeNotifier {
message: "You Need To Login First To Continue".needTranslation,
onOkPressed: () {
navServices.pop();
- navServices.pushAndReplace(
- AppRoutes.loginScreen
- );
+ navServices.pushAndReplace(AppRoutes.loginScreen);
});
}
}
@@ -338,19 +335,20 @@ class EmergencyServicesViewModel extends ChangeNotifier {
void setIsGMSAvailable(bool value) {
notifyListeners();
}
+
Future checkPatientERAdvanceBalance({Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await emergencyServicesRepo.checkPatientERAdvanceBalance();
result.fold(
// (failure) async => await errorHandlerService.handleError(failure: failure),
- (failure) {
+ (failure) {
patientHasAdvanceERBalance = false;
isERBookAppointment = true;
if (onSuccess != null) {
onSuccess(failure.message);
}
},
- (apiResponse) {
+ (apiResponse) {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
patientHasAdvanceERBalance = false;
@@ -371,12 +369,12 @@ class EmergencyServicesViewModel extends ChangeNotifier {
final result = await emergencyServicesRepo.checkPatientERPaymentInformation(projectID: selectedHospital!.iD);
result.fold(
- (failure) {
+ (failure) {
if (onError != null) {
onError(failure.message);
}
},
- (apiResponse) {
+ (apiResponse) {
if (apiResponse.messageStatus == 2) {
} else if (apiResponse.messageStatus == 1) {
erOnlineCheckInPaymentDetailsResponse = apiResponse.data!;
@@ -389,7 +387,8 @@ class EmergencyServicesViewModel extends ChangeNotifier {
);
}
- Future ER_CreateAdvancePayment({required String paymentMethodName, required String paymentReference, Function(dynamic)? onSuccess, Function(String)? onError}) async {
+ Future ER_CreateAdvancePayment(
+ {required String paymentMethodName, required String paymentReference, Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await emergencyServicesRepo.createAdvancePaymentForER(
projectID: selectedHospital!.iD,
authUser: appState.getAuthenticatedUser()!,
@@ -398,12 +397,12 @@ class EmergencyServicesViewModel extends ChangeNotifier {
paymentReference: paymentReference);
result.fold(
- (failure) {
+ (failure) {
if (onError != null) {
onError(failure.message);
}
},
- (apiResponse) {
+ (apiResponse) {
if (apiResponse.messageStatus == 2) {
} else if (apiResponse.messageStatus == 1) {
// erOnlineCheckInPaymentDetailsResponse = apiResponse.data!;
@@ -417,12 +416,17 @@ class EmergencyServicesViewModel extends ChangeNotifier {
}
Future addAdvanceNumberRequest(
- {required String advanceNumber, required String paymentReference, required String appointmentNo, Function(dynamic)? onSuccess, Function(String)? onError}) async {
- final result = await emergencyServicesRepo.addAdvanceNumberRequest(advanceNumber: advanceNumber, paymentReference: paymentReference, appointmentNo: appointmentNo);
+ {required String advanceNumber,
+ required String paymentReference,
+ required String appointmentNo,
+ Function(dynamic)? onSuccess,
+ Function(String)? onError}) async {
+ final result = await emergencyServicesRepo.addAdvanceNumberRequest(
+ advanceNumber: advanceNumber, paymentReference: paymentReference, appointmentNo: appointmentNo);
result.fold(
- (failure) async => await errorHandlerService.handleError(failure: failure),
- (apiResponse) {
+ (failure) async => await errorHandlerService.handleError(failure: failure),
+ (apiResponse) {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
@@ -440,12 +444,12 @@ class EmergencyServicesViewModel extends ChangeNotifier {
result.fold(
// (failure) async => await errorHandlerService.handleError(failure: failure),
- (failure) {
+ (failure) {
if (onError != null) {
onError(failure.message);
}
},
- (apiResponse) {
+ (apiResponse) {
if (apiResponse.messageStatus == 2) {
if (onError != null) {
onError(apiResponse.errorMessage!);
@@ -465,12 +469,12 @@ class EmergencyServicesViewModel extends ChangeNotifier {
result.fold(
// (failure) async => await errorHandlerService.handleError(failure: failure),
- (failure) {
+ (failure) {
if (onError != null) {
onError(failure.message);
}
},
- (apiResponse) {
+ (apiResponse) {
if (apiResponse.messageStatus == 2) {
if (onError != null) {
onError(apiResponse.data["InvoiceResponse"]["Message"]);
@@ -496,14 +500,13 @@ class EmergencyServicesViewModel extends ChangeNotifier {
onOkPressed: () {
navServices.pop();
print("inside the ok button");
- getIt().onLoginPressed();
+ getIt().onLoginPressed();
});
return;
}
int? id = appState.getAuthenticatedUser()?.patientId;
- LoaderBottomSheet.showLoader(
- loadingText: "Getting Ambulance Transport Option".needTranslation);
+ LoaderBottomSheet.showLoader(loadingText: "Getting Ambulance Transport Option".needTranslation);
notifyListeners();
var response = await emergencyServicesRepo.getTransportationMethods(id: id);
@@ -522,8 +525,7 @@ class EmergencyServicesViewModel extends ChangeNotifier {
Future getTransportationMethods() async {
int? id = appState.getAuthenticatedUser()?.patientId;
- LoaderBottomSheet.showLoader(
- loadingText: "Getting Ambulance Transport Option".needTranslation);
+ LoaderBottomSheet.showLoader(loadingText: "Getting Ambulance Transport Option".needTranslation);
notifyListeners();
var response = await emergencyServicesRepo.getTransportationMethods(id: id);
@@ -634,11 +636,7 @@ class EmergencyServicesViewModel extends ChangeNotifier {
sourceList = hmcHospitalList;
break;
}
- displayList = sourceList
- ?.where((hospital) =>
- hospital.name != null &&
- hospital.name!.toLowerCase().contains(query.toLowerCase()))
- .toList();
+ displayList = sourceList?.where((hospital) => hospital.name != null && hospital.name!.toLowerCase().contains(query.toLowerCase())).toList();
notifyListeners();
}
@@ -657,7 +655,6 @@ class EmergencyServicesViewModel extends ChangeNotifier {
notifyListeners();
}
-
void setSelectedHospital(HospitalsModel? hospital) {
selectedHospital = hospital;
notifyListeners();
@@ -705,13 +702,12 @@ class EmergencyServicesViewModel extends ChangeNotifier {
}
Future updateAppointment(bool value) async {
-
if (value) {
await getAppointments();
} else {
clearAppointmentData();
}
- if(appointments?.isNotEmpty == true) {
+ if (appointments?.isNotEmpty == true) {
haveAnAppointment = value;
}
notifyListeners();
@@ -766,7 +762,7 @@ class EmergencyServicesViewModel extends ChangeNotifier {
}
Future getTransportationOrders({bool shouldNavigateToTrackingScreen = false, bool showLoader = false}) async {
- if(shouldNavigateToTrackingScreen == false && showLoader ) {
+ if (shouldNavigateToTrackingScreen == false && showLoader) {
LoaderBottomSheet.showLoader(loadingText: "Fetching Orders");
}
int? id = appState.getAuthenticatedUser()?.patientId;
@@ -774,14 +770,16 @@ class EmergencyServicesViewModel extends ChangeNotifier {
notifyListeners();
var response = await emergencyServicesRepo.getTransportationOrders(id: id);
- if(shouldNavigateToTrackingScreen == false && showLoader ) {
- LoaderBottomSheet.hideLoader();}
+ if (shouldNavigateToTrackingScreen == false && showLoader) {
+ LoaderBottomSheet.hideLoader();
+ }
response.fold(
(failure) async {
historyLoading = false;
notifyListeners();
if (shouldNavigateToTrackingScreen) {
- navServices.pushAndRemoveUntil(CustomPageRoute(page: TrackingScreen(state: OrderTrackingState.waitingForCall)), ModalRoute.withName("/EmergencyServicesPage"));
+ navServices.pushAndRemoveUntil(
+ CustomPageRoute(page: TrackingScreen(state: OrderTrackingState.waitingForCall)), ModalRoute.withName("/EmergencyServicesPage"));
}
},
(apiResponse) {
diff --git a/lib/features/hmg_services/hmg_services_repo.dart b/lib/features/hmg_services/hmg_services_repo.dart
new file mode 100644
index 0000000..254d309
--- /dev/null
+++ b/lib/features/hmg_services/hmg_services_repo.dart
@@ -0,0 +1,522 @@
+import 'dart:developer';
+
+import 'package:dartz/dartz.dart';
+import 'package:hmg_patient_app_new/core/api/api_client.dart';
+import 'package:hmg_patient_app_new/core/api_consts.dart';
+import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart';
+import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
+import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/cmc_create_new_order_req_model.dart';
+import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/order_update_req_model.dart';
+import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_all_orders_resp_model.dart';
+import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_services_resp_model.dart';
+import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart';
+import 'package:hmg_patient_app_new/services/logger_service.dart';
+
+abstract class HmgServicesRepo {
+ Future>>> getAllComprehensiveCheckupOrders();
+
+ Future>>> getAllHomeHealthCareCheckupOrders();
+
+ Future>> updateCmcPresOrder(OrderUpdateRequestModel requestModel);
+
+ Future>> updateHhcPresOrder(OrderUpdateRequestModel requestModel);
+
+ Future>>> getAllCmcServices({required int patientID});
+
+ Future>>> getAllHhcServices({required int patientID});
+
+ Future>>> getHospitalsList();
+
+ Future>> addCmcOrder({
+ required int projectID,
+ required int orderServiceID,
+ required List services,
+ });
+
+ Future>> addHhcOrder({
+ required int projectID,
+ required int orderServiceID,
+ required List services,
+ });
+}
+
+class HmgServicesRepoImp implements HmgServicesRepo {
+ final ApiClient apiClient;
+ final LoggerService loggerService;
+
+ HmgServicesRepoImp({required this.apiClient, required this.loggerService});
+
+ @override
+ Future>>> getAllComprehensiveCheckupOrders() async {
+ Map requestBody = {};
+
+ try {
+ GenericApiModel>? apiResponse;
+ Failure? failure;
+
+ await apiClient.post(
+ ApiConsts.allCMCOrdersRc,
+ isRCService: true,
+ body: requestBody,
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ loggerService.logError("CMC Orders API Failed: $error, Status: $statusCode");
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ try {
+ List cmcOrdersList = [];
+ // Log the full response for debugging
+ // Extract MessageStatus and ErrorEndUserMessage from root level
+ final apiErrorMessage = response['ErrorEndUserMessage'] as String?;
+ // Parse the response array
+ if (response['response'] != null && response['response'] is List) {
+ final ordersList = response['response'] as List;
+
+ for (var orderJson in ordersList) {
+ if (orderJson is Map) {
+ try {
+ cmcOrdersList.add(GetCMCAllOrdersResponseModel.fromJson(orderJson));
+ } catch (e) {
+ loggerService.logError("Error parsing individual order: ${e.toString()}");
+ }
+ }
+ }
+ }
+
+ apiResponse = GenericApiModel>(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: apiErrorMessage ?? errorMessage,
+ data: cmcOrdersList,
+ );
+ } catch (e) {
+ loggerService.logError("Error parsing CMC orders: ${e.toString()}");
+ failure = DataParsingFailure(e.toString());
+ }
+ },
+ );
+
+ if (failure != null) return Left(failure!);
+ if (apiResponse == null) return Left(ServerFailure("Unknown error"));
+ return Right(apiResponse!);
+ } catch (e) {
+ loggerService.logError("Unknown error in getAllCmcOrders: ${e.toString()}");
+ return Left(UnknownFailure(e.toString()));
+ }
+ }
+
+ @override
+ Future>>> getAllHomeHealthCareCheckupOrders() async {
+ Map requestBody = {};
+
+ try {
+ GenericApiModel>? apiResponse;
+ Failure? failure;
+
+ await apiClient.post(
+ ApiConsts.allHHCOrdersRc,
+ isRCService: true,
+ body: requestBody,
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ loggerService.logError("HHC Orders API Failed: $error, Status: $statusCode");
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ try {
+ List cmcOrdersList = [];
+ // Log the full response for debugging
+ // Extract MessageStatus and ErrorEndUserMessage from root level
+ final apiErrorMessage = response['ErrorEndUserMessage'] as String?;
+ // Parse the response array
+ if (response['response'] != null && response['response'] is List) {
+ final ordersList = response['response'] as List;
+
+ for (var orderJson in ordersList) {
+ if (orderJson is Map) {
+ try {
+ cmcOrdersList.add(GetCMCAllOrdersResponseModel.fromJson(orderJson));
+ } catch (e) {
+ loggerService.logError("Error parsing individual order: ${e.toString()}");
+ }
+ }
+ }
+ }
+
+ apiResponse = GenericApiModel>(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: apiErrorMessage ?? errorMessage,
+ data: cmcOrdersList,
+ );
+ } catch (e) {
+ loggerService.logError("Error parsing HHC orders: ${e.toString()}");
+ failure = DataParsingFailure(e.toString());
+ }
+ },
+ );
+
+ if (failure != null) return Left(failure!);
+ if (apiResponse == null) return Left(ServerFailure("Unknown error"));
+ return Right(apiResponse!);
+ } catch (e) {
+ loggerService.logError("Unknown error in getAllHHCOrders: ${e.toString()}");
+ return Left(UnknownFailure(e.toString()));
+ }
+ }
+
+ @override
+ Future>>> getAllCmcServices({required int patientID}) async {
+ Map requestBody = {};
+
+ try {
+ GenericApiModel>? apiResponse;
+ Failure? failure;
+
+ await apiClient.post(
+ '${ApiConsts.allCMCServicesRc}?patientID=$patientID',
+ isRCService: true,
+ isAllowAny: true,
+ body: requestBody,
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ loggerService.logError("CMC Services API Failed: $error, Status: $statusCode");
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ try {
+ List cmcServicesList = [];
+
+ if (response['response'] != null && response['response'] is List) {
+ final servicesList = response['response'] as List;
+
+ for (var serviceJson in servicesList) {
+ if (serviceJson is Map) {
+ cmcServicesList.add(GetCMCServicesResponseModel.fromJson(serviceJson));
+ }
+ }
+ }
+
+ apiResponse = GenericApiModel>(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: errorMessage,
+ data: cmcServicesList,
+ );
+ } catch (e) {
+ loggerService.logError("Error parsing CMC services: ${e.toString()}");
+ failure = DataParsingFailure(e.toString());
+ }
+ },
+ );
+
+ if (failure != null) return Left(failure!);
+ if (apiResponse == null) return Left(ServerFailure("Unknown error"));
+ return Right(apiResponse!);
+ } catch (e) {
+ log("Unknown error in getAllCmcServices: ${e.toString()}");
+ return Left(UnknownFailure(e.toString()));
+ }
+ }
+
+ @override
+ Future>>> getAllHhcServices({required int patientID}) async {
+ Map requestBody = {};
+
+ try {
+ GenericApiModel>? apiResponse;
+ Failure? failure;
+
+ await apiClient.post(
+ '${ApiConsts.allHHCServicesRc}?patientID=$patientID',
+ isRCService: true,
+ isAllowAny: true,
+ body: requestBody,
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ loggerService.logError("HHC Services API Failed: $error, Status: $statusCode");
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ try {
+ List hhcServicesList = [];
+
+ if (response['response'] != null && response['response'] is List) {
+ final servicesList = response['response'] as List;
+
+ for (var serviceJson in servicesList) {
+ if (serviceJson is Map) {
+ hhcServicesList.add(GetCMCServicesResponseModel.fromJson(serviceJson));
+ }
+ }
+ }
+
+ apiResponse = GenericApiModel>(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: errorMessage,
+ data: hhcServicesList,
+ );
+ } catch (e) {
+ loggerService.logError("Error parsing HHC services: ${e.toString()}");
+ failure = DataParsingFailure(e.toString());
+ }
+ },
+ );
+
+ if (failure != null) return Left(failure!);
+ if (apiResponse == null) return Left(ServerFailure("Unknown error"));
+ return Right(apiResponse!);
+ } catch (e) {
+ log("Unknown error in getAllHhcServices: ${e.toString()}");
+ return Left(UnknownFailure(e.toString()));
+ }
+ }
+
+ @override
+ Future>> updateCmcPresOrder(OrderUpdateRequestModel requestModel) async {
+ try {
+ GenericApiModel? apiResponse;
+ Failure? failure;
+
+ await apiClient.post(
+ ApiConsts.updateCMCOrder,
+ isRCService: true,
+ body: requestModel.toJson(),
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ loggerService.logError("Update CMC Order API Failed: $error, Status: $statusCode");
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ try {
+ apiResponse = GenericApiModel(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: errorMessage,
+ data: true,
+ );
+
+ loggerService.logInfo("CMC Order updated successfully: PresOrderID=${requestModel.presOrderID}");
+ } catch (e) {
+ loggerService.logError("Error processing update CMC order response: ${e.toString()}");
+ failure = DataParsingFailure(e.toString());
+ }
+ },
+ );
+
+ if (failure != null) return Left(failure!);
+ if (apiResponse == null) return Left(ServerFailure("Unknown error"));
+ return Right(apiResponse!);
+ } catch (e) {
+ loggerService.logError("Unknown error in updateCmcPresOrder: ${e.toString()}");
+ return Left(UnknownFailure(e.toString()));
+ }
+ }
+
+ @override
+ Future>> updateHhcPresOrder(OrderUpdateRequestModel requestModel) async {
+ try {
+ GenericApiModel? apiResponse;
+ Failure? failure;
+
+ await apiClient.post(
+ ApiConsts.updateHHCOrder,
+ isRCService: true,
+ body: requestModel.toJson(),
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ loggerService.logError("Update HHC Order API Failed: $error, Status: $statusCode");
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ try {
+ apiResponse = GenericApiModel(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: errorMessage,
+ data: true,
+ );
+
+ loggerService.logInfo("HHC Order updated successfully: PresOrderID=${requestModel.presOrderID}");
+ } catch (e) {
+ loggerService.logError("Error processing update HHC order response: ${e.toString()}");
+ failure = DataParsingFailure(e.toString());
+ }
+ },
+ );
+
+ if (failure != null) return Left(failure!);
+ if (apiResponse == null) return Left(ServerFailure("Unknown error"));
+ return Right(apiResponse!);
+ } catch (e) {
+ loggerService.logError("Unknown error in updateHhcPresOrder: ${e.toString()}");
+ return Left(UnknownFailure(e.toString()));
+ }
+ }
+
+ @override
+ Future>>> getHospitalsList() async {
+ Map requestBody = {};
+
+ try {
+ GenericApiModel>? apiResponse;
+ Failure? failure;
+
+ await apiClient.post(
+ ApiConsts.getHospitalsList,
+ isRCService: false, // This uses the base HIS API URL, not RC
+ body: requestBody,
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ loggerService.logError("Get Hospitals List API Failed: $error, Status: $statusCode");
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ try {
+ List hospitalsList = [];
+
+ loggerService.logInfo("Hospitals List Raw Response: $response");
+
+ if (response['ListProject'] != null && response['ListProject'] is List) {
+ final projectsList = response['ListProject'] as List;
+
+ for (var projectJson in projectsList) {
+ try {
+ if (projectJson is Map) {
+ hospitalsList.add(HospitalsModel.fromJson(projectJson));
+ }
+ } catch (e) {
+ loggerService.logError('Error parsing hospital item: ${e.toString()}');
+ }
+ }
+ } else {
+ loggerService.logInfo('Hospitals list response array is empty or missing');
+ }
+
+ apiResponse = GenericApiModel>(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: errorMessage,
+ data: hospitalsList,
+ );
+
+ loggerService.logInfo("Hospitals fetched successfully: ${hospitalsList.length} hospitals");
+ } catch (e) {
+ loggerService.logError("Error parsing hospitals list: ${e.toString()}");
+ failure = DataParsingFailure(e.toString());
+ }
+ },
+ );
+
+ if (failure != null) return Left(failure!);
+ if (apiResponse == null) return Left(ServerFailure("Unknown error"));
+ return Right(apiResponse!);
+ } catch (e) {
+ loggerService.logError("Unknown error in getHospitalsList: ${e.toString()}");
+ return Left(UnknownFailure(e.toString()));
+ }
+ }
+
+ @override
+ Future>> addCmcOrder({
+ required int projectID,
+ required int orderServiceID,
+ required List services,
+ }) async {
+ try {
+ GenericApiModel? apiResponse;
+ Failure? failure;
+
+ final requestBody = {
+ 'ProjectID': projectID,
+ 'OrderServiceID': orderServiceID,
+ 'procedures': services.map((service) => service.toJson()).toList(),
+ };
+
+ await apiClient.post(
+ ApiConsts.addCMCOrder,
+ isRCService: true,
+ body: requestBody,
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ loggerService.logError("Add CMC Order API Failed: $error, Status: $statusCode");
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ int requestId = 0;
+ if (response is Map) {
+ requestId = response['response'];
+ }
+ try {
+ apiResponse = GenericApiModel(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: errorMessage,
+ data: requestId,
+ );
+
+ loggerService.logInfo("CMC Order added successfully: ProjectID=$projectID, OrderServiceID=$orderServiceID");
+ } catch (e) {
+ loggerService.logError("Error processing add CMC order response: ${e.toString()}");
+ failure = DataParsingFailure(e.toString());
+ }
+ },
+ );
+
+ if (failure != null) return Left(failure!);
+ if (apiResponse == null) return Left(ServerFailure("Unknown error"));
+ return Right(apiResponse!);
+ } catch (e) {
+ loggerService.logError("Unknown error in addCmcOrder: ${e.toString()}");
+ return Left(UnknownFailure(e.toString()));
+ }
+ }
+
+ @override
+ Future>> addHhcOrder({
+ required int projectID,
+ required int orderServiceID,
+ required List services,
+ }) async {
+ try {
+ GenericApiModel? apiResponse;
+ Failure? failure;
+
+ final requestBody = {
+ 'ProjectID': projectID,
+ 'OrderServiceID': orderServiceID,
+ 'procedures': services.map((service) => service.toJson()).toList(),
+ };
+
+ await apiClient.post(
+ ApiConsts.addHHCOrder,
+ isRCService: true,
+ body: requestBody,
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ loggerService.logError("Add HHC Order API Failed: $error, Status: $statusCode");
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ int requestId = 0;
+ if (response is Map) {
+ requestId = response['response'];
+ }
+ try {
+ apiResponse = GenericApiModel(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: errorMessage,
+ data: requestId,
+ );
+
+ loggerService.logInfo("HHC Order added successfully: ProjectID=$projectID, OrderServiceID=$orderServiceID");
+ } catch (e) {
+ loggerService.logError("Error processing add HHC order response: ${e.toString()}");
+ failure = DataParsingFailure(e.toString());
+ }
+ },
+ );
+
+ if (failure != null) return Left(failure!);
+ if (apiResponse == null) return Left(ServerFailure("Unknown error"));
+ return Right(apiResponse!);
+ } catch (e) {
+ loggerService.logError("Unknown error in addHhcOrder: ${e.toString()}");
+ return Left(UnknownFailure(e.toString()));
+ }
+ }
+}
diff --git a/lib/features/hmg_services/hmg_services_view_model.dart b/lib/features/hmg_services/hmg_services_view_model.dart
new file mode 100644
index 0000000..24b7fff
--- /dev/null
+++ b/lib/features/hmg_services/hmg_services_view_model.dart
@@ -0,0 +1,515 @@
+import 'package:flutter/material.dart';
+import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_repo.dart';
+import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_repo.dart';
+import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/cmc_create_new_order_req_model.dart';
+import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/order_update_req_model.dart';
+import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_all_orders_resp_model.dart';
+import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_services_resp_model.dart';
+import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart';
+import 'package:hmg_patient_app_new/services/error_handler_service.dart';
+
+class HmgServicesViewModel extends ChangeNotifier {
+ final HmgServicesRepo hmgServicesRepo;
+ final BookAppointmentsRepo bookAppointmentsRepo;
+ final ErrorHandlerService errorHandlerService;
+
+ HmgServicesViewModel({required this.bookAppointmentsRepo, required this.hmgServicesRepo, required this.errorHandlerService});
+
+ bool isCmcOrdersLoading = false;
+ bool isCmcServicesLoading = false;
+ bool isUpdatingOrder = false;
+ bool isHospitalListLoading = false;
+
+ // HHC specific loading states
+ bool isHhcOrdersLoading = false;
+ bool isHhcServicesLoading = false;
+
+ List cmcOrdersList = [];
+ List cmcServicesList = [];
+ List hospitalsList = [];
+ List filteredHospitalsList = [];
+ HospitalsModel? selectedHospital;
+
+ // HHC specific lists
+ List hhcOrdersList = [];
+ List hhcServicesList = [];
+
+ // CMC order creation state
+ HospitalsModel? selectedHospitalForOrder;
+ GetCMCServicesResponseModel? selectedServiceForOrder;
+
+ // HHC order creation state (no hospital selection needed for home healthcare)
+ GetCMCServicesResponseModel? selectedServiceForHhcOrder;
+
+ // HHC multiple services selection
+ List selectedHhcServices = [];
+
+ Future getCmcOrdersList() async {
+ cmcOrdersList.clear();
+ isCmcOrdersLoading = true;
+ notifyListeners();
+ await getAllCmcOrders();
+ }
+
+ // Helper to sort hospitals by distance (ascending). Safely converts distanceinkMS to double.
+ void _sortHospitalsByDistance(List list) {
+ double toDouble(dynamic v) {
+ if (v == null) return double.infinity;
+ if (v is num) return v.toDouble();
+ if (v is String) return double.tryParse(v) ?? double.infinity;
+ return double.infinity;
+ }
+
+ list.sort((a, b) {
+ final da = toDouble(a.distanceInKilometers);
+ final db = toDouble(b.distanceInKilometers);
+ return da.compareTo(db);
+ });
+ }
+
+ Future getAllCmcOrders({
+ Function(dynamic)? onSuccess,
+ Function(String)? onError,
+ }) async {
+ isCmcOrdersLoading = true;
+ notifyListeners();
+
+ final result = await hmgServicesRepo.getAllComprehensiveCheckupOrders();
+
+ result.fold(
+ (failure) async {
+ isCmcOrdersLoading = false;
+ notifyListeners();
+ await errorHandlerService.handleError(failure: failure);
+ if (onError != null) {
+ onError(failure.toString());
+ }
+ },
+ (apiResponse) {
+ isCmcOrdersLoading = false;
+ if (apiResponse.messageStatus == 1) {
+ cmcOrdersList = apiResponse.data ?? [];
+ notifyListeners();
+ if (onSuccess != null) {
+ onSuccess(apiResponse);
+ }
+ } else {
+ notifyListeners();
+ if (onError != null) {
+ onError(apiResponse.errorMessage ?? 'Unknown error');
+ }
+ }
+ },
+ );
+ }
+
+ Future getAllCmcServices({required int patientID, Function(dynamic)? onSuccess, Function(String)? onError}) async {
+ isCmcServicesLoading = true;
+ notifyListeners();
+
+ final result = await hmgServicesRepo.getAllCmcServices(patientID: patientID);
+
+ result.fold(
+ (failure) async {
+ isCmcServicesLoading = false;
+ notifyListeners();
+ await errorHandlerService.handleError(failure: failure);
+ if (onError != null) {
+ onError(failure.toString());
+ }
+ },
+ (apiResponse) {
+ isCmcServicesLoading = false;
+ if (apiResponse.messageStatus == 1) {
+ cmcServicesList = apiResponse.data ?? [];
+ notifyListeners();
+ if (onSuccess != null) {
+ onSuccess(apiResponse);
+ }
+ } else {
+ notifyListeners();
+ if (onError != null) {
+ onError(apiResponse.errorMessage ?? 'Unknown error');
+ }
+ }
+ },
+ );
+ }
+
+ Future updateCmcPresOrder({
+ required OrderUpdateRequestModel requestModel,
+ Function(dynamic)? onSuccess,
+ Function(String)? onError,
+ }) async {
+ isUpdatingOrder = true;
+ notifyListeners();
+
+ final result = await hmgServicesRepo.updateCmcPresOrder(requestModel);
+
+ bool success = false;
+
+ result.fold(
+ (failure) async {
+ isUpdatingOrder = false;
+ notifyListeners();
+ await errorHandlerService.handleError(failure: failure);
+ if (onError != null) {
+ onError(failure.toString());
+ }
+ },
+ (apiResponse) {
+ isUpdatingOrder = false;
+ if (apiResponse.messageStatus == 1) {
+ success = true;
+ notifyListeners();
+ if (onSuccess != null) {
+ onSuccess(apiResponse);
+ }
+ } else {
+ notifyListeners();
+ if (onError != null) {
+ onError(apiResponse.errorMessage ?? 'Unknown error');
+ }
+ }
+ },
+ );
+
+ return success;
+ }
+
+ Future getHospitalsList({Function(dynamic)? onSuccess, Function(String)? onError}) async {
+ isHospitalListLoading = true;
+ notifyListeners();
+
+ final result = await hmgServicesRepo.getHospitalsList();
+
+ result.fold(
+ (failure) async {
+ isHospitalListLoading = false;
+ notifyListeners();
+ await errorHandlerService.handleError(failure: failure);
+ if (onError != null) {
+ onError(failure.toString());
+ }
+ },
+ (apiResponse) {
+ isHospitalListLoading = false;
+ if (apiResponse.messageStatus == 1) {
+ hospitalsList = apiResponse.data ?? [];
+ filteredHospitalsList = List.from(hospitalsList);
+ // ensure hospitals are sorted by distance before showing
+ _sortHospitalsByDistance(filteredHospitalsList);
+ notifyListeners();
+ if (onSuccess != null) {
+ onSuccess(apiResponse);
+ }
+ } else {
+ notifyListeners();
+ if (onError != null) {
+ onError(apiResponse.errorMessage ?? 'Unknown error');
+ }
+ }
+ },
+ );
+ }
+
+ void filterHospitalsByString(String searchText, bool isArabic) {
+ if (searchText.isEmpty) {
+ filteredHospitalsList = List.from(hospitalsList);
+ _sortHospitalsByDistance(filteredHospitalsList);
+ } else {
+ filteredHospitalsList = hospitalsList.where((HospitalsModel hospital) {
+ final name = isArabic ? (hospital.nameN ?? '') : (hospital.name ?? '');
+ return name.toLowerCase().contains(searchText.toLowerCase());
+ }).toList();
+ _sortHospitalsByDistance(filteredHospitalsList);
+ }
+ notifyListeners();
+ }
+
+ void setSelectedHospital(HospitalsModel? hospital) {
+ selectedHospital = hospital;
+ notifyListeners();
+ }
+
+ void clearHospitalSelection() {
+ selectedHospital = null;
+ filteredHospitalsList = List.from(hospitalsList);
+ _sortHospitalsByDistance(filteredHospitalsList);
+ notifyListeners();
+ }
+
+ // CMC Order management methods
+ void setSelectedHospitalForOrder(HospitalsModel? hospital) {
+ selectedHospitalForOrder = hospital;
+ notifyListeners();
+ }
+
+ void setSelectedServiceForOrder(GetCMCServicesResponseModel? service) {
+ selectedServiceForOrder = service;
+ notifyListeners();
+ }
+
+ void clearOrderSelection() {
+ selectedHospitalForOrder = null;
+ selectedServiceForOrder = null;
+ notifyListeners();
+ }
+
+ bool get isOrderReadyToConfirm => selectedHospitalForOrder != null && selectedServiceForOrder != null;
+
+ Future addCmcOrder({
+ required int projectID,
+ required int orderServiceID,
+ required List services,
+ Function(int)? onSuccess,
+ Function(String)? onError,
+ }) async {
+ isUpdatingOrder = true;
+ notifyListeners();
+
+ final result = await hmgServicesRepo.addCmcOrder(
+ projectID: projectID,
+ orderServiceID: orderServiceID,
+ services: services,
+ );
+
+ int requestId = 0;
+
+ result.fold(
+ (failure) async {
+ isUpdatingOrder = false;
+ notifyListeners();
+ await errorHandlerService.handleError(failure: failure);
+ if (onError != null) {
+ onError(failure.toString());
+ }
+ },
+ (apiResponse) {
+ isUpdatingOrder = false;
+ if (apiResponse.messageStatus == 1) {
+ requestId = apiResponse.data ?? 0;
+ notifyListeners();
+ if (onSuccess != null) {
+ onSuccess(requestId);
+ }
+ } else {
+ notifyListeners();
+ if (onError != null) {
+ onError(apiResponse.errorMessage ?? 'Unknown error');
+ }
+ }
+ },
+ );
+
+ return requestId;
+ }
+
+// ******************* HOME HEALTHCARE APIs ********************
+
+ Future getHhcOrdersList() async {
+ hhcOrdersList.clear();
+ isHhcOrdersLoading = true;
+ notifyListeners();
+ await getAllHhcOrders();
+ }
+
+ Future getAllHhcOrders({
+ Function(dynamic)? onSuccess,
+ Function(String)? onError,
+ }) async {
+ isHhcOrdersLoading = true;
+ notifyListeners();
+
+ final result = await hmgServicesRepo.getAllHomeHealthCareCheckupOrders();
+
+ result.fold(
+ (failure) async {
+ isHhcOrdersLoading = false;
+ notifyListeners();
+ await errorHandlerService.handleError(failure: failure);
+ if (onError != null) {
+ onError(failure.toString());
+ }
+ },
+ (apiResponse) {
+ isHhcOrdersLoading = false;
+ if (apiResponse.messageStatus == 1) {
+ hhcOrdersList = apiResponse.data ?? [];
+ notifyListeners();
+ if (onSuccess != null) {
+ onSuccess(apiResponse);
+ }
+ } else {
+ notifyListeners();
+ if (onError != null) {
+ onError(apiResponse.errorMessage ?? 'Unknown error');
+ }
+ }
+ },
+ );
+ }
+
+ Future getAllHhcServices({required int patientID, Function(dynamic)? onSuccess, Function(String)? onError}) async {
+ isHhcServicesLoading = true;
+ notifyListeners();
+
+ final result = await hmgServicesRepo.getAllHhcServices(patientID: patientID);
+
+ result.fold(
+ (failure) async {
+ isHhcServicesLoading = false;
+ notifyListeners();
+ await errorHandlerService.handleError(failure: failure);
+ if (onError != null) {
+ onError(failure.toString());
+ }
+ },
+ (apiResponse) {
+ isHhcServicesLoading = false;
+ if (apiResponse.messageStatus == 1) {
+ hhcServicesList = apiResponse.data ?? [];
+ notifyListeners();
+ if (onSuccess != null) {
+ onSuccess(apiResponse);
+ }
+ } else {
+ notifyListeners();
+ if (onError != null) {
+ onError(apiResponse.errorMessage ?? 'Unknown error');
+ }
+ }
+ },
+ );
+ }
+
+ Future updateHhcPresOrder({
+ required OrderUpdateRequestModel requestModel,
+ Function(dynamic)? onSuccess,
+ Function(String)? onError,
+ }) async {
+ isUpdatingOrder = true;
+ notifyListeners();
+
+ final result = await hmgServicesRepo.updateHhcPresOrder(requestModel);
+
+ bool success = false;
+
+ result.fold(
+ (failure) async {
+ isUpdatingOrder = false;
+ notifyListeners();
+ await errorHandlerService.handleError(failure: failure);
+ if (onError != null) {
+ onError(failure.toString());
+ }
+ },
+ (apiResponse) {
+ isUpdatingOrder = false;
+ if (apiResponse.messageStatus == 1) {
+ success = true;
+ notifyListeners();
+ if (onSuccess != null) {
+ onSuccess(apiResponse);
+ }
+ } else {
+ notifyListeners();
+ if (onError != null) {
+ onError(apiResponse.errorMessage ?? 'Unknown error');
+ }
+ }
+ },
+ );
+
+ return success;
+ }
+
+ Future addHhcOrder({
+ required int projectID,
+ required int orderServiceID,
+ required List services,
+ Function(int)? onSuccess,
+ Function(String)? onError,
+ }) async {
+ isUpdatingOrder = true;
+ notifyListeners();
+
+ final result = await hmgServicesRepo.addHhcOrder(
+ projectID: projectID,
+ orderServiceID: orderServiceID,
+ services: services,
+ );
+
+ int requestId = 0;
+
+ result.fold(
+ (failure) async {
+ isUpdatingOrder = false;
+ notifyListeners();
+ await errorHandlerService.handleError(failure: failure);
+ if (onError != null) {
+ onError(failure.toString());
+ }
+ },
+ (apiResponse) {
+ isUpdatingOrder = false;
+ if (apiResponse.messageStatus == 1) {
+ requestId = apiResponse.data ?? 0;
+ notifyListeners();
+ if (onSuccess != null) {
+ onSuccess(requestId);
+ }
+ } else {
+ notifyListeners();
+ if (onError != null) {
+ onError(apiResponse.errorMessage ?? 'Unknown error');
+ }
+ }
+ },
+ );
+
+ return requestId;
+ }
+
+ // HHC Order management methods (no hospital selection for home healthcare)
+ void setSelectedServiceForHhcOrder(GetCMCServicesResponseModel? service) {
+ selectedServiceForHhcOrder = service;
+ notifyListeners();
+ }
+
+ void clearHhcOrderSelection() {
+ selectedServiceForHhcOrder = null;
+ selectedHhcServices.clear();
+ notifyListeners();
+ }
+
+ bool get isHhcOrderReadyToConfirm => selectedServiceForHhcOrder != null;
+
+ // Multiple HHC services selection methods
+ void toggleHhcServiceSelection(GetCMCServicesResponseModel service) {
+ final index = selectedHhcServices.indexWhere((s) => s.iD == service.iD);
+ if (index != -1) {
+ selectedHhcServices.removeAt(index);
+ } else {
+ selectedHhcServices.add(service);
+ }
+ notifyListeners();
+ }
+
+ bool isHhcServiceSelected(GetCMCServicesResponseModel service) {
+ return selectedHhcServices.any((s) => s.iD == service.iD);
+ }
+
+ double getHhcSelectedServicesTotal() {
+ double total = 0.0;
+ for (var service in selectedHhcServices) {
+ total += (service.priceTotal ?? 0);
+ }
+ return total;
+ }
+
+ void clearHhcServicesSelection() {
+ selectedHhcServices.clear();
+ notifyListeners();
+ }
+}
diff --git a/lib/features/hmg_services/models/req_models/cmc_create_new_order_req_model.dart b/lib/features/hmg_services/models/req_models/cmc_create_new_order_req_model.dart
new file mode 100644
index 0000000..a1fcc4b
--- /dev/null
+++ b/lib/features/hmg_services/models/req_models/cmc_create_new_order_req_model.dart
@@ -0,0 +1,131 @@
+class CMCInsertPresOrderRequestModel {
+ double? versionID;
+ int? channel;
+ int? languageID;
+ String? iPAdress;
+ String? generalid;
+ int? patientOutSA;
+ String? sessionID;
+ bool? isDentalAllowedBackend;
+ int? deviceTypeID;
+ int? patientID;
+ String? tokenID;
+ int? patientTypeID;
+ int? patientType;
+ double? latitude;
+ double? longitude;
+ int? createdBy;
+ int? orderServiceID;
+ int? projectID;
+ List? patientERCMCInsertServicesList;
+
+ CMCInsertPresOrderRequestModel(
+ {this.versionID,
+ this.channel,
+ this.languageID,
+ this.iPAdress,
+ this.generalid,
+ this.patientOutSA,
+ this.sessionID,
+ this.isDentalAllowedBackend,
+ this.deviceTypeID,
+ this.patientID,
+ this.tokenID,
+ this.patientTypeID,
+ this.patientType,
+ this.latitude,
+ this.longitude,
+ this.createdBy,
+ this.orderServiceID,
+ this.projectID,
+ this.patientERCMCInsertServicesList});
+
+ CMCInsertPresOrderRequestModel.fromJson(Map json) {
+ versionID = json['VersionID'];
+ channel = json['Channel'];
+ languageID = json['LanguageID'];
+ iPAdress = json['IPAdress'];
+ generalid = json['generalid'];
+ patientOutSA = json['PatientOutSA'];
+ sessionID = json['SessionID'];
+ isDentalAllowedBackend = json['isDentalAllowedBackend'];
+ deviceTypeID = json['DeviceTypeID'];
+ patientID = json['PatientID'];
+ tokenID = json['TokenID'];
+ patientTypeID = json['PatientTypeID'];
+ patientType = json['PatientType'];
+ latitude = json['Latitude'];
+ longitude = json['Longitude'];
+ createdBy = json['CreatedBy'];
+ orderServiceID = json['OrderServiceID'];
+ projectID = json['ProjectId'];
+ if (json['PatientER_CMC_InsertServicesList'] != null) {
+ patientERCMCInsertServicesList = [];
+ json['PatientER_CMC_InsertServicesList'].forEach((v) {
+ patientERCMCInsertServicesList!.add(
+ new PatientERCMCInsertServicesList.fromJson(v),
+ );
+ });
+ }
+ }
+
+ Map toJson() {
+ final Map data = new Map();
+ data['VersionID'] = this.versionID;
+ data['Channel'] = this.channel;
+ data['LanguageID'] = this.languageID;
+ data['IPAdress'] = this.iPAdress;
+ data['generalid'] = this.generalid;
+ data['isOutPatient'] = this.patientOutSA == 0 ? false : true;
+ data['SessionID'] = this.sessionID;
+ data['isDentalAllowedBackend'] = this.isDentalAllowedBackend;
+ data['DeviceTypeID'] = this.deviceTypeID;
+ data['TokenID'] = this.tokenID;
+ data['PatientTypeID'] = this.patientTypeID;
+ data['PatientType'] = this.patientType;
+ data['latitude'] = this.latitude;
+ data['longitude'] = this.longitude;
+ // data['CreatedBy'] = this.createdBy;
+ data['OrderServiceID'] = this.orderServiceID;
+ data['ProjectID'] = this.projectID;
+ if (this.patientERCMCInsertServicesList != null) {
+ data['procedures'] = this.patientERCMCInsertServicesList!.map((v) => v.toJson()).toList();
+ }
+ return data;
+ }
+}
+
+class PatientERCMCInsertServicesList {
+ int? recordID;
+ String? serviceID;
+ String? selectedServiceName;
+ String? selectedServiceNameAR;
+ dynamic price;
+ dynamic vAT;
+ dynamic totalPrice;
+
+ PatientERCMCInsertServicesList(
+ {this.recordID, this.serviceID, this.selectedServiceName, this.selectedServiceNameAR, this.price, this.vAT, this.totalPrice});
+
+ PatientERCMCInsertServicesList.fromJson(Map json) {
+ recordID = json['RecordID'];
+ serviceID = json['ServiceID'];
+ selectedServiceName = json['selectedServiceName'];
+ selectedServiceNameAR = json['selectedServiceNameAR'];
+ price = json['Price'];
+ vAT = json['VAT'];
+ totalPrice = json['TotalPrice'];
+ }
+
+ Map toJson() {
+ final Map data = new Map();
+ data['RecordID'] = this.recordID;
+ data['ServiceID'] = this.serviceID;
+ data['selectedServiceName'] = this.selectedServiceName;
+ data['selectedServiceNameAR'] = this.selectedServiceNameAR;
+ data['Price'] = this.price;
+ data['VAT'] = this.vAT;
+ data['TotalPrice'] = this.totalPrice;
+ return data;
+ }
+}
diff --git a/lib/features/hmg_services/models/req_models/cmc_create_service_order_req_model.dart b/lib/features/hmg_services/models/req_models/cmc_create_service_order_req_model.dart
new file mode 100644
index 0000000..af2808d
--- /dev/null
+++ b/lib/features/hmg_services/models/req_models/cmc_create_service_order_req_model.dart
@@ -0,0 +1,41 @@
+class CmcCreateServiceOrderReqModel {
+ int? recordID;
+ String? serviceID;
+ String? selectedServiceName;
+ String? selectedServiceNameAR;
+ dynamic price;
+ dynamic vAT;
+ dynamic totalPrice;
+
+ CmcCreateServiceOrderReqModel({
+ this.recordID,
+ this.serviceID,
+ this.selectedServiceName,
+ this.selectedServiceNameAR,
+ this.price,
+ this.vAT,
+ this.totalPrice,
+ });
+
+ CmcCreateServiceOrderReqModel.fromJson(Map json) {
+ recordID = json['RecordID'];
+ serviceID = json['ServiceID'];
+ selectedServiceName = json['selectedServiceName'];
+ selectedServiceNameAR = json['selectedServiceNameAR'];
+ price = json['Price'];
+ vAT = json['VAT'];
+ totalPrice = json['TotalPrice'];
+ }
+
+ Map toJson() {
+ final Map data = {};
+ data['RecordID'] = recordID;
+ data['ServiceID'] = serviceID;
+ data['selectedServiceName'] = selectedServiceName;
+ data['selectedServiceNameAR'] = selectedServiceNameAR;
+ data['Price'] = price;
+ data['VAT'] = vAT;
+ data['TotalPrice'] = totalPrice;
+ return data;
+ }
+}
diff --git a/lib/features/hmg_services/models/req_models/order_update_req_model.dart b/lib/features/hmg_services/models/req_models/order_update_req_model.dart
new file mode 100644
index 0000000..0f9964f
--- /dev/null
+++ b/lib/features/hmg_services/models/req_models/order_update_req_model.dart
@@ -0,0 +1,82 @@
+class OrderUpdateRequestModel {
+ double? versionID;
+ int? channel;
+ int? languageID;
+ String? iPAdress;
+ String? generalid;
+ int? patientOutSA;
+ String? sessionID;
+ bool? isDentalAllowedBackend;
+ int? deviceTypeID;
+ int? patientID;
+ String? tokenID;
+ int? patientTypeID;
+ int? patientType;
+ int? presOrderID;
+ int? presOrderStatus;
+ int? editedBy;
+ String? rejectionReason;
+
+ OrderUpdateRequestModel({
+ this.versionID,
+ this.channel,
+ this.languageID,
+ this.iPAdress,
+ this.generalid,
+ this.patientOutSA,
+ this.sessionID,
+ this.isDentalAllowedBackend,
+ this.deviceTypeID,
+ this.patientID,
+ this.tokenID,
+ this.patientTypeID,
+ this.patientType,
+ this.presOrderID,
+ this.presOrderStatus,
+ this.editedBy,
+ this.rejectionReason,
+ });
+
+ OrderUpdateRequestModel.fromJson(Map json) {
+ versionID = json['VersionID'];
+ channel = json['Channel'];
+ languageID = json['LanguageID'];
+ iPAdress = json['IPAdress'];
+ generalid = json['generalid'];
+ patientOutSA = json['PatientOutSA'];
+ sessionID = json['SessionID'];
+ isDentalAllowedBackend = json['isDentalAllowedBackend'];
+ deviceTypeID = json['DeviceTypeID'];
+ patientID = json['PatientID'];
+ tokenID = json['TokenID'];
+ patientTypeID = json['PatientTypeID'];
+ patientType = json['PatientType'];
+ presOrderID = json['PresOrderID'];
+ presOrderStatus = json['PresOrderStatus'];
+ editedBy = json['EditedBy'];
+ rejectionReason = json['RejectionReason'];
+ }
+
+ Map toJson() {
+ final Map data = {};
+ data['VersionID'] = versionID;
+ data['Channel'] = channel;
+ data['LanguageID'] = languageID;
+ data['IPAdress'] = iPAdress;
+ data['generalid'] = generalid;
+ data['PatientOutSA'] = patientOutSA;
+ data['SessionID'] = sessionID;
+ data['isDentalAllowedBackend'] = isDentalAllowedBackend;
+ data['DeviceTypeID'] = deviceTypeID;
+ data['PatientID'] = patientID;
+ data['TokenID'] = tokenID;
+ data['PatientTypeID'] = patientTypeID;
+ data['PatientType'] = patientType;
+ data['Id'] = presOrderID;
+ data['ClickButton'] = 14;
+ data['PresOrderStatus'] = presOrderStatus;
+ data['EditedBy'] = editedBy;
+ data['RejectionReason'] = rejectionReason;
+ return data;
+ }
+}
diff --git a/lib/features/hmg_services/models/resq_models/get_cmc_all_orders_resp_model.dart b/lib/features/hmg_services/models/resq_models/get_cmc_all_orders_resp_model.dart
new file mode 100644
index 0000000..ddd91f4
--- /dev/null
+++ b/lib/features/hmg_services/models/resq_models/get_cmc_all_orders_resp_model.dart
@@ -0,0 +1,344 @@
+import 'dart:developer';
+
+class GetCMCAllOrdersResponseModel {
+ int? iD;
+ int? patientId;
+ int? patientOutSa;
+ bool? isOutPatient;
+ int? projectId;
+ int? nearestProjectId;
+ dynamic longitude;
+ dynamic latitude;
+ dynamic appointmentNo;
+ dynamic dischargeId;
+ int? statusId;
+ int? serviceId;
+ int? channel;
+ Orderpayment? orderpayment;
+ dynamic orderselectedservice;
+ dynamic wforder;
+ dynamic orderapprovalobj;
+ String? created;
+ dynamic createdBy;
+ dynamic modified;
+ dynamic modifiedBy;
+ bool? isDeleted;
+ String? statusText;
+ int? paymentStatus;
+ dynamic clientRequestid;
+ dynamic paymentStatusText;
+ String? projectName;
+ String? nearestProjectName;
+ dynamic paymentAmount;
+ WFOrder? wFOrder;
+ String? serviceText;
+ bool? isSentForApproval;
+ int? exaCartOrderId;
+ bool? isTimer;
+ int? timeSeconds;
+ int? totalPendingSeconds;
+ int? timeMinute;
+ int? timeHour;
+ int? timeTotalSeconds;
+ int? timeTotalMinute;
+ int? timeTotalHour;
+ dynamic approvalStatus;
+ bool? isActive;
+ int? clickButton;
+ List? procedures;
+ dynamic pickupLocation;
+ dynamic dropOffLocation;
+ dynamic clinicName;
+ dynamic doctorName;
+ dynamic branch;
+ dynamic time;
+ dynamic notes;
+
+ GetCMCAllOrdersResponseModel(
+ {this.iD,
+ this.patientId,
+ this.patientOutSa,
+ this.isOutPatient,
+ this.projectId,
+ this.nearestProjectId,
+ this.longitude,
+ this.latitude,
+ this.appointmentNo,
+ this.dischargeId,
+ this.statusId,
+ this.serviceId,
+ this.channel,
+ this.orderpayment,
+ this.orderselectedservice,
+ this.wforder,
+ this.orderapprovalobj,
+ this.created,
+ this.createdBy,
+ this.modified,
+ this.modifiedBy,
+ this.isDeleted,
+ this.statusText,
+ this.paymentStatus,
+ this.clientRequestid,
+ this.paymentStatusText,
+ this.projectName,
+ this.nearestProjectName,
+ this.paymentAmount,
+ this.wFOrder,
+ this.serviceText,
+ this.isSentForApproval,
+ this.exaCartOrderId,
+ this.isTimer,
+ this.timeSeconds,
+ this.totalPendingSeconds,
+ this.timeMinute,
+ this.timeHour,
+ this.timeTotalSeconds,
+ this.timeTotalMinute,
+ this.timeTotalHour,
+ this.approvalStatus,
+ this.isActive,
+ this.clickButton,
+ this.procedures,
+ this.pickupLocation,
+ this.dropOffLocation,
+ this.clinicName,
+ this.doctorName,
+ this.branch,
+ this.time,
+ this.notes});
+
+ GetCMCAllOrdersResponseModel.fromJson(Map json) {
+ log("responseJson: $json");
+ iD = json['ID'];
+ patientId = json['PatientId'];
+ patientOutSa = json['PatientOutSa'];
+ isOutPatient = json['IsOutPatient'];
+ projectId = json['ProjectId'];
+ nearestProjectId = json['NearestProjectId'];
+ longitude = json['Longitude'];
+ latitude = json['Latitude'];
+ appointmentNo = json['AppointmentNo'];
+ dischargeId = json['DischargeId'];
+ statusId = json['StatusId'];
+ serviceId = json['ServiceId'];
+ channel = json['Channel'];
+ orderpayment = json['orderpayment'] != null ? Orderpayment.fromJson(json['orderpayment']) : null;
+ orderselectedservice = json['orderselectedservice'];
+ wforder = json['wforder'];
+ orderapprovalobj = json['orderapprovalobj'];
+ created = json['Created'];
+ createdBy = json['CreatedBy'];
+ modified = json['Modified'];
+ modifiedBy = json['ModifiedBy'];
+ isDeleted = json['IsDeleted'];
+ statusText = json['StatusText'];
+ paymentStatus = json['PaymentStatus'];
+ clientRequestid = json['ClientRequestid'];
+ paymentStatusText = json['PaymentStatusText'];
+ projectName = json['ProjectName'];
+ nearestProjectName = json['NearestProjectName'];
+ paymentAmount = json['PaymentAmount'];
+ wFOrder = json['WF_order'] != null ? WFOrder.fromJson(json['WF_order']) : null;
+ serviceText = json['ServiceText'];
+ isSentForApproval = json['isSentForApproval'];
+ exaCartOrderId = json['ExaCart_OrderId'];
+ isTimer = json['isTimer'];
+ timeSeconds = json['TimeSeconds'];
+ totalPendingSeconds = json['TotalPendingSeconds'];
+ timeMinute = json['TimeMinute'];
+ timeHour = json['TimeHour'];
+ timeTotalSeconds = json['TimeTotalSeconds'];
+ timeTotalMinute = json['TimeTotalMinute'];
+ timeTotalHour = json['TimeTotalHour'];
+ approvalStatus = json['ApprovalStatus'];
+ isActive = json['isActive'];
+ clickButton = json['ClickButton'];
+ pickupLocation = json['PickupLocation'];
+ dropOffLocation = json['DropOffLocation'];
+ clinicName = json['clinicName'];
+ doctorName = json['DoctorName'];
+ branch = json['Branch'];
+ time = json['Time'];
+ notes = json['Notes'];
+ }
+
+ Map toJson() {
+ final Map data = {};
+ data['ID'] = iD;
+ data['PatientId'] = patientId;
+ data['PatientOutSa'] = patientOutSa;
+ data['IsOutPatient'] = isOutPatient;
+ data['ProjectId'] = projectId;
+ data['NearestProjectId'] = nearestProjectId;
+ data['Longitude'] = longitude;
+ data['Latitude'] = latitude;
+ data['AppointmentNo'] = appointmentNo;
+ data['DischargeId'] = dischargeId;
+ data['StatusId'] = statusId;
+ data['ServiceId'] = serviceId;
+ data['Channel'] = channel;
+ if (orderpayment != null) {
+ data['orderpayment'] = orderpayment!.toJson();
+ }
+ data['orderselectedservice'] = orderselectedservice;
+
+ data['wforder'] = wforder;
+ data['orderapprovalobj'] = orderapprovalobj;
+ data['Created'] = created;
+ data['CreatedBy'] = createdBy;
+ data['Modified'] = modified;
+ data['ModifiedBy'] = modifiedBy;
+ data['IsDeleted'] = isDeleted;
+ data['StatusText'] = statusText;
+ data['PaymentStatus'] = paymentStatus;
+ data['ClientRequestid'] = clientRequestid;
+ data['PaymentStatusText'] = paymentStatusText;
+ data['ProjectName'] = projectName;
+ data['NearestProjectName'] = nearestProjectName;
+ data['PaymentAmount'] = paymentAmount;
+ if (wFOrder != null) {
+ data['WF_order'] = wFOrder!.toJson();
+ }
+ data['ServiceText'] = serviceText;
+ data['isSentForApproval'] = isSentForApproval;
+ data['ExaCart_OrderId'] = exaCartOrderId;
+ data['isTimer'] = isTimer;
+ data['TimeSeconds'] = timeSeconds;
+ data['TotalPendingSeconds'] = totalPendingSeconds;
+ data['TimeMinute'] = timeMinute;
+ data['TimeHour'] = timeHour;
+ data['TimeTotalSeconds'] = timeTotalSeconds;
+ data['TimeTotalMinute'] = timeTotalMinute;
+ data['TimeTotalHour'] = timeTotalHour;
+ data['ApprovalStatus'] = approvalStatus;
+ data['isActive'] = isActive;
+ data['ClickButton'] = clickButton;
+ data['PickupLocation'] = pickupLocation;
+ data['DropOffLocation'] = dropOffLocation;
+ data['clinicName'] = clinicName;
+ data['DoctorName'] = doctorName;
+ data['Branch'] = branch;
+ data['Time'] = time;
+ data['Notes'] = notes;
+ return data;
+ }
+}
+
+class Orderpayment {
+ int? iD;
+ int? orderId;
+ dynamic clientRequestId;
+ dynamic totalAmount;
+ int? paymentStatus;
+ dynamic order;
+ String? created;
+ dynamic createdBy;
+ dynamic modified;
+ dynamic modifiedBy;
+ bool? isDeleted;
+
+ Orderpayment(
+ {this.iD,
+ this.orderId,
+ this.clientRequestId,
+ this.totalAmount,
+ this.paymentStatus,
+ this.order,
+ this.created,
+ this.createdBy,
+ this.modified,
+ this.modifiedBy,
+ this.isDeleted});
+
+ Orderpayment.fromJson(Map json) {
+ iD = json['ID'];
+ orderId = json['OrderId'];
+ clientRequestId = json['ClientRequestId'];
+ totalAmount = json['TotalAmount'];
+ paymentStatus = json['PaymentStatus'];
+ order = json['Order'];
+ created = json['Created'];
+ createdBy = json['CreatedBy'];
+ modified = json['Modified'];
+ modifiedBy = json['ModifiedBy'];
+ isDeleted = json['IsDeleted'];
+ }
+
+ Map toJson() {
+ final Map data = {};
+ data['ID'] = iD;
+ data['OrderId'] = orderId;
+ data['ClientRequestId'] = clientRequestId;
+ data['TotalAmount'] = totalAmount;
+ data['PaymentStatus'] = paymentStatus;
+ data['Order'] = order;
+ data['Created'] = created;
+ data['CreatedBy'] = createdBy;
+ data['Modified'] = modified;
+ data['ModifiedBy'] = modifiedBy;
+ data['IsDeleted'] = isDeleted;
+ return data;
+ }
+}
+
+class WFOrder {
+ dynamic wfButtonsDTO;
+ int? iD;
+ int? orderId;
+ int? previousStep;
+ int? nextStep;
+ int? serviceId;
+ dynamic order;
+ String? created;
+ dynamic createdBy;
+ dynamic modified;
+ dynamic modifiedBy;
+ bool? isDeleted;
+
+ WFOrder(
+ {this.wfButtonsDTO,
+ this.iD,
+ this.orderId,
+ this.previousStep,
+ this.nextStep,
+ this.serviceId,
+ this.order,
+ this.created,
+ this.createdBy,
+ this.modified,
+ this.modifiedBy,
+ this.isDeleted});
+
+ WFOrder.fromJson(Map json) {
+ wfButtonsDTO = json['wf_ButtonsDTO'];
+ iD = json['ID'];
+ orderId = json['OrderId'];
+ previousStep = json['PreviousStep'];
+ nextStep = json['NextStep'];
+ serviceId = json['ServiceId'];
+ order = json['Order'];
+ created = json['Created'];
+ createdBy = json['CreatedBy'];
+ modified = json['Modified'];
+ modifiedBy = json['ModifiedBy'];
+ isDeleted = json['IsDeleted'];
+ }
+
+ Map toJson() {
+ final Map data = {};
+ data['wf_ButtonsDTO'] = wfButtonsDTO;
+ data['ID'] = iD;
+ data['OrderId'] = orderId;
+ data['PreviousStep'] = previousStep;
+ data['NextStep'] = nextStep;
+ data['ServiceId'] = serviceId;
+ data['Order'] = order;
+ data['Created'] = created;
+ data['CreatedBy'] = createdBy;
+ data['Modified'] = modified;
+ data['ModifiedBy'] = modifiedBy;
+ data['IsDeleted'] = isDeleted;
+ return data;
+ }
+}
diff --git a/lib/features/hmg_services/models/resq_models/get_cmc_services_resp_model.dart b/lib/features/hmg_services/models/resq_models/get_cmc_services_resp_model.dart
new file mode 100644
index 0000000..670a582
--- /dev/null
+++ b/lib/features/hmg_services/models/resq_models/get_cmc_services_resp_model.dart
@@ -0,0 +1,57 @@
+class GetCMCServicesResponseModel {
+ int? iD;
+ String? serviceID;
+ int? orderServiceID;
+ String? text;
+ String? textN;
+ dynamic price;
+ dynamic priceVAT;
+ dynamic priceTotal;
+ bool? isEnabled;
+ int? orderId;
+ int? quantity;
+
+ GetCMCServicesResponseModel({
+ this.iD,
+ this.serviceID,
+ this.orderServiceID,
+ this.text,
+ this.textN,
+ this.price,
+ this.priceVAT,
+ this.priceTotal,
+ this.isEnabled,
+ this.orderId,
+ this.quantity,
+ });
+
+ GetCMCServicesResponseModel.fromJson(Map json) {
+ iD = json['ID'];
+ serviceID = json['ServiceID'];
+ orderServiceID = json['OrderServiceID'];
+ text = json['Text'];
+ textN = json['TextN'];
+ price = json['Price'];
+ priceVAT = json['PriceVAT'];
+ priceTotal = json['PriceTotal'];
+ isEnabled = json['IsEnabled'];
+ orderId = json['OrderId'];
+ quantity = json['Quantity'];
+ }
+
+ Map toJson() {
+ final Map data = {};
+ data['ID'] = this.iD;
+ data['ServiceID'] = this.serviceID;
+ data['OrderServiceID'] = this.orderServiceID;
+ data['Text'] = this.text;
+ data['TextN'] = this.textN;
+ data['Price'] = this.price;
+ data['PriceVAT'] = this.priceVAT;
+ data['PriceTotal'] = this.priceTotal;
+ data['IsEnabled'] = this.isEnabled;
+ data['OrderId'] = this.orderId;
+ data['Quantity'] = this.quantity;
+ return data;
+ }
+}
diff --git a/lib/features/hmg_services/models/ui_models/hmg_services_component_model.dart b/lib/features/hmg_services/models/ui_models/hmg_services_component_model.dart
new file mode 100644
index 0000000..d5180ae
--- /dev/null
+++ b/lib/features/hmg_services/models/ui_models/hmg_services_component_model.dart
@@ -0,0 +1,25 @@
+import 'package:flutter/material.dart';
+
+class HmgServicesComponentModel {
+ int action;
+ String title;
+ String subTitle;
+ String icon;
+ bool isLogin;
+ bool isLocked;
+ Color bgColor;
+ Color textColor;
+ String route;
+
+ HmgServicesComponentModel(
+ this.action,
+ this.title,
+ this.subTitle,
+ this.icon,
+ this.isLogin, {
+ this.isLocked = false,
+ this.bgColor = Colors.white,
+ this.textColor = Colors.black,
+ this.route = '',
+ });
+}
diff --git a/lib/features/my_appointments/models/resp_models/hospital_model.dart b/lib/features/my_appointments/models/resp_models/hospital_model.dart
index 9a211d0..a807b99 100644
--- a/lib/features/my_appointments/models/resp_models/hospital_model.dart
+++ b/lib/features/my_appointments/models/resp_models/hospital_model.dart
@@ -62,9 +62,9 @@ class HospitalsModel {
mainProjectID = json['MainProjectID'];
projectOutSA = json['ProjectOutSA'];
usingInDoctorApp = json['UsingInDoctorApp'];
- this.isHMC = json["IsHMC"];
- this.regionArabic = json['RegionNameN'];
- this.regionEnglish = json['RegionName'];
+ isHMC = json["IsHMC"];
+ regionArabic = json['RegionNameN'];
+ regionEnglish = json['RegionName'];
}
String? getRegionName(bool isArabic) {
@@ -83,24 +83,22 @@ class HospitalsModel {
Map toJson() {
final Map data = new Map();
- data['Desciption'] = this.desciption;
- data['DesciptionN'] = this.desciptionN;
- data['ID'] = this.iD;
- data['LegalName'] = this.legalName;
- data['LegalNameN'] = this.legalNameN;
- data['Name'] = this.name;
- data['NameN'] = this.nameN;
- data['PhoneNumber'] = this.phoneNumber;
- data['SetupID'] = this.setupID;
- data['DistanceInKilometers'] = this.distanceInKilometers;
- data['IsActive'] = this.isActive;
- data['Latitude'] = this.latitude;
- data['Longitude'] = this.longitude;
- data['MainProjectID'] = this.mainProjectID;
- data['ProjectOutSA'] = this.projectOutSA;
- data['UsingInDoctorApp'] = this.usingInDoctorApp;
+ data['Desciption'] = desciption;
+ data['DesciptionN'] = desciptionN;
+ data['ID'] = iD;
+ data['LegalName'] = legalName;
+ data['LegalNameN'] = legalNameN;
+ data['Name'] = name;
+ data['NameN'] = nameN;
+ data['PhoneNumber'] = phoneNumber;
+ data['SetupID'] = setupID;
+ data['DistanceInKilometers'] = distanceInKilometers;
+ data['IsActive'] = isActive;
+ data['Latitude'] = latitude;
+ data['Longitude'] = longitude;
+ data['MainProjectID'] = mainProjectID;
+ data['ProjectOutSA'] = projectOutSA;
+ data['UsingInDoctorApp'] = usingInDoctorApp;
return data;
}
-
-
}
diff --git a/lib/features/radiology/radiology_view_model.dart b/lib/features/radiology/radiology_view_model.dart
index de6a796..d39d84f 100644
--- a/lib/features/radiology/radiology_view_model.dart
+++ b/lib/features/radiology/radiology_view_model.dart
@@ -13,14 +13,20 @@ class RadiologyViewModel extends ChangeNotifier {
ErrorHandlerService errorHandlerService;
List patientRadiologyOrders = [];
-
+ List filteredRadiologyOrders = [];
+ List tempRadiologyOrders = [];
String radiologyImageURL = "";
String patientRadiologyReportPDFBase64 = "";
+ late List _radiologySuggestionsList = [];
+
+ List get radiologySuggestions => _radiologySuggestionsList;
+
RadiologyViewModel({required this.radiologyRepo, required this.errorHandlerService});
initRadiologyViewModel() {
patientRadiologyOrders.clear();
+ filteredRadiologyOrders.clear();
isRadiologyOrdersLoading = true;
isRadiologyPDFReportLoading = true;
radiologyImageURL = "";
@@ -38,7 +44,10 @@ class RadiologyViewModel extends ChangeNotifier {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
patientRadiologyOrders = apiResponse.data!;
+ filteredRadiologyOrders = List.from(patientRadiologyOrders);
+ tempRadiologyOrders = [...patientRadiologyOrders];
isRadiologyOrdersLoading = false;
+ filterSuggestions();
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
@@ -98,4 +107,23 @@ class RadiologyViewModel extends ChangeNotifier {
},
);
}
+ filterSuggestions() {
+ final List labels = patientRadiologyOrders
+ .map((detail) => detail.description)
+ .whereType()
+ .toList();
+ _radiologySuggestionsList = labels.toSet().toList();
+ notifyListeners();
+ }
+ filterRadiologyReports(String query) {
+ if (query.isEmpty) {
+ patientRadiologyOrders =tempRadiologyOrders;// reset
+ } else {
+ filteredRadiologyOrders =
+ filteredRadiologyOrders.where((desc) => desc.description!.toLowerCase().contains(query.toLowerCase())).toList();
+ patientRadiologyOrders = filteredRadiologyOrders;
+ }
+ notifyListeners();
+ }
+
}
diff --git a/lib/main.dart b/lib/main.dart
index 20c4ece..1af80b6 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -14,6 +14,7 @@ import 'package:hmg_patient_app_new/features/contact_us/contact_us_view_model.da
import 'package:hmg_patient_app_new/features/doctor_filter/doctor_filter_view_model.dart';
import 'package:hmg_patient_app_new/features/emergency_services/emergency_services_view_model.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart';
+import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_view_model.dart';
import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart';
import 'package:hmg_patient_app_new/features/lab/history/lab_history_viewmodel.dart';
@@ -132,7 +133,8 @@ void main() async {
),
ChangeNotifierProvider(
create: (_) => getIt.get(),
- ),ChangeNotifierProvider(
+ ),
+ ChangeNotifierProvider(
create: (_) => getIt.get(),
),
ChangeNotifierProvider(
@@ -140,6 +142,9 @@ void main() async {
),
ChangeNotifierProvider(
create: (_) => getIt.get(),
+ ),
+ ChangeNotifierProvider(
+ create: (_) => getIt.get(),
)
], child: MyApp()),
),
diff --git a/lib/presentation/appointments/appointment_details_page.dart b/lib/presentation/appointments/appointment_details_page.dart
index 79084c9..12f3589 100644
--- a/lib/presentation/appointments/appointment_details_page.dart
+++ b/lib/presentation/appointments/appointment_details_page.dart
@@ -30,7 +30,7 @@ import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
-import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart';
+import 'package:hmg_patient_app_new/widgets/shimmer/common_shimmer_widget.dart';
import 'package:maps_launcher/maps_launcher.dart';
import 'package:provider/provider.dart';
@@ -317,7 +317,7 @@ class _AppointmentDetailsPageState extends State {
SizedBox(height: 16.h),
Consumer(builder: (context, prescriptionVM, child) {
return prescriptionVM.isPrescriptionsDetailsLoading
- ? const MoviesShimmerWidget()
+ ? const CommonShimmerWidget()
: Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: Colors.white,
diff --git a/lib/presentation/appointments/widgets/hospital_bottom_sheet/hospital_bottom_sheet_body.dart b/lib/presentation/appointments/widgets/hospital_bottom_sheet/hospital_bottom_sheet_body.dart
index 29a7b96..ad48a6d 100644
--- a/lib/presentation/appointments/widgets/hospital_bottom_sheet/hospital_bottom_sheet_body.dart
+++ b/lib/presentation/appointments/widgets/hospital_bottom_sheet/hospital_bottom_sheet_body.dart
@@ -1,18 +1,13 @@
-import 'package:easy_localization/easy_localization.dart'
- show tr, StringTranslateExtension;
+import 'package:easy_localization/easy_localization.dart' show StringTranslateExtension;
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
-import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package: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/my_appointments/appointment_via_region_viewmodel.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/facility_selection.dart';
-import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/hospital_bottom_sheet/hospital_list_items.dart';
-import 'package:hmg_patient_app_new/presentation/appointments/widgets/hospital_bottom_sheet/type_selection_widget.dart';
-import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/theme/colors.dart' show AppColors;
import 'package:hmg_patient_app_new/widgets/input_widget.dart';
import 'package:provider/provider.dart';
@@ -53,8 +48,8 @@ class HospitalBottomSheetBody extends StatelessWidget {
hintText: LocaleKeys.searchHospital.tr(),
controller: searchText,
onChange: (value) {
- appointmentsViewModel.filterHospitalListByString(value, regionalViewModel.selectedRegionId , regionalViewModel.selectedFacilityType ==
- FacilitySelection.HMG.name);
+ appointmentsViewModel.filterHospitalListByString(
+ value, regionalViewModel.selectedRegionId, regionalViewModel.selectedFacilityType == FacilitySelection.HMG.name);
},
isEnable: true,
prefix: null,
@@ -77,25 +72,15 @@ class HospitalBottomSheetBody extends StatelessWidget {
SizedBox(
height: MediaQuery.sizeOf(context).height * .4,
child: ListView.separated(
- itemBuilder: (_, index)
- {
- var hospital = regionalViewModel.selectedFacilityType ==
- FacilitySelection.HMG.name
- ? appointmentsViewModel
- .filteredHospitalList!
- .registeredDoctorMap![
- regionalViewModel.selectedRegionId!]!
- .hmgDoctorList![index]
- : appointmentsViewModel
- .filteredHospitalList
- ?.registeredDoctorMap?[
- regionalViewModel.selectedRegionId!]
- ?.hmcDoctorList?[index];
+ itemBuilder: (_, index) {
+ var hospital = regionalViewModel.selectedFacilityType == FacilitySelection.HMG.name
+ ? appointmentsViewModel.filteredHospitalList!.registeredDoctorMap![regionalViewModel.selectedRegionId!]!.hmgDoctorList![index]
+ : appointmentsViewModel.filteredHospitalList?.registeredDoctorMap?[regionalViewModel.selectedRegionId!]?.hmcDoctorList?[index];
return HospitalListItem(
- hospitalData: hospital,
- isLocationEnabled: appointmentsViewModel.isLocationEnabled(),
- ).onPress(() {
- regionalViewModel.setHospitalModel(hospital);
+ hospitalData: hospital,
+ isLocationEnabled: appointmentsViewModel.isLocationEnabled(),
+ ).onPress(() {
+ regionalViewModel.setHospitalModel(hospital);
if (regionalViewModel.regionBottomSheetType == RegionBottomSheetType.FOR_REGION) {
regionalViewModel.setBottomSheetState(AppointmentViaRegionState.CLINIC_SELECTION);
regionalViewModel.handleLastStepForRegion();
@@ -104,21 +89,18 @@ class HospitalBottomSheetBody extends StatelessWidget {
regionalViewModel.handleLastStepForClinic();
} else if (regionalViewModel.regionBottomSheetType == RegionBottomSheetType.REGION_FOR_DENTAL_AND_LASER) {
regionalViewModel.setBottomSheetState(AppointmentViaRegionState.DOCTOR_SELECTION);
- regionalViewModel.handleLastStepForClinicForDentalAndLaser(appointmentsViewModel.selectedClinic.clinicID??-1);
+ regionalViewModel.handleLastStepForClinicForDentalAndLaser(appointmentsViewModel.selectedClinic.clinicID ?? -1);
// regionalViewModel.handleLastStepForClinic();
}
- });},
+ });
+ },
separatorBuilder: (_, __) => SizedBox(
height: 16.h,
),
- itemCount: (regionalViewModel.selectedFacilityType ==
- FacilitySelection.HMG.name
- ? (appointmentsViewModel.filteredHospitalList?.registeredDoctorMap?[
- regionalViewModel.selectedRegionId]?.hmgDoctorList)
- : (appointmentsViewModel
- .filteredHospitalList
- ?.registeredDoctorMap?[
- regionalViewModel.selectedRegionId]?.hmcDoctorList))?.length ??
+ itemCount: (regionalViewModel.selectedFacilityType == FacilitySelection.HMG.name
+ ? (appointmentsViewModel.filteredHospitalList?.registeredDoctorMap?[regionalViewModel.selectedRegionId]?.hmgDoctorList)
+ : (appointmentsViewModel.filteredHospitalList?.registeredDoctorMap?[regionalViewModel.selectedRegionId]?.hmcDoctorList))
+ ?.length ??
0),
)
],
diff --git a/lib/presentation/comprehensive_checkup/cmc_order_detail_page.dart b/lib/presentation/comprehensive_checkup/cmc_order_detail_page.dart
new file mode 100644
index 0000000..7547fd0
--- /dev/null
+++ b/lib/presentation/comprehensive_checkup/cmc_order_detail_page.dart
@@ -0,0 +1,255 @@
+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_assets.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/hmg_services/hmg_services_view_model.dart';
+import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_all_orders_resp_model.dart';
+import 'package:hmg_patient_app_new/presentation/comprehensive_checkup/widgets/cmc_ui_selection_helper.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/chip/app_custom_chip_widget.dart';
+import 'package:provider/provider.dart';
+
+class CmcOrderDetailPage extends StatefulWidget {
+ const CmcOrderDetailPage({super.key});
+
+ @override
+ State createState() => _CmcOrderDetailPageState();
+}
+
+class _CmcOrderDetailPageState extends State {
+ @override
+ void initState() {
+ super.initState();
+ final hmgServicesViewModel = context.read();
+ scheduleMicrotask(() async {
+ await hmgServicesViewModel.getCmcOrdersList();
+ });
+ }
+
+ Color _getStatusColor(int? statusId) {
+ switch (statusId) {
+ case 1: // Pending
+ return const Color(0xffCC9B14);
+ case 2: // Processing
+ return const Color(0xff2E303A);
+ case 3: // Completed
+ return const Color(0xff359846);
+ case 4: // Cancelled
+ case 6: // Rejected
+ case 7: // Rejected
+ return const Color(0xffD02127);
+ default:
+ return AppColors.greyColor;
+ }
+ }
+
+ String _formatDate(String? dateString) {
+ if (dateString == null) return '';
+ try {
+ final date = DateTime.parse(dateString);
+ return DateFormat('MMM dd, yyyy').format(date);
+ } catch (e) {
+ return dateString;
+ }
+ }
+
+ Widget _buildLoadingShimmer() {
+ return ListView.separated(
+ shrinkWrap: true,
+ physics: const NeverScrollableScrollPhysics(),
+ itemCount: 3,
+ separatorBuilder: (_, __) => SizedBox(height: 12.h),
+ itemBuilder: (context, index) {
+ return _buildOrderCard(GetCMCAllOrdersResponseModel(), isLoading: true);
+ },
+ );
+ }
+
+ Widget _buildOrderCard(GetCMCAllOrdersResponseModel order, {bool isLoading = false}) {
+ final statusColor = _getStatusColor(order.statusId);
+ final canCancel = order.statusId == 1 || order.statusId == 2;
+
+ return AnimatedContainer(
+ duration: Duration(milliseconds: 300),
+ curve: Curves.easeInOut,
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 24.h,
+ hasShadow: true,
+ ),
+ child: Padding(
+ padding: EdgeInsets.all(16.w),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ // Status and Date Row
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Container(
+ padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 6.h),
+ decoration: BoxDecoration(
+ color: statusColor.withValues(alpha: 0.1),
+ borderRadius: BorderRadius.circular(8.r),
+ ),
+ child: (isLoading ? "Processing" : order.statusText ?? '')
+ .toText12(
+ color: statusColor,
+ fontWeight: FontWeight.w600,
+ )
+ .toShimmer2(isShow: isLoading),
+ ),
+ SizedBox(width: 8.w),
+ (isLoading ? "Jan 15, 2024" : _formatDate(order.created))
+ .toText12(
+ color: AppColors.textColorLight,
+ fontWeight: FontWeight.w500,
+ )
+ .toShimmer2(isShow: isLoading),
+ ],
+ ),
+
+ SizedBox(height: 16.h),
+
+ // Request ID
+ Row(
+ children: [
+ if (!isLoading) ...[
+ "Request ID:".needTranslation.toText14(
+ color: AppColors.textColorLight,
+ weight: FontWeight.w500,
+ ),
+ SizedBox(width: 4.w),
+ ],
+ (isLoading ? "12345" : "${order.iD ?? '-'}").toText16(isBold: true).toShimmer2(isShow: isLoading),
+ ],
+ ),
+
+ SizedBox(height: 12.h),
+
+ // Chips for Hospital, Service, and Amount
+ Wrap(
+ spacing: 6.w,
+ runSpacing: 6.h,
+ children: [
+ // Hospital
+ if (order.projectName != null || isLoading)
+ AppCustomChipWidget(
+ icon: AppAssets.hospital,
+ labelText: isLoading ? "Hospital Name" : order.projectName ?? '-',
+ ).toShimmer2(isShow: isLoading),
+
+ // Service
+ if (order.serviceText != null || isLoading)
+ AppCustomChipWidget(
+ icon: AppAssets.servicesBottom,
+ labelText: isLoading ? "Service Name" : order.serviceText ?? '-',
+ ).toShimmer2(isShow: isLoading),
+ ],
+ ),
+
+ // Cancel Button
+ if (canCancel || isLoading) ...[
+ SizedBox(height: 16.h),
+ Row(
+ children: [
+ Expanded(
+ child: CustomButton(
+ text: "Cancel Order".needTranslation,
+ onPressed: isLoading ? () {} : () => CmcUiSelectionHelper.showCancelConfirmationDialog(context: context, order: order),
+ backgroundColor: AppColors.primaryRedColor,
+ borderColor: AppColors.primaryRedColor,
+ textColor: AppColors.whiteColor,
+ fontSize: 14.f,
+ fontWeight: FontWeight.w600,
+ borderRadius: 10.r,
+ height: 44.h,
+ ).toShimmer2(isShow: isLoading),
+ ),
+ ],
+ ),
+ ]
+ ],
+ ),
+ ),
+ );
+ }
+
+ Widget _buildEmptyState() {
+ return Center(
+ child: Padding(
+ padding: EdgeInsets.symmetric(vertical: 40.h),
+ child: Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 12.r,
+ hasShadow: false,
+ ),
+ child: Utils.getNoDataWidget(
+ context,
+ noDataText: "You don't have any CMC orders yet.".needTranslation,
+ isSmallWidget: true,
+ width: 62.w,
+ height: 62.h,
+ ),
+ ),
+ ),
+ );
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return CollapsingListView(
+ title: "CMC Orders".needTranslation,
+ isLeading: true,
+ child: SingleChildScrollView(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Consumer(
+ builder: (context, viewModel, child) {
+ if (viewModel.isCmcOrdersLoading) {
+ return _buildLoadingShimmer();
+ }
+
+ if (viewModel.cmcOrdersList.isEmpty) {
+ return _buildEmptyState();
+ }
+
+ return ListView.separated(
+ shrinkWrap: true,
+ physics: const NeverScrollableScrollPhysics(),
+ itemCount: viewModel.cmcOrdersList.length,
+ separatorBuilder: (_, __) => SizedBox(height: 12.h),
+ itemBuilder: (context, index) {
+ final order = viewModel.cmcOrdersList.reversed.toList()[index];
+
+ return AnimationConfiguration.staggeredList(
+ position: index,
+ duration: const Duration(milliseconds: 500),
+ child: SlideAnimation(
+ verticalOffset: 100.0,
+ child: FadeInAnimation(
+ child: _buildOrderCard(order),
+ ),
+ ),
+ );
+ },
+ );
+ },
+ ),
+ ],
+ ).paddingSymmetrical(24.w, 0),
+ ),
+ );
+ }
+}
diff --git a/lib/presentation/comprehensive_checkup/cmc_selection_review_page.dart b/lib/presentation/comprehensive_checkup/cmc_selection_review_page.dart
new file mode 100644
index 0000000..b6164d9
--- /dev/null
+++ b/lib/presentation/comprehensive_checkup/cmc_selection_review_page.dart
@@ -0,0 +1,515 @@
+import 'dart:developer';
+
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:hmg_patient_app_new/core/app_assets.dart';
+import 'package:hmg_patient_app_new/core/app_state.dart';
+import 'package:hmg_patient_app_new/core/dependencies.dart';
+import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
+import 'package:hmg_patient_app_new/core/utils/utils.dart';
+import 'package:hmg_patient_app_new/extensions/route_extensions.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/req_models/cmc_create_new_order_req_model.dart';
+import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_services_resp_model.dart';
+import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart';
+import 'package:hmg_patient_app_new/features/payfort/payfort_view_model.dart';
+import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
+import 'package:hmg_patient_app_new/presentation/comprehensive_checkup/widgets/cmc_ui_selection_helper.dart';
+import 'package:hmg_patient_app_new/theme/colors.dart';
+import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
+import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
+import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
+import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
+import 'package:maps_launcher/maps_launcher.dart';
+import 'package:provider/provider.dart';
+
+class CmcSelectionReviewPage extends StatefulWidget {
+ final GetCMCServicesResponseModel selectedService;
+ final HospitalsModel? preSelectedHospital;
+
+ const CmcSelectionReviewPage({super.key, required this.selectedService, this.preSelectedHospital});
+
+ @override
+ State createState() => _CmcSelectionReviewPageState();
+}
+
+class _CmcSelectionReviewPageState extends State {
+ @override
+ void initState() {
+ super.initState();
+ // Initialize ViewModel state with preselected hospital if provided
+ if (widget.preSelectedHospital != null) {
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ final hmgServicesViewModel = context.read();
+ hmgServicesViewModel.setSelectedHospitalForOrder(widget.preSelectedHospital);
+ hmgServicesViewModel.setSelectedServiceForOrder(widget.selectedService);
+ });
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final appState = getIt.get();
+ final isArabic = appState.isArabic();
+
+ return CollapsingListView(
+ title: "Summary".needTranslation,
+ bottomChild: _buildBottomButton(),
+ child: SingleChildScrollView(
+ padding: EdgeInsets.all(16.w),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ _buildOrderSummaryCard(isArabic),
+ SizedBox(height: 16.h),
+ _buildSelectedServiceCard(isArabic),
+ SizedBox(height: 16.h),
+ _buildPaymentSummary(),
+ ],
+ ),
+ ),
+ );
+ }
+
+ Widget _buildOrderSummaryCard(bool isArabic) {
+ return Consumer(
+ builder: (context, hmgServicesViewModel, child) {
+ final selectedHospital = hmgServicesViewModel.selectedHospitalForOrder;
+ final isLocationSelected = selectedHospital != null;
+
+ return Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 16.r,
+ ),
+ padding: EdgeInsets.all(16.w),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ "Select Hospital".needTranslation,
+ style: TextStyle(
+ fontSize: 16.f,
+ fontWeight: FontWeight.w700,
+ color: AppColors.blackColor,
+ letterSpacing: -0.5,
+ ),
+ ),
+ SizedBox(height: 12.h),
+ _buildHospitalSelector(isArabic, selectedHospital, isLocationSelected),
+ if (isLocationSelected) ...[
+ SizedBox(height: 16.h),
+ _buildHospitalMap(selectedHospital),
+ ],
+ ],
+ ),
+ );
+ },
+ );
+ }
+
+ Widget _buildHospitalSelector(bool isArabic, HospitalsModel? selectedHospital, bool isLocationSelected) {
+ return InkWell(
+ onTap: _showHospitalSelectionBottomSheet,
+ child: Container(
+ padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 14.h),
+ decoration: BoxDecoration(
+ color: AppColors.bgScaffoldColor,
+ borderRadius: BorderRadius.circular(12.r),
+ border: Border.all(
+ color: AppColors.greyColor.withAlpha(51),
+ width: 1,
+ ),
+ ),
+ child: Row(
+ children: [
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ isLocationSelected && selectedHospital != null
+ ? (isArabic ? (selectedHospital.nameN ?? selectedHospital.name ?? '') : (selectedHospital.name ?? ''))
+ : "Select Hospital".needTranslation,
+ style: TextStyle(
+ fontSize: 14.f,
+ fontWeight: isLocationSelected ? FontWeight.w600 : FontWeight.w400,
+ color: isLocationSelected ? AppColors.blackColor : AppColors.greyTextColor,
+ letterSpacing: -0.4,
+ ),
+ ),
+ ],
+ ),
+ ),
+ Icon(
+ Icons.keyboard_arrow_down,
+ color: AppColors.greyTextColor,
+ size: 24.h,
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+
+ Widget _buildHospitalMap(HospitalsModel selectedHospital) {
+ final String lat = selectedHospital.latitude ?? "0.0";
+ final String lng = selectedHospital.longitude ?? "0.0";
+
+ log("selectedHospital: $lng and $lat");
+
+ if (lat == "0.0" || lng == "0.0") return SizedBox.shrink();
+
+ final String staticMapUrl =
+ "https://maps.googleapis.com/maps/api/staticmap?center=$lat,$lng&zoom=16&size=600x300&maptype=roadmap&markers=color:red%7C$lat,$lng&key=AIzaSyCyDbWUM9d_sBUGIE8PcuShzPaqO08NSC8";
+
+ return Stack(
+ children: [
+ ClipRRect(
+ borderRadius: BorderRadius.circular(12.r),
+ child: Image.network(
+ staticMapUrl,
+ height: 200.h,
+ width: double.infinity,
+ fit: BoxFit.cover,
+ loadingBuilder: (context, child, loadingProgress) {
+ if (loadingProgress == null) return child;
+ return Container(
+ height: 200.h,
+ decoration: BoxDecoration(
+ color: AppColors.bgScaffoldColor,
+ borderRadius: BorderRadius.circular(12.r),
+ ),
+ child: Center(
+ child: CircularProgressIndicator(
+ color: AppColors.primaryRedColor,
+ ),
+ ),
+ );
+ },
+ errorBuilder: (context, error, stackTrace) {
+ return Container(
+ height: 200.h,
+ decoration: BoxDecoration(
+ color: AppColors.bgScaffoldColor,
+ borderRadius: BorderRadius.circular(12.r),
+ ),
+ child: Center(
+ child: Icon(
+ Icons.error_outline,
+ size: 48.h,
+ color: AppColors.greyTextColor,
+ ),
+ ),
+ );
+ },
+ ),
+ ),
+ Positioned(
+ bottom: 12.h,
+ right: 12.w,
+ child: InkWell(
+ onTap: () => _launchDirections(selectedHospital),
+ child: Container(
+ padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 8.h),
+ decoration: BoxDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: BorderRadius.circular(1000.r),
+ boxShadow: [
+ BoxShadow(
+ color: Color.fromARGB(26, 0, 0, 0),
+ blurRadius: 8,
+ offset: Offset(0, 2),
+ ),
+ ],
+ ),
+ child: Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Utils.buildSvgWithAssets(
+ icon: AppAssets.directions_icon,
+ width: 16.w,
+ height: 16.h,
+ ),
+ SizedBox(width: 6.w),
+ Text(
+ "Get Directions".needTranslation,
+ style: TextStyle(
+ fontSize: 12.f,
+ fontWeight: FontWeight.w600,
+ color: AppColors.blackColor,
+ letterSpacing: -0.4,
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ ],
+ );
+ }
+
+ Widget _buildSelectedServiceCard(bool isArabic) {
+ final serviceName = isArabic ? (widget.selectedService.textN ?? widget.selectedService.text ?? '') : (widget.selectedService.text ?? '');
+ final price = widget.selectedService.priceTotal ?? 0.0;
+
+ return Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 16.r,
+ ),
+ padding: EdgeInsets.all(16.w),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ "Selected Service".needTranslation.toText14(
+ weight: FontWeight.w600,
+ color: AppColors.greyTextColor,
+ letterSpacing: -0.4,
+ ),
+ SizedBox(height: 6.h),
+ Row(
+ children: [
+ Expanded(child: serviceName.toText16(weight: FontWeight.w700, color: AppColors.blackColor, letterSpacing: -0.5)),
+ ],
+ ),
+ ],
+ ),
+ );
+ }
+
+ Widget _buildPaymentSummary() {
+ // Use selected service from widget
+ final service = widget.selectedService;
+
+ log("service: ${service.toJson()}");
+
+ final double amountBeforeTax = service.price ?? 0.0;
+ final double taxAmount = service.priceVAT ?? 0.0;
+ final double totalAmount = service.priceTotal ?? (amountBeforeTax + taxAmount);
+
+ return Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 24.h,
+ hasShadow: false,
+ ),
+ child: Consumer(builder: (context, payfortVM, child) {
+ return Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ SizedBox(height: 24.h),
+ "Total amount to pay".needTranslation.toText18(isBold: true).paddingSymmetrical(24.h, 0.h),
+ SizedBox(height: 17.h),
+
+ // Amount before tax
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ "Amount before tax".needTranslation.toText14(isBold: true),
+ Utils.getPaymentAmountWithSymbol(
+ amountBeforeTax.toString().toText16(isBold: true),
+ AppColors.blackColor,
+ 13,
+ isSaudiCurrency: true,
+ ),
+ ],
+ ).paddingSymmetrical(24.h, 0.h),
+
+ // VAT (use label VAT 15% if desired)
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ "VAT 15%".needTranslation.toText14(isBold: true, color: AppColors.greyTextColor),
+ Utils.getPaymentAmountWithSymbol(
+ taxAmount.toString().toText14(isBold: true, color: AppColors.greyTextColor),
+ AppColors.greyTextColor,
+ 13,
+ isSaudiCurrency: true,
+ ),
+ ],
+ ).paddingSymmetrical(24.h, 0.h),
+ SizedBox(height: 17.h),
+ // Total Amount
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ "".needTranslation.toText14(isBold: true),
+ Utils.getPaymentAmountWithSymbol(
+ totalAmount.toString().toText24(isBold: true),
+ AppColors.blackColor,
+ 17,
+ isSaudiCurrency: true,
+ ),
+ ],
+ ).paddingSymmetrical(24.h, 0.h),
+
+ SizedBox(height: 16.h),
+ ],
+ );
+ }),
+ );
+ }
+
+ Widget _buildBottomButton() {
+ return Consumer(
+ builder: (context, hmgServicesViewModel, child) {
+ final isLocationSelected = hmgServicesViewModel.selectedHospitalForOrder != null;
+
+ return SafeArea(
+ top: false,
+ child: Container(
+ padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 12.h),
+ decoration: BoxDecoration(
+ color: AppColors.whiteColor,
+ boxShadow: [
+ BoxShadow(
+ color: Color.fromARGB(13, 0, 0, 0),
+ blurRadius: 8,
+ offset: Offset(0, -2),
+ ),
+ ],
+ ),
+ child: CustomButton(
+ text: "Confirm".needTranslation,
+ onPressed: () {
+ isLocationSelected ? _handleConfirm() : null;
+ },
+ textColor: AppColors.whiteColor,
+ backgroundColor: isLocationSelected ? AppColors.successColor : AppColors.greyColor,
+ borderRadius: 12.r,
+ borderColor: Colors.transparent,
+ borderWidth: 0,
+ padding: EdgeInsets.symmetric(vertical: 14.h),
+ ),
+ ),
+ );
+ },
+ );
+ }
+
+ void _showHospitalSelectionBottomSheet() {
+ CmcUiSelectionHelper.showHospitalSelectionBottomSheet(context: context, onHospitalSelected: (hospital) => context.pop());
+ }
+
+ void _launchDirections(HospitalsModel selectedHospital) {
+ final double lat = double.parse(selectedHospital.latitude ?? "0.0");
+ final double lng = double.parse(selectedHospital.longitude ?? "0.0");
+
+ if (lat != 0.0 && lng != 0.0) {
+ MapsLauncher.launchCoordinates(
+ lat,
+ lng,
+ selectedHospital.name ?? "Hospital",
+ );
+ }
+ }
+
+ showSuccessBottomSheet(int requestId, HmgServicesViewModel hmgServicesViewModel) {
+ return showCommonBottomSheetWithoutHeight(
+ context,
+ child: Padding(
+ padding: EdgeInsets.all(16.w),
+ child: Column(
+ children: [
+ Utils.getSuccessWidget(loadingText: "Your request has been successfully submitted.".needTranslation),
+ Row(
+ children: [
+ "Here is your request #: ".needTranslation.toText14(
+ color: AppColors.textColorLight,
+ weight: FontWeight.w500,
+ ),
+ SizedBox(width: 4.w),
+ ("$requestId").toText16(isBold: true),
+ ],
+ ),
+ SizedBox(height: 24.h),
+ Row(
+ children: [
+ Expanded(
+ child: CustomButton(
+ height: 56.h,
+ text: LocaleKeys.ok.tr(),
+ onPressed: () {
+ context.pop();
+ context.pop();
+ hmgServicesViewModel.getAllCmcOrders();
+ },
+ textColor: AppColors.whiteColor,
+ ),
+ ),
+ ],
+ ),
+ ],
+ ),
+ ),
+ isCloseButtonVisible: false,
+ isDismissible: false,
+ isFullScreen: false,
+ );
+ }
+
+ void _handleConfirm() {
+ final hmgServicesViewModel = context.read();
+ final selectedHospital = hmgServicesViewModel.selectedHospitalForOrder;
+
+ if (selectedHospital == null) {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(
+ content: Text("Please select a hospital to continue".needTranslation),
+ backgroundColor: AppColors.errorColor,
+ ),
+ );
+ return;
+ }
+
+ final selectedService = widget.selectedService;
+ return showCommonBottomSheetWithoutHeight(
+ title: LocaleKeys.notice.tr(context: context),
+ context,
+ child: Utils.getWarningWidget(
+ loadingText: "Are you sure you want to submit this request?".needTranslation,
+ isShowActionButtons: true,
+ onCancelTap: () {
+ Navigator.pop(context);
+ },
+ onConfirmTap: () async {
+ Navigator.pop(context);
+ LoaderBottomSheet.showLoader();
+
+ // Create the services list
+ final servicesList = [
+ PatientERCMCInsertServicesList(
+ recordID: selectedService.iD,
+ serviceID: selectedService.serviceID,
+ selectedServiceName: selectedService.text,
+ selectedServiceNameAR: selectedService.textN,
+ price: selectedService.price,
+ vAT: selectedService.priceVAT,
+ totalPrice: selectedService.priceTotal,
+ ),
+ ];
+
+ await hmgServicesViewModel.addCmcOrder(
+ projectID: selectedHospital.mainProjectID ?? 0,
+ orderServiceID: selectedService.orderServiceID ?? 3,
+ services: servicesList,
+ onSuccess: (requestId) {
+ LoaderBottomSheet.hideLoader();
+ showSuccessBottomSheet(requestId, hmgServicesViewModel);
+ },
+ onError: (err) {
+ LoaderBottomSheet.hideLoader();
+ // showCommonBottomSheetWithoutHeight(context, child: Utils.getErrorWidget(loadingText: err), callBackFunc: () {});
+ },
+ );
+ }),
+ callBackFunc: () {},
+ isFullScreen: false,
+ isCloseButtonVisible: true,
+ );
+ }
+}
diff --git a/lib/presentation/comprehensive_checkup/comprehensive_checkup_page.dart b/lib/presentation/comprehensive_checkup/comprehensive_checkup_page.dart
new file mode 100644
index 0000000..5529b93
--- /dev/null
+++ b/lib/presentation/comprehensive_checkup/comprehensive_checkup_page.dart
@@ -0,0 +1,405 @@
+import 'dart:async';
+
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:hmg_patient_app_new/core/app_assets.dart';
+import 'package:hmg_patient_app_new/core/app_state.dart';
+import 'package:hmg_patient_app_new/core/dependencies.dart';
+import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
+import 'package:hmg_patient_app_new/core/utils/utils.dart';
+import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
+import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
+import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart';
+import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_all_orders_resp_model.dart';
+import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_services_resp_model.dart';
+import 'package:hmg_patient_app_new/presentation/comprehensive_checkup/cmc_order_detail_page.dart';
+import 'package:hmg_patient_app_new/presentation/comprehensive_checkup/cmc_selection_review_page.dart';
+import 'package:hmg_patient_app_new/presentation/comprehensive_checkup/widgets/cmc_ui_selection_helper.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/chip/app_custom_chip_widget.dart';
+import 'package:hmg_patient_app_new/widgets/media_viewer/full_screen_image_viewer.dart';
+import 'package:hmg_patient_app_new/widgets/radio_list_tile_widget.dart';
+import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
+import 'package:provider/provider.dart';
+import 'package:shimmer/shimmer.dart';
+
+class ComprehensiveCheckupPage extends StatefulWidget {
+ const ComprehensiveCheckupPage({super.key});
+
+ @override
+ State createState() => _ComprehensiveCheckupPageState();
+}
+
+class _ComprehensiveCheckupPageState extends State {
+ int? _selectedServiceId;
+ GetCMCServicesResponseModel? _selectedService;
+
+ @override
+ void initState() {
+ super.initState();
+ final HmgServicesViewModel hmgServicesViewModel = context.read();
+ final AppState appState = getIt.get();
+
+ scheduleMicrotask(() async {
+ final user = appState.getAuthenticatedUser();
+ if (user != null) {
+ await hmgServicesViewModel.getAllCmcOrders();
+ await hmgServicesViewModel.getAllCmcServices(patientID: user.patientId ?? 0);
+ }
+ });
+ }
+
+ GetCMCAllOrdersResponseModel? _getPendingOrder(List orders) {
+ if (orders.isEmpty) return null;
+
+ // Find pending or processing orders (status 1 or 2)
+ for (var order in orders) {
+ if (order.statusId == 1 || order.statusId == 2) {
+ return order;
+ }
+ }
+
+ return null;
+ }
+
+ Widget _buildPendingOrderCard(GetCMCAllOrdersResponseModel order) {
+ int status = order.statusId ?? 0;
+ String statusDisp = order.statusText ?? "";
+ Color statusColor;
+
+ if (status == 1) {
+ // pending
+ statusColor = AppColors.statusPendingColor;
+ } else if (status == 2) {
+ // processing
+ statusColor = AppColors.statusProcessingColor;
+ } else if (status == 3) {
+ // completed
+ statusColor = AppColors.statusCompletedColor;
+ } else {
+ // cancel / rejected
+ statusColor = AppColors.statusRejectedColor;
+ }
+
+ final canCancel = order.statusId == 1 || order.statusId == 2;
+
+ return Container(
+ width: double.infinity,
+ margin: EdgeInsets.all(16.w),
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 24.h,
+ hasShadow: true,
+ ),
+ child: Padding(
+ padding: EdgeInsets.all(16.w),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ // Status and Date Row
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Container(
+ padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 6.h),
+ decoration: BoxDecoration(
+ color: statusColor.withValues(alpha: 0.1),
+ borderRadius: BorderRadius.circular(8.r),
+ ),
+ child: statusDisp.toText12(
+ color: statusColor,
+ fontWeight: FontWeight.w600,
+ ),
+ ),
+ SizedBox(width: 8.w),
+ if (order.created != null)
+ DateFormat('MMM dd, yyyy').format(DateTime.parse(order.created!)).toText12(
+ color: AppColors.textColorLight,
+ fontWeight: FontWeight.w500,
+ ),
+ ],
+ ),
+
+ SizedBox(height: 16.h),
+
+ // Request ID
+ Row(
+ children: [
+ "Request ID:".needTranslation.toText14(color: AppColors.textColorLight, weight: FontWeight.w500),
+ SizedBox(width: 4.w),
+ "${order.iD ?? '-'}".toText16(isBold: true),
+ ],
+ ),
+
+ SizedBox(height: 12.h),
+
+ // Chips for Hospital, Service, and Amount
+ Wrap(
+ spacing: 6.w,
+ runSpacing: 6.h,
+ children: [
+ // Hospital
+ if (order.projectName != null)
+ AppCustomChipWidget(
+ icon: AppAssets.hospital,
+ labelText: order.projectName ?? '-',
+ ),
+
+ // Service
+ if (order.serviceText != null)
+ AppCustomChipWidget(
+ icon: AppAssets.file_icon,
+ labelText: order.serviceText ?? '-',
+ ),
+ ],
+ ),
+
+ SizedBox(height: 16.h),
+
+ // Info message
+ Container(
+ padding: EdgeInsets.all(12.w),
+ decoration: BoxDecoration(
+ color: AppColors.infoBannerBgColor,
+ borderRadius: BorderRadius.circular(10.r),
+ border: Border.all(
+ color: AppColors.infoBannerBorderColor,
+ width: 1,
+ ),
+ ),
+ child: Row(
+ children: [
+ Icon(
+ Icons.info_outline,
+ size: 20.w,
+ color: AppColors.infoBannerIconColor,
+ ),
+ SizedBox(width: 8.w),
+ Expanded(
+ child: "You have a pending order. Please wait for it to be processed.".needTranslation.toText12(
+ color: AppColors.infoBannerTextColor,
+ fontWeight: FontWeight.w500,
+ ),
+ ),
+ ],
+ ),
+ ),
+ if (canCancel) ...[
+ SizedBox(height: 16.h),
+ Row(
+ children: [
+ Expanded(
+ child: CustomButton(
+ text: "Cancel Order".needTranslation,
+ onPressed: () => CmcUiSelectionHelper.showCancelConfirmationDialog(context: context, order: order),
+ backgroundColor: AppColors.primaryRedColor,
+ borderColor: AppColors.primaryRedColor,
+ textColor: AppColors.whiteColor,
+ fontSize: 14.f,
+ fontWeight: FontWeight.w600,
+ borderRadius: 10.r,
+ height: 44.h,
+ ),
+ ),
+ ],
+ ),
+ ]
+ ],
+ ),
+ ),
+ );
+ }
+
+ Widget _buildServiceSelectionList(List services) {
+ if (services.isEmpty) {
+ return Center(
+ child: Padding(
+ padding: EdgeInsets.all(24.h),
+ child: Text(
+ 'No services available'.needTranslation,
+ style: TextStyle(
+ fontSize: 16.h,
+ color: AppColors.greyTextColor,
+ ),
+ ),
+ ),
+ );
+ }
+
+ return Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ SizedBox(height: 16.h),
+ Text(
+ 'Select a Service'.needTranslation,
+ style: TextStyle(
+ fontSize: 20.h,
+ fontWeight: FontWeight.w700,
+ color: AppColors.blackColor,
+ letterSpacing: -0.8,
+ ),
+ ).paddingOnly(left: 16.w, right: 16.w),
+ ListView.builder(
+ padding: EdgeInsets.zero,
+ itemCount: services.length,
+ shrinkWrap: true,
+ physics: NeverScrollableScrollPhysics(),
+ itemBuilder: (context, index) {
+ final service = services[index];
+ final serviceName = service.text ?? service.textN ?? '';
+ final price = service.priceTotal ?? 0.0;
+ return RadioListTileWidget(
+ value: service.iD ?? 0,
+ groupValue: _selectedServiceId,
+ title: serviceName,
+ subtitleWidget: Utils.getPaymentAmountWithSymbol(
+ isExpanded: false,
+ price.toString().toText14(),
+ AppColors.blackColor,
+ 14,
+ isSaudiCurrency: true,
+ ),
+ onChanged: (value) {
+ setState(() {
+ _selectedServiceId = value;
+ _selectedService = service;
+ });
+ },
+ );
+ },
+ ),
+ // Illustration image below the services list similar to the old implementation
+ SizedBox(height: 12.h),
+ Builder(builder: (context) {
+ final appStateLocal = getIt.get();
+ final String imagePath = appStateLocal.isArabic() ? AppAssets.comprehensiveCheckupAr : AppAssets.comprehensiveCheckupEn;
+ return Stack(
+ children: [
+ Image.asset(
+ imagePath,
+ width: double.infinity,
+ fit: BoxFit.cover,
+ ).paddingAll(16.w),
+ Align(
+ alignment: Alignment.topRight,
+ child: Container(
+ decoration: BoxDecoration(
+ color: Color.fromARGB(51, 0, 0, 0),
+ borderRadius: BorderRadius.circular(1000.r),
+ ),
+ margin: EdgeInsets.all(16.h),
+ child: IconButton(
+ icon: Icon(
+ Icons.zoom_in,
+ color: Colors.white,
+ size: 26.w,
+ ),
+ padding: EdgeInsets.all(10.h),
+ onPressed: () => _showFullScreenImage(context, imagePath, isSvg: false),
+ ),
+ ),
+ ),
+ ],
+ );
+ }),
+ ],
+ );
+ }
+
+ void _proceedWithSelectedService() {
+ if (_selectedService != null) {
+ final hmgServicesViewModel = context.read();
+
+ // Store selected service in ViewModel
+ hmgServicesViewModel.setSelectedServiceForOrder(_selectedService);
+ hmgServicesViewModel.getHospitalsList();
+ // Show hospital selection bottom sheet using common helper
+ CmcUiSelectionHelper.showHospitalSelectionBottomSheet(
+ context: context,
+ onHospitalSelected: (hospital) {
+ Navigator.of(context).pushReplacement(
+ CustomPageRoute(
+ page: CmcSelectionReviewPage(selectedService: _selectedService!, preSelectedHospital: hospital),
+ direction: AxisDirection.left,
+ ),
+ );
+ },
+ );
+ }
+ }
+
+ Widget _buildLoadingShimmer() {
+ return ListView.separated(
+ shrinkWrap: true,
+ physics: const NeverScrollableScrollPhysics(),
+ padding: EdgeInsets.all(16.w),
+ itemCount: 10,
+ separatorBuilder: (_, __) => SizedBox(height: 12.h),
+ itemBuilder: (context, index) {
+ return Shimmer.fromColors(
+ baseColor: Colors.grey[300]!,
+ highlightColor: Colors.grey[100]!,
+ child: Container(
+ height: 80.h,
+ decoration: BoxDecoration(
+ color: Colors.white,
+ borderRadius: BorderRadius.circular(10.r),
+ ),
+ ),
+ );
+ },
+ );
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return CollapsingListView(
+ title: "Comprehensive Checkup".needTranslation,
+ history: () => Navigator.of(context).push(CustomPageRoute(page: CmcOrderDetailPage(), direction: AxisDirection.up)),
+ bottomChild: Consumer(
+ builder: (context, hmgServicesViewModel, child) {
+ if (hmgServicesViewModel.isCmcOrdersLoading || hmgServicesViewModel.isCmcServicesLoading) return SizedBox.shrink();
+ final pendingOrder = _getPendingOrder(hmgServicesViewModel.cmcOrdersList);
+ if (pendingOrder == null && _selectedServiceId != null) {
+ return SafeArea(
+ top: false,
+ child: Padding(
+ padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 12.h),
+ child: CustomButton(
+ borderWidth: 0,
+ text: "Next".needTranslation,
+ onPressed: _proceedWithSelectedService,
+ textColor: AppColors.whiteColor,
+ borderRadius: 12.r,
+ borderColor: Colors.transparent,
+ padding: EdgeInsets.symmetric(vertical: 14.h),
+ ),
+ ),
+ );
+ }
+ return SizedBox.shrink();
+ },
+ ),
+ child: Consumer(
+ builder: (context, hmgServicesViewModel, child) {
+ if (hmgServicesViewModel.isCmcOrdersLoading || hmgServicesViewModel.isCmcServicesLoading) {
+ return _buildLoadingShimmer();
+ }
+ final pendingOrder = _getPendingOrder(hmgServicesViewModel.cmcOrdersList);
+ if (pendingOrder != null) {
+ return _buildPendingOrderCard(pendingOrder);
+ } else {
+ return _buildServiceSelectionList(hmgServicesViewModel.cmcServicesList);
+ }
+ },
+ ),
+ );
+ }
+
+ void _showFullScreenImage(BuildContext context, String path, {bool isSvg = false}) {
+ Navigator.of(context).push(MaterialPageRoute(builder: (_) => FullScreenImageViewer(isSvg: isSvg, path: path)));
+ }
+}
diff --git a/lib/presentation/comprehensive_checkup/widgets/cmc_hospital_bottom_sheet_body.dart b/lib/presentation/comprehensive_checkup/widgets/cmc_hospital_bottom_sheet_body.dart
new file mode 100644
index 0000000..98e91b8
--- /dev/null
+++ b/lib/presentation/comprehensive_checkup/widgets/cmc_hospital_bottom_sheet_body.dart
@@ -0,0 +1,111 @@
+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/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/extensions/string_extensions.dart';
+import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart';
+import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart';
+import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
+import 'package:hmg_patient_app_new/presentation/comprehensive_checkup/widgets/cmc_hospital_list_item.dart';
+import 'package:hmg_patient_app_new/theme/colors.dart';
+import 'package:hmg_patient_app_new/widgets/input_widget.dart';
+import 'package:provider/provider.dart';
+import 'package:shimmer/shimmer.dart';
+
+class CmcHospitalBottomSheetBody extends StatelessWidget {
+ final Function(HospitalsModel) onHospitalSelected;
+
+ const CmcHospitalBottomSheetBody({super.key, required this.onHospitalSelected});
+
+ Widget _buildLoadingShimmer() {
+ return ListView.separated(
+ shrinkWrap: true,
+ physics: const NeverScrollableScrollPhysics(),
+ itemCount: 4,
+ separatorBuilder: (_, __) => SizedBox(height: 12.h),
+ itemBuilder: (context, index) {
+ return Shimmer.fromColors(
+ baseColor: Colors.grey[300]!,
+ highlightColor: Colors.grey[100]!,
+ child: Container(
+ height: 80.h,
+ decoration: BoxDecoration(
+ color: Colors.white,
+ borderRadius: BorderRadius.circular(10.r),
+ ),
+ ),
+ );
+ },
+ );
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final appState = getIt.get();
+ final bool isArabic = appState.isArabic();
+ final bool isLocationEnabled = (appState.userLat != 0) && (appState.userLong != 0);
+
+ return Consumer(
+ builder: (BuildContext context, HmgServicesViewModel hmgServicesViewModel, Widget? child) {
+ return Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ "Choose your preferred hospital for the service".needTranslation.toText14(
+ weight: FontWeight.w400,
+ color: AppColors.greyTextColor,
+ letterSpacing: -0.4,
+ ),
+ SizedBox(height: 16.h),
+ TextInputWidget(
+ labelText: LocaleKeys.search.tr(),
+ hintText: LocaleKeys.searchHospital.tr(),
+ onChange: (value) {
+ hmgServicesViewModel.filterHospitalsByString(value ?? '', isArabic);
+ },
+ isEnable: true,
+ prefix: null,
+ autoFocus: false,
+ isBorderAllowed: false,
+ keyboardType: TextInputType.text,
+ isAllowLeadingIcon: true,
+ selectionType: SelectionTypeEnum.search,
+ padding: EdgeInsets.symmetric(vertical: ResponsiveExtension(10).h, horizontal: ResponsiveExtension(15).h),
+ ),
+ ],
+ ),
+ SizedBox(height: 8.h),
+ SizedBox(
+ height: MediaQuery.of(context).size.height * 0.4,
+ child: hmgServicesViewModel.isHospitalListLoading
+ ? _buildLoadingShimmer()
+ : hmgServicesViewModel.filteredHospitalsList.isEmpty
+ ? Center(
+ child: "No hospitals Found".needTranslation.toText16(weight: FontWeight.w500, color: AppColors.greyTextColor),
+ )
+ : ListView.separated(
+ itemCount: hmgServicesViewModel.filteredHospitalsList.length,
+ separatorBuilder: (context, index) => SizedBox(height: 12.h),
+ itemBuilder: (context, index) {
+ final hospital = hmgServicesViewModel.filteredHospitalsList[index];
+ return CmcHospitalListItem(
+ hospital: hospital,
+ isLocationEnabled: isLocationEnabled,
+ onPress: () {
+ hmgServicesViewModel.setSelectedHospital(hospital);
+ onHospitalSelected(hospital);
+ },
+ );
+ },
+ ),
+ ),
+ ],
+ );
+ },
+ );
+ }
+}
diff --git a/lib/presentation/comprehensive_checkup/widgets/cmc_hospital_list_item.dart b/lib/presentation/comprehensive_checkup/widgets/cmc_hospital_list_item.dart
new file mode 100644
index 0000000..39d6d7c
--- /dev/null
+++ b/lib/presentation/comprehensive_checkup/widgets/cmc_hospital_list_item.dart
@@ -0,0 +1,126 @@
+import 'dart:developer';
+
+import 'package:flutter/material.dart';
+import 'package:hmg_patient_app_new/core/app_assets.dart';
+import 'package:hmg_patient_app_new/core/app_state.dart';
+import 'package:hmg_patient_app_new/core/dependencies.dart';
+import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
+import 'package:hmg_patient_app_new/core/utils/utils.dart';
+import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
+import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
+import 'package:hmg_patient_app_new/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/chip/app_custom_chip_widget.dart';
+
+class CmcHospitalListItem extends StatelessWidget {
+ final HospitalsModel hospital;
+ final VoidCallback onPress;
+ final bool isLocationEnabled;
+
+ const CmcHospitalListItem({
+ super.key,
+ required this.hospital,
+ required this.onPress,
+ this.isLocationEnabled = false,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ final appState = getIt.get