Merge remote-tracking branch 'origin/master' into ambulance_listing_and_request

# Conflicts:
#	lib/core/dependencies.dart
#	lib/features/emergency_services/emergency_services_view_model.dart
#	lib/presentation/appointments/widgets/appointment_doctor_card.dart
#	lib/widgets/common_bottom_sheet.dart
pull/96/head
tahaalam 3 months ago
commit 53be50b40c

@ -88,16 +88,16 @@ class ApiClientImp implements ApiClient {
@override
post(
String endPoint, {
required Map<String, dynamic> body,
required Function(dynamic response, int statusCode, {int? messageStatus, String? errorMessage}) onSuccess,
required Function(String error, int statusCode, {int? messageStatus, Failure? failureType}) onFailure,
bool isAllowAny = false,
bool isExternal = false,
bool isRCService = false,
bool isPaymentServices = false,
bool bypassConnectionCheck = true,
}) async {
String endPoint, {
required Map<String, dynamic> body,
required Function(dynamic response, int statusCode, {int? messageStatus, String? errorMessage}) onSuccess,
required Function(String error, int statusCode, {int? messageStatus, Failure? failureType}) onFailure,
bool isAllowAny = false,
bool isExternal = false,
bool isRCService = false,
bool isPaymentServices = false,
bool bypassConnectionCheck = true,
}) async {
String url;
if (isExternal) {
url = endPoint;
@ -119,7 +119,8 @@ class ApiClientImp implements ApiClient {
} else {}
if (body.containsKey('isDentalAllowedBackend')) {
body['isDentalAllowedBackend'] = body.containsKey('isDentalAllowedBackend') ? body['isDentalAllowedBackend'] ?? IS_DENTAL_ALLOWED_BACKEND : IS_DENTAL_ALLOWED_BACKEND;
body['isDentalAllowedBackend'] =
body.containsKey('isDentalAllowedBackend') ? body['isDentalAllowedBackend'] ?? IS_DENTAL_ALLOWED_BACKEND : IS_DENTAL_ALLOWED_BACKEND;
}
if (!body.containsKey('IsPublicRequest')) {
@ -136,9 +137,9 @@ class ApiClientImp implements ApiClient {
body['PatientType'] = PATIENT_TYPE_ID.toString();
}
// TODO : These should be from the appState
if (user != null) {
body['TokenID'] = body['TokenID'] ?? token;
body['PatientID'] = body['PatientID'] ?? user.patientId;
body['PatientOutSA'] = body.containsKey('PatientOutSA') ? body['PatientOutSA'] ?? user.outSa : user.outSa;
@ -174,6 +175,7 @@ class ApiClientImp implements ApiClient {
}
// body['TokenID'] = "@dm!n";
// body['PatientID'] = 4772429;
// body['PatientID'] = 1231755;
// body['PatientTypeID'] = 1;
//
@ -182,9 +184,10 @@ class ApiClientImp implements ApiClient {
}
body.removeWhere((key, value) => value == null);
log("body: ${json.encode(body)}");
log("uri: ${Uri.parse(url.trim())}");
log("body: ${json.encode(body)}");
final bool networkStatus = await Utils.checkConnection(bypassConnectionCheck: bypassConnectionCheck);
if (!networkStatus) {
@ -210,35 +213,43 @@ 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.contains('MessageStatus') ? parsed['MessageStatus'] : 1,
errorMessage: parsed.contains('ErrorEndUserMessage') ? parsed['ErrorEndUserMessage'] : "");
}
} else {
if (parsed['Response_Message'] != null) {
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
onSuccess(parsed, statusCode,
messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
} else {
if (parsed['ErrorType'] == 4) {
//TODO : handle app update
onFailure(parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], statusCode, failureType: AppUpdateFailure("parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']"));
onFailure(parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], statusCode,
failureType: AppUpdateFailure("parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']"));
logApiEndpointError(endPoint, parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], statusCode);
}
if (parsed['ErrorType'] == 2) {
// todo: handle Logout
// todo_section: handle Logout
onFailure(
parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'],
statusCode,
failureType: UnAuthenticatedUserFailure(parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'] ?? "User is not Authenticated", url: url),
failureType:
UnAuthenticatedUserFailure(parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'] ?? "User is not Authenticated", url: url),
);
// logApiEndpointError(endPoint, "session logged out", statusCode);
}
if (isAllowAny) {
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
onSuccess(parsed, statusCode,
messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
} else if (parsed['IsAuthenticated'] == null) {
if (parsed['isSMSSent'] == true) {
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
onSuccess(parsed, statusCode,
messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
} else if (parsed['MessageStatus'] == 1) {
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
onSuccess(parsed, statusCode,
messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
} else if (parsed['Result'] == 'OK') {
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
onSuccess(parsed, statusCode,
messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
} else {
onFailure(
parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'],
@ -248,16 +259,19 @@ class ApiClientImp implements ApiClient {
logApiEndpointError(endPoint, parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], statusCode);
}
} else if (parsed['MessageStatus'] == 1 || parsed['SMSLoginRequired'] == true) {
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
onSuccess(parsed, statusCode,
messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
} else if (parsed['IsAuthenticated'] == false) {
onFailure(
"User is not Authenticated",
statusCode,
failureType: UnAuthenticatedUserFailure(parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'] ?? "User is not Authenticated", url: url),
failureType:
UnAuthenticatedUserFailure(parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'] ?? "User is not Authenticated", url: url),
);
} else if (parsed['MessageStatus'] == 2 && parsed['IsAuthenticated']) {
if (parsed['SameClinicApptList'] != null) {
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
onSuccess(parsed, statusCode,
messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
} else {
if (parsed['message'] == null && parsed['ErrorEndUserMessage'] == null) {
if (parsed['ErrorSearchMsg'] == null) {
@ -276,7 +290,6 @@ class ApiClientImp implements ApiClient {
logApiEndpointError(endPoint, parsed['ErrorSearchMsg'], statusCode);
}
} else {
onFailure(
parsed['message'] ?? parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'],
statusCode,
@ -287,7 +300,8 @@ class ApiClientImp implements ApiClient {
}
} else {
if (parsed['SameClinicApptList'] != null) {
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
onSuccess(parsed, statusCode,
messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
} else {
if (parsed['message'] != null) {
onFailure(

@ -1,10 +1,6 @@
import 'package:amazon_payfort/amazon_payfort.dart';
import 'package:hmg_patient_app_new/core/enums.dart';
var MAX_SMALL_SCREEN = 660;
final OPENTOK_API_KEY = '46209962';
// final OPENTOK_API_KEY = '47464241';
// PACKAGES and OFFERS
var EXA_CART_API_BASE_URL = 'https://mdlaboratories.com/offersdiscounts';
// var EXA_CART_API_BASE_URL = 'http://10.200.101.75:9000';
@ -265,7 +261,6 @@ var CANCEL_APPOINTMENT = "Services/Doctors.svc/REST/CancelAppointment";
var GENERATE_QR_APPOINTMENT = "Services/Doctors.svc/REST/GenerateQRAppointmentNo";
//URL send email appointment QR
var EMAIL_QR_APPOINTMENT = "Services/Notifications.svc/REST/sendEmailForOnLineCheckin";
//URL check payment status
var CHECK_PAYMENT_STATUS = "Services/PayFort_Serv.svc/REST/GetRequestStatusByRequestID";
@ -275,14 +270,8 @@ var CREATE_ADVANCE_PAYMENT = "Services/Doctors.svc/REST/CreateAdvancePayment";
var HIS_CREATE_ADVANCE_PAYMENT = "Services/Patients.svc/REST/HIS_CreateAdvancePayment";
var ER_CREATE_ADVANCE_PAYMENT = "services/Doctors.svc/REST/ER_CreateAdvancePaymentForClinic";
var ER_INSERT_ADVANCE_PAYMENT = "services/Doctors.svc/REST/ER_InsertEROnlinePaymentDetails";
var ADD_ADVANCE_NUMBER_REQUEST = 'Services/PayFort_Serv.svc/REST/AddAdvancedNumberRequest';
var GENERATE_ANCILLARY_ORDERS_INVOICE = 'Services/Doctors.svc/REST/AutoGenerateAncillaryOrderInvoice';
var IS_ALLOW_ASK_DOCTOR = 'Services/Doctors.svc/REST/GetPatientDoctorAppointmentResult';
var GET_CALL_REQUEST_TYPE = 'Services/Doctors.svc/REST/GetCallRequestType_LOV';
var ADD_VIDA_REQUEST = 'Services/ER_VirtualCall.svc/REST/PatientER_VidaRequestInseart';
@ -308,8 +297,6 @@ var GET_LIVECARE_CLINIC_TIMING = 'Services/ER_VirtualCall.svc/REST/PatientER_Get
var GET_ER_APPOINTMENT_FEES = 'Services/DoctorApplication.svc/REST/GetERAppointmentFees';
var GET_ER_APPOINTMENT_TIME = 'Services/ER_VirtualCall.svc/REST/GetRestTime';
var CHECK_PATIENT_DERMA_PACKAGE = 'Services/OUTPs.svc/REST/getPatientPackageComponentsForOnlineCheckIn';
var ADD_NEW_CALL_FOR_PATIENT_ER = 'Services/DoctorApplication.svc/REST/NewCallForPatientER';
var GET_LIVECARE_HISTORY = 'Services/ER_VirtualCall.svc/REST/GetPatientErVirtualHistory';
@ -721,6 +708,8 @@ const SAVE_SETTING = 'Services/Patients.svc/REST/UpdatePateintInfo';
const DEACTIVATE_ACCOUNT = 'Services/Patients.svc/REST/PatientAppleActivation_InsertUpdate';
var ER_CREATE_ADVANCE_PAYMENT = "services/Doctors.svc/REST/ER_CreateAdvancePaymentForClinic";
//family Files
const FAMILY_FILES = 'Services/Authentication.svc/REST/GetAllSharedRecordsByStatus';
@ -738,10 +727,6 @@ class ApiConsts {
static String RCBaseUrl = 'https://rc.hmg.com/'; // RC API URL PROD
static String SELECT_DEVICE_IMEI = 'Services/Patients.svc/REST/Patient_SELECTDeviceIMEIbyIMEI';
static num VERSION_ID = 18.9;
static var payFortEnvironment = FortEnvironment.production;
static var applePayMerchantId = "merchant.com.hmgwebservices";
@ -849,7 +834,19 @@ class ApiConsts {
static final String removeFileFromFamilyMembers = 'Services/Authentication.svc/REST/ActiveDeactive_PatientFile';
static final String acceptAndRejectFamilyFile = 'Services/Authentication.svc/REST/Update_FileStatus';
// static values for Api
// Ancillary Order Apis
static final String getOnlineAncillaryOrderList = 'Services/Doctors.svc/REST/GetOnlineAncillaryOrderList';
static final String getOnlineAncillaryOrderProcList = 'Services/Doctors.svc/REST/GetOnlineAncillaryOrderProcList';
static final String generateAncillaryOrderInvoice = 'Services/Doctors.svc/REST/AutoGenerateAncillaryOrderInvoice';
static final String autoGenerateAncillaryOrdersInvoice = 'Services/Doctors.svc/REST/AutoGenerateAncillaryOrderInvoice';
static final String getRequestStatusByRequestID = 'Services/PayFort_Serv.svc/REST/GetRequestStatusByRequestID';
//Payment APIs
static final String applePayInsertRequest = "Services/PayFort_Serv.svc/REST/PayFort_ApplePayRequestData_Insert";
static final String createAdvancePayments = 'Services/Patients.svc/REST/HIS_CreateAdvancePayment';
static final String addAdvanceNumberRequest = 'Services/PayFort_Serv.svc/REST/AddAdvancedNumberRequest';
// ************ static values for Api ****************
static final double appVersionID = 18.7;
static final int appChannelId = 3;
static final String appIpAddress = "10.20.10.20";

@ -35,6 +35,8 @@ import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_mo
import 'package:hmg_patient_app_new/features/profile_settings/profile_settings_view_model.dart';
import 'package:hmg_patient_app_new/features/radiology/radiology_repo.dart';
import 'package:hmg_patient_app_new/features/radiology/radiology_view_model.dart';
import 'package:hmg_patient_app_new/features/todo_section/todo_section_repo.dart';
import 'package:hmg_patient_app_new/features/todo_section/todo_section_view_model.dart';
import 'package:hmg_patient_app_new/services/analytics/analytics_service.dart';
import 'package:hmg_patient_app_new/services/cache_service.dart';
import 'package:hmg_patient_app_new/services/dialog_service.dart';
@ -44,7 +46,6 @@ import 'package:hmg_patient_app_new/services/localauth_service.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_view_model.dart';
import 'package:http/http.dart';
import 'package:local_auth/local_auth.dart';
import 'package:logger/web.dart';
import 'package:shared_preferences/shared_preferences.dart';
@ -102,11 +103,13 @@ class AppDependencies {
getIt.registerLazySingleton<PrescriptionsRepo>(() => PrescriptionsRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<InsuranceRepo>(() => InsuranceRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<PayfortRepo>(() => PayfortRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<LocalAuthService>(() => LocalAuthService(loggerService: getIt<LoggerService>(), localAuth: getIt<LocalAuthentication>()));
getIt.registerLazySingleton<LocalAuthService>(
() => LocalAuthService(loggerService: getIt<LoggerService>(), localAuth: getIt<LocalAuthentication>()));
getIt.registerLazySingleton<HabibWalletRepo>(() => HabibWalletRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<MedicalFileRepo>(() => MedicalFileRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<ImmediateLiveCareRepo>(() => ImmediateLiveCareRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<EmergencyServicesRepo>(() => EmergencyServicesRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<TodoSectionRepo>(() => TodoSectionRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<LocationRepo>(
() => LocationRepoImpl(apiClient: getIt()));
getIt.registerLazySingleton<ContactUsRepo>(() => ContactUsRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
@ -165,7 +168,13 @@ class AppDependencies {
);
getIt.registerLazySingleton<BookAppointmentsViewModel>(
() => BookAppointmentsViewModel(bookAppointmentsRepo: getIt(), errorHandlerService: getIt(), navigationService: getIt(), myAppointmentsViewModel: getIt(), locationUtils: getIt(), dialogService: getIt()),
() => BookAppointmentsViewModel(
bookAppointmentsRepo: getIt(),
errorHandlerService: getIt(),
navigationService: getIt(),
myAppointmentsViewModel: getIt(),
locationUtils: getIt(),
dialogService: getIt()),
);
getIt.registerLazySingleton<ImmediateLiveCareViewModel>(
@ -179,7 +188,13 @@ class AppDependencies {
getIt.registerLazySingleton<AuthenticationViewModel>(
() => AuthenticationViewModel(
authenticationRepo: getIt(), cacheService: getIt(), navigationService: getIt(), dialogService: getIt(), appState: getIt(), errorHandlerService: getIt(), localAuthService: getIt()),
authenticationRepo: getIt(),
cacheService: getIt(),
navigationService: getIt(),
dialogService: getIt(),
appState: getIt(),
errorHandlerService: getIt(),
localAuthService: getIt()),
);
getIt.registerLazySingleton<ProfileSettingsViewModel>(() => ProfileSettingsViewModel());
@ -192,8 +207,7 @@ class AppDependencies {
);
getIt.registerLazySingleton<AppointmentViaRegionViewmodel>(
() =>
AppointmentViaRegionViewmodel(
() => AppointmentViaRegionViewmodel(
navigationService: getIt(),
appState: getIt(),
),
@ -226,6 +240,13 @@ class AppDependencies {
),
);
getIt.registerLazySingleton<TodoSectionViewModel>(
() => TodoSectionViewModel(
todoSectionRepo: getIt(),
errorHandlerService: getIt(),
),
);
// Screen-specific VMs Factory
// getIt.registerFactory<BookAppointmentsViewModel>(
// () => BookAppointmentsViewModel(

@ -1,3 +1,5 @@
import 'dart:developer';
import 'package:easy_localization/easy_localization.dart';
import 'package:hijri_gregorian_calendar/hijri_gregorian_calendar.dart';
import 'package:hmg_patient_app_new/core/api_consts.dart';
@ -96,6 +98,7 @@ class RequestUtils {
request.patientIdentificationID = request.nationalID = (registeredData.patientIdentificationId ?? 0);
request.dob = registeredData.dob;
request.isRegister = registeredData.isRegister;
log("nationIdText: ${nationIdText}");
} else {
if (fileNo) {
request.patientID = patientId ?? int.parse(nationIdText);
@ -199,7 +202,8 @@ class RequestUtils {
return request;
}
static dynamic getUserSignupCompletionRequest({String? fullName, String? emailAddress, GenderTypeEnum? gender, MaritalStatusTypeEnum? maritalStatus}) {
static dynamic getUserSignupCompletionRequest(
{String? fullName, String? emailAddress, GenderTypeEnum? gender, MaritalStatusTypeEnum? maritalStatus}) {
AppState appState = getIt.get<AppState>();
bool isDubai = appState.getUserRegistrationPayload.patientOutSa == 1 ? true : false;
@ -215,11 +219,19 @@ class RequestUtils {
return {
"Patientobject": {
"TempValue": true,
"PatientIdentificationType":
(isDubai ? appState.getUserRegistrationPayload.patientIdentificationId?.toString().substring(0, 1) : appState.getNHICUserData.idNumber!.substring(0, 1)) == "1" ? 1 : 2,
"PatientIdentificationNo": isDubai ? appState.getUserRegistrationPayload.patientIdentificationId.toString() : appState.getNHICUserData.idNumber.toString(),
"PatientIdentificationType": (isDubai
? appState.getUserRegistrationPayload.patientIdentificationId?.toString().substring(0, 1)
: appState.getNHICUserData.idNumber!.substring(0, 1)) ==
"1"
? 1
: 2,
"PatientIdentificationNo":
isDubai ? appState.getUserRegistrationPayload.patientIdentificationId.toString() : appState.getNHICUserData.idNumber.toString(),
"MobileNumber": appState.getUserRegistrationPayload.patientMobileNumber ?? 0,
"PatientOutSA": (appState.getUserRegistrationPayload.zipCode == CountryEnum.saudiArabia.countryCode || appState.getUserRegistrationPayload.zipCode == '+966') ? 0 : 1,
"PatientOutSA": (appState.getUserRegistrationPayload.zipCode == CountryEnum.saudiArabia.countryCode ||
appState.getUserRegistrationPayload.zipCode == '+966')
? 0
: 1,
"FirstNameN": isDubai ? "..." : appState.getNHICUserData.firstNameAr,
"FirstName": isDubai ? (names.isNotEmpty ? names[0] : "...") : appState.getNHICUserData.firstNameEn,
"MiddleNameN": isDubai ? "..." : appState.getNHICUserData.secondNameAr,
@ -233,7 +245,10 @@ class RequestUtils {
"eHealthIDField": isDubai ? null : appState.getNHICUserData.healthId,
"DateofBirthN": date,
"EmailAddress": emailAddress,
"SourceType": (appState.getUserRegistrationPayload.zipCode == CountryEnum.saudiArabia.countryCode || appState.getUserRegistrationPayload.zipCode == '+966') ? "1" : "2",
"SourceType": (appState.getUserRegistrationPayload.zipCode == CountryEnum.saudiArabia.countryCode ||
appState.getUserRegistrationPayload.zipCode == '+966')
? "1"
: "2",
"PreferredLanguage": appState.getLanguageCode() == "ar" ? (isDubai ? "1" : 1) : (isDubai ? "2" : 2),
"Marital": isDubai
? (maritalStatus == MaritalStatusTypeEnum.single
@ -247,20 +262,25 @@ class RequestUtils {
? '1'
: '2'),
},
"PatientIdentificationID": isDubai ? appState.getUserRegistrationPayload.patientIdentificationId.toString() : appState.getNHICUserData.idNumber.toString(),
"PatientIdentificationID":
isDubai ? appState.getUserRegistrationPayload.patientIdentificationId.toString() : appState.getNHICUserData.idNumber.toString(),
"PatientMobileNumber": appState.getUserRegistrationPayload.patientMobileNumber.toString()[0] == '0'
? appState.getUserRegistrationPayload.patientMobileNumber
: '0${appState.getUserRegistrationPayload.patientMobileNumber}',
"DOB": dob,
"IsHijri": appState.getUserRegistrationPayload.isHijri,
"PatientOutSA": (appState.getUserRegistrationPayload.zipCode == CountryEnum.saudiArabia.countryCode || appState.getUserRegistrationPayload.zipCode == '+966') ? 0 : 1,
"PatientOutSA": (appState.getUserRegistrationPayload.zipCode == CountryEnum.saudiArabia.countryCode ||
appState.getUserRegistrationPayload.zipCode == '+966')
? 0
: 1,
"isDentalAllowedBackend": appState.getUserRegistrationPayload.isDentalAllowedBackend,
"ZipCode": appState.getUserRegistrationPayload.zipCode,
if (!isDubai) "HealthId": appState.getNHICUserData.healthId,
};
}
static Future<FamilyFileRequest> getAddFamilyRequest({required String nationalIDorFile, required String mobileNo, required String countryCode}) async {
static Future<FamilyFileRequest> getAddFamilyRequest(
{required String nationalIDorFile, required String mobileNo, required String countryCode}) async {
FamilyFileRequest request = FamilyFileRequest();
int? loginType = 0;

@ -323,7 +323,8 @@ class Utils {
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(height: isSmallWidget ? 0.h : 48.h),
Lottie.asset(AppAnimations.noData, repeat: false, reverse: false, frameRate: FrameRate(60), width: width.h, height: height.h, fit: BoxFit.fill),
Lottie.asset(AppAnimations.noData,
repeat: false, reverse: false, frameRate: FrameRate(60), width: width.h, height: height.h, fit: BoxFit.fill),
SizedBox(height: 16.h),
(noDataText ?? LocaleKeys.noDataAvailable.tr())
.toText16(weight: FontWeight.w500, color: AppColors.greyTextColor, isCenter: true)
@ -339,7 +340,8 @@ class Utils {
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Lottie.asset(AppAnimations.loadingAnimation, repeat: true, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill),
Lottie.asset(AppAnimations.loadingAnimation,
repeat: true, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill),
SizedBox(height: 8.h),
(loadingText ?? LocaleKeys.loadingText.tr()).toText16(color: AppColors.blackColor, isCenter: true),
SizedBox(height: 8.h),
@ -365,7 +367,8 @@ class Utils {
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Lottie.asset(AppAnimations.errorAnimation, repeat: true, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill),
Lottie.asset(AppAnimations.errorAnimation,
repeat: true, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill),
SizedBox(height: 8.h),
(loadingText ?? LocaleKeys.loadingText.tr()).toText16(color: AppColors.blackColor),
SizedBox(height: 8.h),
@ -373,12 +376,14 @@ 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),
Lottie.asset(AppAnimations.warningAnimation,
repeat: false, reverse: false, frameRate: FrameRate(60), width: 128.h, height: 128.h, fit: BoxFit.fill),
SizedBox(height: 8.h),
(loadingText ?? LocaleKeys.loadingText.tr()).toText14(color: AppColors.blackColor, letterSpacing: 0),
SizedBox(height: 16.h),
@ -698,7 +703,7 @@ class Utils {
fit: fit,
errorBuilder: errorBuilder ??
(_, __, ___) {
//todo change the error builder icon that it is returning
//todo_section change the error builder icon that it is returning
return Utils.buildSvgWithAssets(width: iconW, height: iconH, icon: AppAssets.no_visit_icon);
},
);
@ -816,7 +821,7 @@ class Utils {
static Future<String> createFileFromString(String encodedStr, String ext) async {
Uint8List bytes = base64.decode(encodedStr);
String dir = (await getApplicationDocumentsDirectory()).path;
File file = File("$dir/" + DateTime.now().millisecondsSinceEpoch.toString() + "." + ext);
File file = File("$dir/${DateTime.now().millisecondsSinceEpoch}.$ext");
await file.writeAsBytes(bytes);
return file.path;
}

@ -339,7 +339,7 @@ class AuthenticationViewModel extends ChangeNotifier {
_navigationService.pop();
});
},
activationCode: null, //todo silent login case halded on the repo itself..
activationCode: null, //todo_section silent login case halded on the repo itself..
);
}
}

@ -1,3 +1,5 @@
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';
@ -26,10 +28,15 @@ abstract class EmergencyServicesRepo {
Future<Either<Failure, GenericApiModel<EROnlineCheckInPaymentDetailsResponse>>> checkPatientERPaymentInformation({int projectID});
Future<Either<Failure, GenericApiModel<dynamic>>> ER_CreateAdvancePayment(
{required int projectID, required AuthenticatedUser authUser, required num paymentAmount, required String paymentMethodName, required String paymentReference});
Future<Either<Failure, GenericApiModel<dynamic>>> createAdvancePaymentForER(
{required int projectID,
required AuthenticatedUser authUser,
required num paymentAmount,
required String paymentMethodName,
required String paymentReference});
Future<Either<Failure, GenericApiModel<dynamic>>> addAdvanceNumberRequest({required String advanceNumber, required String paymentReference, required String appointmentNo});
Future<Either<Failure, GenericApiModel<dynamic>>> addAdvanceNumberRequest(
{required String advanceNumber, required String paymentReference, required String appointmentNo});
Future<Either<Failure, GenericApiModel<dynamic>>> getProjectIDFromNFC({required String nfcCode});
@ -68,7 +75,8 @@ class EmergencyServicesRepoImp implements EmergencyServicesRepo {
try {
final list = response['List_ProjectAvgERWaitingTime'];
final clinicsList = list.map((item) => ProjectAvgERWaitingTime.fromJson(item as Map<String, dynamic>)).toList().cast<ProjectAvgERWaitingTime>();
final clinicsList =
list.map((item) => ProjectAvgERWaitingTime.fromJson(item as Map<String, dynamic>)).toList().cast<ProjectAvgERWaitingTime>();
apiResponse = GenericApiModel<List<ProjectAvgERWaitingTime>>(
messageStatus: messageStatus,
statusCode: statusCode,
@ -104,7 +112,8 @@ class EmergencyServicesRepoImp implements EmergencyServicesRepo {
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response['Vida_ProcedureList'];
final proceduresList = list.map((item) => RRTProceduresResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<RRTProceduresResponseModel>();
final proceduresList =
list.map((item) => RRTProceduresResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<RRTProceduresResponseModel>();
apiResponse = GenericApiModel<List<RRTProceduresResponseModel>>(
messageStatus: messageStatus,
@ -381,8 +390,12 @@ class EmergencyServicesRepoImp implements EmergencyServicesRepo {
}
@override
Future<Either<Failure, GenericApiModel>> ER_CreateAdvancePayment(
{required int projectID, required AuthenticatedUser authUser, required num paymentAmount, required String paymentMethodName, required String paymentReference}) async {
Future<Either<Failure, GenericApiModel>> createAdvancePaymentForER(
{required int projectID,
required AuthenticatedUser authUser,
required num paymentAmount,
required String paymentMethodName,
required String paymentReference}) async {
Map<String, dynamic> mapDevice = {
"LanguageID": 1,
"ERAdvanceAmount": {
@ -412,7 +425,7 @@ class EmergencyServicesRepoImp implements EmergencyServicesRepo {
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final vidaAdvanceNumber = response['ER_AdvancePaymentResponse']['AdvanceNumber'].toString();
print(vidaAdvanceNumber);
log(vidaAdvanceNumber);
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
@ -433,7 +446,8 @@ class EmergencyServicesRepoImp implements EmergencyServicesRepo {
}
@override
Future<Either<Failure, GenericApiModel>> addAdvanceNumberRequest({required String advanceNumber, required String paymentReference, required String appointmentNo}) async {
Future<Either<Failure, GenericApiModel>> addAdvanceNumberRequest(
{required String advanceNumber, required String paymentReference, required String appointmentNo}) async {
Map<String, dynamic> requestBody = {
"AdvanceNumber": advanceNumber,
"AdvanceNumber_VP": advanceNumber,

@ -232,7 +232,7 @@ class EmergencyServicesViewModel extends ChangeNotifier {
}
handleGMSMapCameraMoved(GMSMapServices.CameraPosition value) {
//todo handle the camera moved position for GMS devices
//todo_section handle the camera moved position for GMS devices
}
HMSCameraServices.CameraPosition getHMSLocation() {
@ -240,7 +240,7 @@ class EmergencyServicesViewModel extends ChangeNotifier {
}
handleHMSMapCameraMoved(HMSCameraServices.CameraPosition value) {
//todo handle the camera moved position for HMS devices
//todo_section handle the camera moved position for HMS devices
}
void navigateTOAmbulancePage() {
@ -351,7 +351,7 @@ class EmergencyServicesViewModel extends ChangeNotifier {
}
Future<void> ER_CreateAdvancePayment({required String paymentMethodName, required String paymentReference, Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await emergencyServicesRepo.ER_CreateAdvancePayment(
final result = await emergencyServicesRepo.createAdvancePaymentForER(
projectID: selectedHospital!.iD,
authUser: appState.getAuthenticatedUser()!,
paymentAmount: erOnlineCheckInPaymentDetailsResponse.patientShareWithTax!,

@ -122,7 +122,7 @@ class AppointmentViaRegionViewmodel extends ChangeNotifier {
}
void handleLastStepForDentalAndLaser() {
//todo handle the routing here
//todo_section handle the routing here
navigationService.pop();
navigationService.push(
CustomPageRoute(

@ -1,3 +1,5 @@
import 'dart:developer';
import 'package:amazon_payfort/amazon_payfort.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/api_consts.dart';
@ -40,7 +42,8 @@ class PayfortViewModel extends ChangeNotifier {
notifyListeners();
}
Future<void> getPayfortConfigurations({int? serviceId, int? projectId, int integrationId = 2, Function(dynamic)? onSuccess, Function(String)? onError}) async {
Future<void> getPayfortConfigurations(
{int? serviceId, int? projectId, int integrationId = 2, Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await payfortRepo.getPayfortConfigurations(serviceId: serviceId, projectId: projectId, integrationId: integrationId);
result.fold(
@ -60,7 +63,8 @@ class PayfortViewModel extends ChangeNotifier {
);
}
Future<void> applePayRequestInsert({required ApplePayInsertRequest applePayInsertRequest, Function(dynamic)? onSuccess, Function(String)? onError}) async {
Future<void> applePayRequestInsert(
{required ApplePayInsertRequest applePayInsertRequest, Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await payfortRepo.applePayRequestInsert(applePayInsertRequest: applePayInsertRequest);
result.fold(
@ -106,7 +110,7 @@ class PayfortViewModel extends ChangeNotifier {
onError!(failure.message);
},
(apiResponse) {
print(apiResponse.data);
log(apiResponse.data);
if (onSuccess != null) {
onSuccess(apiResponse);
}
@ -116,15 +120,21 @@ class PayfortViewModel extends ChangeNotifier {
}
Future<void> updateTamaraRequestStatus(
{required String responseMessage, required String status, required String clientRequestID, required String tamaraOrderID, Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await payfortRepo.updateTamaraRequestStatus(responseMessage: responseMessage, status: status, clientRequestID: clientRequestID, tamaraOrderID: tamaraOrderID);
{required String responseMessage,
required String status,
required String clientRequestID,
required String tamaraOrderID,
Function(dynamic)? onSuccess,
Function(String)? onError}) async {
final result = await payfortRepo.updateTamaraRequestStatus(
responseMessage: responseMessage, status: status, clientRequestID: clientRequestID, tamaraOrderID: tamaraOrderID);
result.fold(
(failure) async {
onError!(failure.message);
},
(apiResponse) {
print(apiResponse.data);
log(apiResponse.data);
if (onSuccess != null) {
onSuccess(apiResponse);
}
@ -138,7 +148,7 @@ class PayfortViewModel extends ChangeNotifier {
String? applePayShaType,
String? applePayShaRequestPhrase,
}) async {
var sdkTokenResponse;
SdkTokenResponse? sdkTokenResponse;
try {
String? deviceId = await _payfort.getDeviceId();
@ -172,7 +182,7 @@ class PayfortViewModel extends ChangeNotifier {
},
);
} catch (e) {
print("Error here: ${e.toString()}");
log("Error here: ${e.toString()}");
}
return sdkTokenResponse;
}
@ -238,7 +248,8 @@ class PayfortViewModel extends ChangeNotifier {
}
}
Future<void> markAppointmentAsTamaraPaid({required int projectID, required int appointmentNo, Function(dynamic)? onSuccess, Function(String)? onError}) async {
Future<void> markAppointmentAsTamaraPaid(
{required int projectID, required int appointmentNo, Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await payfortRepo.markAppointmentAsTamaraPaid(projectID: projectID, appointmentNo: appointmentNo);
result.fold(
@ -246,7 +257,7 @@ class PayfortViewModel extends ChangeNotifier {
onError!(failure.message);
},
(apiResponse) {
print(apiResponse.data);
log(apiResponse.data);
if (onSuccess != null) {
onSuccess(apiResponse);
}

@ -9,11 +9,12 @@ import 'package:hmg_patient_app_new/features/radiology/models/resp_models/patien
import 'package:hmg_patient_app_new/services/logger_service.dart';
abstract class RadiologyRepo {
Future<Either<Failure, GenericApiModel<List<PatientRadiologyResponseModel>>>> getPatientRadiologyOrders({required String patientId});
Future<Either<Failure, GenericApiModel<List<PatientRadiologyResponseModel>>>> getPatientRadiologyOrders();
Future<Either<Failure, GenericApiModel<String>>> getRadiologyImage({required PatientRadiologyResponseModel patientRadiologyResponseModel});
Future<Either<Failure, GenericApiModel<String>>> getRadiologyReportPDF({required PatientRadiologyResponseModel patientRadiologyResponseModel, required AuthenticatedUser authenticatedUser});
Future<Either<Failure, GenericApiModel<String>>> getRadiologyReportPDF(
{required PatientRadiologyResponseModel patientRadiologyResponseModel, required AuthenticatedUser authenticatedUser});
}
class RadiologyRepoImp implements RadiologyRepo {
@ -23,7 +24,7 @@ class RadiologyRepoImp implements RadiologyRepo {
RadiologyRepoImp({required this.loggerService, required this.apiClient});
@override
Future<Either<Failure, GenericApiModel<List<PatientRadiologyResponseModel>>>> getPatientRadiologyOrders({required String patientId}) async {
Future<Either<Failure, GenericApiModel<List<PatientRadiologyResponseModel>>>> getPatientRadiologyOrders() async {
Map<String, dynamic> mapDevice = {};
try {
@ -40,10 +41,16 @@ class RadiologyRepoImp implements RadiologyRepo {
try {
if (response['FinalRadiologyList'] != null && response['FinalRadiologyList'].length != 0) {
final list = response['FinalRadiologyList'];
radOrders = list.map((item) => PatientRadiologyResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<PatientRadiologyResponseModel>();
radOrders = list
.map((item) => PatientRadiologyResponseModel.fromJson(item as Map<String, dynamic>))
.toList()
.cast<PatientRadiologyResponseModel>();
} else {
final list = response['FinalRadiologyListAPI'];
radOrders = list.map((item) => PatientRadiologyResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<PatientRadiologyResponseModel>();
radOrders = list
.map((item) => PatientRadiologyResponseModel.fromJson(item as Map<String, dynamic>))
.toList()
.cast<PatientRadiologyResponseModel>();
}
apiResponse = GenericApiModel<List<PatientRadiologyResponseModel>>(
@ -107,7 +114,8 @@ class RadiologyRepoImp implements RadiologyRepo {
}
@override
Future<Either<Failure, GenericApiModel<String>>> getRadiologyReportPDF({required PatientRadiologyResponseModel patientRadiologyResponseModel, required AuthenticatedUser authenticatedUser}) async {
Future<Either<Failure, GenericApiModel<String>>> getRadiologyReportPDF(
{required PatientRadiologyResponseModel patientRadiologyResponseModel, required AuthenticatedUser authenticatedUser}) async {
Map<String, dynamic> mapDevice = {
"InvoiceNo": Utils.isVidaPlusProject(patientRadiologyResponseModel.projectID!) ? 0 : patientRadiologyResponseModel.invoiceNo,
"InvoiceNo_VP": Utils.isVidaPlusProject(patientRadiologyResponseModel.projectID!) ? patientRadiologyResponseModel.invoiceNo : 0,
@ -121,7 +129,8 @@ class RadiologyRepoImp implements RadiologyRepo {
'ClinicName': patientRadiologyResponseModel.clinicDescription,
'DateofBirth': authenticatedUser.dateofBirth,
'DoctorName': patientRadiologyResponseModel.doctorName,
'OrderDate': '${patientRadiologyResponseModel.orderDate!.year}-${patientRadiologyResponseModel.orderDate!.month}-${patientRadiologyResponseModel.orderDate!.day}',
'OrderDate':
'${patientRadiologyResponseModel.orderDate!.year}-${patientRadiologyResponseModel.orderDate!.month}-${patientRadiologyResponseModel.orderDate!.day}',
'PatientIditificationNum': authenticatedUser.patientIdentificationNo,
'PatientMobileNumber': authenticatedUser.mobileNumber,
'PatientName': "${authenticatedUser.firstName!} ${authenticatedUser.lastName!}",

@ -19,7 +19,7 @@ class RadiologyViewModel extends ChangeNotifier {
RadiologyViewModel({required this.radiologyRepo, required this.errorHandlerService});
initRadiologyProvider() {
initRadiologyViewModel() {
patientRadiologyOrders.clear();
isRadiologyOrdersLoading = true;
isRadiologyPDFReportLoading = true;
@ -29,7 +29,7 @@ class RadiologyViewModel extends ChangeNotifier {
}
Future<void> getPatientRadiologyOrders({Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await radiologyRepo.getPatientRadiologyOrders(patientId: "1231755");
final result = await radiologyRepo.getPatientRadiologyOrders();
result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure),
@ -48,7 +48,8 @@ class RadiologyViewModel extends ChangeNotifier {
);
}
Future<void> getRadiologyImage({required PatientRadiologyResponseModel patientRadiologyResponseModel, Function(dynamic)? onSuccess, Function(String)? onError}) async {
Future<void> getRadiologyImage(
{required PatientRadiologyResponseModel patientRadiologyResponseModel, Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await radiologyRepo.getRadiologyImage(patientRadiologyResponseModel: patientRadiologyResponseModel);
result.fold(
@ -68,8 +69,12 @@ class RadiologyViewModel extends ChangeNotifier {
}
Future<void> getRadiologyPDF(
{required PatientRadiologyResponseModel patientRadiologyResponseModel, required AuthenticatedUser authenticatedUser, Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await radiologyRepo.getRadiologyReportPDF(patientRadiologyResponseModel: patientRadiologyResponseModel, authenticatedUser: authenticatedUser);
{required PatientRadiologyResponseModel patientRadiologyResponseModel,
required AuthenticatedUser authenticatedUser,
Function(dynamic)? onSuccess,
Function(String)? onError}) async {
final result =
await radiologyRepo.getRadiologyReportPDF(patientRadiologyResponseModel: patientRadiologyResponseModel, authenticatedUser: authenticatedUser);
result.fold(
(failure) async => await errorHandlerService.handleError(

@ -0,0 +1,115 @@
// Dart model for the "AncillaryOrderList" structure
// Uses DateUtil.convertStringToDate and DateUtil.dateToDotNetString from your project to parse/serialize .NET-style dates.
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
class AncillaryOrderListModel {
List<AncillaryOrderGroup>? ancillaryOrderList;
AncillaryOrderListModel({this.ancillaryOrderList});
factory AncillaryOrderListModel.fromJson(Map<String, dynamic> json) => AncillaryOrderListModel(
ancillaryOrderList: json['AncillaryOrderList'] != null
? List<AncillaryOrderGroup>.from(
(json['AncillaryOrderList'] as List).map(
(x) => AncillaryOrderGroup.fromJson(x as Map<String, dynamic>),
),
)
: null,
);
}
class AncillaryOrderGroup {
List<AncillaryOrderItem>? ancillaryOrderList;
dynamic errCode;
String? message;
int? patientID;
String? patientName;
int? patientType;
int? projectID;
String? projectName;
String? setupID;
int? statusCode;
AncillaryOrderGroup({
this.ancillaryOrderList,
this.errCode,
this.message,
this.patientID,
this.patientName,
this.patientType,
this.projectID,
this.projectName,
this.setupID,
this.statusCode,
});
factory AncillaryOrderGroup.fromJson(Map<String, dynamic> json) => AncillaryOrderGroup(
ancillaryOrderList: json['AncillaryOrderList'] != null
? List<AncillaryOrderItem>.from(
(json['AncillaryOrderList'] as List).map(
(x) => AncillaryOrderItem.fromJson(x as Map<String, dynamic>),
),
)
: null,
errCode: json['ErrCode'],
message: json['Message'] as String?,
patientID: json['PatientID'] as int?,
patientName: json['PatientName'] as String?,
patientType: json['PatientType'] as int?,
projectID: json['ProjectID'] as int?,
projectName: json['ProjectName'] as String?,
setupID: json['SetupID'] as String?,
statusCode: json['StatusCode'] as int?,
);
}
class AncillaryOrderItem {
dynamic ancillaryProcedureListModels;
DateTime? appointmentDate;
int? appointmentNo;
int? clinicID;
String? clinicName;
int? doctorID;
String? doctorName;
int? invoiceNo;
bool? isCheckInAllow;
bool? isQueued;
DateTime? orderDate;
int? orderNo;
String? projectName; // Added from parent AncillaryOrderGroup
int? projectID; // Added from parent AncillaryOrderGroup
AncillaryOrderItem({
this.ancillaryProcedureListModels,
this.appointmentDate,
this.appointmentNo,
this.clinicID,
this.clinicName,
this.doctorID,
this.doctorName,
this.invoiceNo,
this.isCheckInAllow,
this.isQueued,
this.orderDate,
this.orderNo,
this.projectName,
this.projectID,
});
factory AncillaryOrderItem.fromJson(Map<String, dynamic> json, {String? projectName, int? projectID}) => AncillaryOrderItem(
ancillaryProcedureListModels: json['AncillaryProcedureListModels'],
appointmentDate: DateUtil.convertStringToDate(json['AppointmentDate']),
appointmentNo: json['AppointmentNo'] as int?,
clinicID: json['ClinicID'] as int?,
clinicName: json['ClinicName'] as String?,
doctorID: json['DoctorID'] as int?,
doctorName: json['DoctorName'] as String?,
invoiceNo: json['Invoiceno'] as int?,
isCheckInAllow: json['IsCheckInAllow'] as bool?,
isQueued: json['IsQueued'] as bool?,
orderDate: DateUtil.convertStringToDate(json['OrderDate']),
orderNo: json['OrderNo'] as int?,
projectName: projectName,
projectID: projectID,
);
}

@ -0,0 +1,221 @@
// Dart model classes for "AncillaryOrderProcList"
// Generated for user: faizatflutter
// Uses DateUtil.convertStringToDate for parsing .NET-style dates (same approach as your PatientRadiologyResponseModel)
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
class AncillaryOrderProcListModel {
List<AncillaryOrderProcedureItem>? ancillaryOrderProcList;
AncillaryOrderProcListModel({this.ancillaryOrderProcList});
factory AncillaryOrderProcListModel.fromJson(Map<String, dynamic> json) => AncillaryOrderProcListModel(
ancillaryOrderProcList: json['AncillaryOrderProcList'] != null
? List<AncillaryOrderProcedureItem>.from(
(json['AncillaryOrderProcList'] as List).map(
(x) => AncillaryOrderProcedureItem.fromJson(x as Map<String, dynamic>),
),
)
: null,
);
}
class AncillaryOrderProcedureItem {
List<AncillaryOrderProcDetail>? ancillaryOrderProcDetailsList;
DateTime? appointmentDate;
int? appointmentNo;
int? clinicID;
String? clinicName;
int? companyID;
String? companyName;
int? doctorID;
String? doctorName;
dynamic errCode;
int? groupID;
String? insurancePolicyNo;
String? message;
String? patientCardID;
int? patientID;
String? patientName;
int? patientType;
int? policyID;
String? policyName;
int? projectID;
String? setupID;
int? statusCode;
int? subCategoryID;
String? subPolicyNo;
AncillaryOrderProcedureItem({
this.ancillaryOrderProcDetailsList,
this.appointmentDate,
this.appointmentNo,
this.clinicID,
this.clinicName,
this.companyID,
this.companyName,
this.doctorID,
this.doctorName,
this.errCode,
this.groupID,
this.insurancePolicyNo,
this.message,
this.patientCardID,
this.patientID,
this.patientName,
this.patientType,
this.policyID,
this.policyName,
this.projectID,
this.setupID,
this.statusCode,
this.subCategoryID,
this.subPolicyNo,
});
factory AncillaryOrderProcedureItem.fromJson(Map<String, dynamic> json) => AncillaryOrderProcedureItem(
ancillaryOrderProcDetailsList: json['AncillaryOrderProcDetailsList'] != null
? List<AncillaryOrderProcDetail>.from(
(json['AncillaryOrderProcDetailsList'] as List).map(
(x) => AncillaryOrderProcDetail.fromJson(x as Map<String, dynamic>),
),
)
: null,
appointmentDate: DateUtil.convertStringToDate(json['AppointmentDate']),
appointmentNo: json['AppointmentNo'] as int?,
clinicID: json['ClinicID'] as int?,
clinicName: json['ClinicName'] as String?,
companyID: json['CompanyID'] as int?,
companyName: json['CompanyName'] as String?,
doctorID: json['DoctorID'] as int?,
doctorName: json['DoctorName'] as String?,
errCode: json['ErrCode'],
groupID: json['GroupID'] as int?,
insurancePolicyNo: json['InsurancePolicyNo'] as String?,
message: json['Message'] as String?,
patientCardID: json['PatientCardID'] as String?,
patientID: json['PatientID'] as int?,
patientName: json['PatientName'] as String?,
patientType: json['PatientType'] as int?,
policyID: json['PolicyID'] as int?,
policyName: json['PolicyName'] as String?,
projectID: json['ProjectID'] as int?,
setupID: json['SetupID'] as String?,
statusCode: json['StatusCode'] as int?,
subCategoryID: json['SubCategoryID'] as int?,
subPolicyNo: json['SubPolicyNo'] as String?,
);
}
class AncillaryOrderProcDetail {
int? approvalLineItemNo;
int? approvalNo;
String? approvalStatus;
int? approvalStatusID;
num? companyShare;
num? companyShareWithTax;
num? companyTaxAmount;
num? discountAmount;
int? discountCategory;
String? discountType;
num? discountTypeValue;
bool? isApprovalCreated;
bool? isApprovalRequired;
dynamic isCheckInAllow;
bool? isCovered;
bool? isLab;
DateTime? orderDate;
int? orderLineItemNo;
int? orderNo;
int? partnerID;
num? partnerShare;
String? partnerShareType;
num? patientShare;
num? patientShareWithTax;
num? patientTaxAmount;
num? procPrice;
int? procedureCategoryID;
String? procedureCategoryName;
String? procedureID;
String? procedureName;
num? taxAmount;
num? taxPct;
AncillaryOrderProcDetail({
this.approvalLineItemNo,
this.approvalNo,
this.approvalStatus,
this.approvalStatusID,
this.companyShare,
this.companyShareWithTax,
this.companyTaxAmount,
this.discountAmount,
this.discountCategory,
this.discountType,
this.discountTypeValue,
this.isApprovalCreated,
this.isApprovalRequired,
this.isCheckInAllow,
this.isCovered,
this.isLab,
this.orderDate,
this.orderLineItemNo,
this.orderNo,
this.partnerID,
this.partnerShare,
this.partnerShareType,
this.patientShare,
this.patientShareWithTax,
this.patientTaxAmount,
this.procPrice,
this.procedureCategoryID,
this.procedureCategoryName,
this.procedureID,
this.procedureName,
this.taxAmount,
this.taxPct,
});
factory AncillaryOrderProcDetail.fromJson(Map<String, dynamic> json) => AncillaryOrderProcDetail(
approvalLineItemNo: json['ApprovalLineItemNo'] as int?,
approvalNo: json['ApprovalNo'] as int?,
approvalStatus: json['ApprovalStatus'] as String?,
approvalStatusID: json['ApprovalStatusID'] as int?,
companyShare: _toNum(json['CompanyShare']),
companyShareWithTax: _toNum(json['CompanyShareWithTax']),
companyTaxAmount: _toNum(json['CompanyTaxAmount']),
discountAmount: _toNum(json['DiscountAmount']),
discountCategory: json['DiscountCategory'] as int?,
discountType: json['DiscountType'] as String?,
discountTypeValue: _toNum(json['DiscountTypeValue']),
isApprovalCreated: json['IsApprovalCreated'] as bool?,
isApprovalRequired: json['IsApprovalRequired'] as bool?,
isCheckInAllow: json['IsCheckInAllow'],
isCovered: json['IsCovered'] as bool?,
isLab: json['IsLab'] as bool?,
orderDate: DateUtil.convertStringToDate(json['OrderDate']),
orderLineItemNo: json['OrderLineItemNo'] as int?,
orderNo: json['OrderNo'] as int?,
partnerID: json['PartnerID'] as int?,
partnerShare: _toNum(json['PartnerShare']),
partnerShareType: json['PartnerShareType'] as String?,
patientShare: _toNum(json['PatientShare']),
patientShareWithTax: _toNum(json['PatientShareWithTax']),
patientTaxAmount: _toNum(json['PatientTaxAmount']),
procPrice: _toNum(json['ProcPrice']),
procedureCategoryID: json['ProcedureCategoryID'] as int?,
procedureCategoryName: json['ProcedureCategoryName'] as String?,
procedureID: json['ProcedureID'] as String?,
procedureName: json['ProcedureName'] as String?,
taxAmount: _toNum(json['TaxAmount']),
taxPct: _toNum(json['TaxPct']),
);
}
// Helper to safely parse numeric fields that may be int/double/string/null
num? _toNum(dynamic v) {
if (v == null) return null;
if (v is num) return v;
if (v is String) return num.tryParse(v);
return null;
}

@ -0,0 +1,379 @@
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/todo_section/models/resp_models/ancillary_order_list_response_model.dart';
import 'package:hmg_patient_app_new/features/todo_section/models/resp_models/ancillary_order_procedures_detail_response_model.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
abstract class TodoSectionRepo {
Future<Either<Failure, GenericApiModel<List<AncillaryOrderItem>>>> getOnlineAncillaryOrderList();
Future<Either<Failure, GenericApiModel<List<AncillaryOrderProcedureItem>>>> getOnlineAncillaryOrderDetailsProceduresList({
required int appointmentNoVida,
required int orderNo,
required int projectID,
});
Future<Either<Failure, dynamic>> checkPaymentStatus({required String transID});
Future<Either<Failure, dynamic>> createAdvancePayment({
required int projectID,
required double paymentAmount,
required String paymentReference,
required String paymentMethodName,
required int patientTypeID,
required String patientName,
required int patientID,
required String setupID,
required bool isAncillaryOrder,
});
Future<Either<Failure, dynamic>> addAdvancedNumberRequest({
required String advanceNumber,
required String paymentReference,
required int appointmentID,
required int patientID,
required int patientTypeID,
required int patientOutSA,
});
Future<Either<Failure, dynamic>> autoGenerateAncillaryOrdersInvoice({
required int orderNo,
required int projectID,
required int appointmentNo,
required List<dynamic> selectedProcedures,
required int languageID,
});
Future<Either<Failure, dynamic>> applePayInsertRequest({required dynamic applePayInsertRequest});
}
class TodoSectionRepoImp implements TodoSectionRepo {
final ApiClient apiClient;
final LoggerService loggerService;
TodoSectionRepoImp({required this.loggerService, required this.apiClient});
@override
Future<Either<Failure, GenericApiModel<List<AncillaryOrderItem>>>> getOnlineAncillaryOrderList() async {
Map<String, dynamic> mapDevice = {};
try {
GenericApiModel<List<AncillaryOrderItem>>? apiResponse;
Failure? failure;
await apiClient.post(
ApiConsts.getOnlineAncillaryOrderList,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
List<AncillaryOrderItem> ancillaryOrders = [];
// Parse the nested structure
if (response['AncillaryOrderList'] != null && response['AncillaryOrderList'] is List) {
final groupsList = response['AncillaryOrderList'] as List;
// Iterate through each group
for (var group in groupsList) {
if (group is Map<String, dynamic> && group['AncillaryOrderList'] != null) {
final ordersList = group['AncillaryOrderList'] as List;
final projectName = group['ProjectName'] as String?;
final projectID = group['ProjectID'] as int?;
// Parse each order item in the group
for (var orderJson in ordersList) {
if (orderJson is Map<String, dynamic>) {
ancillaryOrders.add(AncillaryOrderItem.fromJson(orderJson, projectName: projectName, projectID: projectID));
}
}
}
}
}
apiResponse = GenericApiModel<List<AncillaryOrderItem>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: ancillaryOrders,
);
} catch (e) {
loggerService.logInfo("Error parsing ancillary 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 getOnlineAncillaryOrderList: ${e.toString()}");
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<List<AncillaryOrderProcedureItem>>>> getOnlineAncillaryOrderDetailsProceduresList({
required int appointmentNoVida,
required int orderNo,
required int projectID,
}) async {
Map<String, dynamic> mapDevice = {
'AppointmentNo_Vida': appointmentNoVida,
'OrderNo': orderNo,
'ProjectID': projectID,
};
try {
GenericApiModel<List<AncillaryOrderProcedureItem>>? apiResponse;
Failure? failure;
await apiClient.post(
ApiConsts.getOnlineAncillaryOrderProcList,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
List<AncillaryOrderProcedureItem> ancillaryOrdersProcedures = [];
// Parse the flat array structure (NOT nested like AncillaryOrderList)
if (response['AncillaryOrderProcList'] != null && response['AncillaryOrderProcList'] is List) {
final procList = response['AncillaryOrderProcList'] as List;
// Parse each procedure item directly
for (var procJson in procList) {
if (procJson is Map<String, dynamic>) {
ancillaryOrdersProcedures.add(AncillaryOrderProcedureItem.fromJson(procJson));
}
}
}
apiResponse = GenericApiModel<List<AncillaryOrderProcedureItem>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: ancillaryOrdersProcedures,
);
} catch (e) {
loggerService.logError("Error parsing ancillary Procedures: ${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 getOnlineAncillaryOrderDetailsProceduresList: ${e.toString()}");
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, dynamic>> checkPaymentStatus({required String transID}) async {
Map<String, dynamic> mapDevice = {'ClientRequestID': transID};
try {
dynamic apiResponse;
Failure? failure;
await apiClient.post(
ApiConsts.getRequestStatusByRequestID,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
apiResponse = response;
},
);
if (failure != null) return Left(failure!);
return Right(apiResponse);
} catch (e) {
loggerService.logError("Unknown error in checkPaymentStatus: ${e.toString()}");
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, dynamic>> createAdvancePayment({
required int projectID,
required double paymentAmount,
required String paymentReference,
required String paymentMethodName,
required int patientTypeID,
required String patientName,
required int patientID,
required String setupID,
required bool isAncillaryOrder,
}) async {
// //VersionID (number)
// // Channel (number)
// // IPAdress (string)
// // generalid (string)
// // LanguageID (number)
// // Latitude (number)
// // Longitude (number)
// // DeviceTypeID (number)
// // PatientType (number)
// // PatientTypeID (number)
// // PatientID (number)
// // PatientOutSA (number)
// // TokenID (string)
// // SessionID (string)
Map<String, dynamic> mapDevice = {
'CustName': patientName,
'CustID': patientID,
'SetupID': setupID,
'ProjectID': projectID,
'AccountID': patientID,
'PaymentAmount': paymentAmount,
'NationalityID': null,
'DepositorName': patientName,
'CreatedBy': 3,
'PaymentMethodName': paymentMethodName,
'PaymentReference': paymentReference,
'PaymentMethod': paymentMethodName,
'IsAncillaryOrder': isAncillaryOrder,
};
try {
dynamic apiResponse;
Failure? failure;
await apiClient.post(
ApiConsts.createAdvancePayments,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
apiResponse = response;
},
);
if (failure != null) return Left(failure!);
return Right(apiResponse);
} catch (e) {
loggerService.logError("Unknown error in createAdvancePayment: ${e.toString()}");
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, dynamic>> addAdvancedNumberRequest({
required String advanceNumber,
required String paymentReference,
required int appointmentID,
required int patientID,
required int patientTypeID,
required int patientOutSA,
}) async {
Map<String, dynamic> mapDevice = {
'AdvanceNumber': advanceNumber,
'PaymentReference': paymentReference,
'AppointmentID': appointmentID,
'PatientID': patientID,
'PatientTypeID': patientTypeID,
'PatientOutSA': patientOutSA,
};
try {
dynamic apiResponse;
Failure? failure;
await apiClient.post(
ApiConsts.addAdvanceNumberRequest,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
apiResponse = response;
},
);
if (failure != null) return Left(failure!);
return Right(apiResponse);
} catch (e) {
loggerService.logError("Unknown error in addAdvancedNumberRequest: ${e.toString()}");
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, dynamic>> autoGenerateAncillaryOrdersInvoice({
required int orderNo,
required int projectID,
required int appointmentNo,
required List<dynamic> selectedProcedures,
required int languageID,
}) async {
// Extract procedure IDs from selectedProcedures
List<String> procedureOrderIDs = [];
selectedProcedures.forEach((element) {
procedureOrderIDs.add(element["ProcedureID"].toString());
});
Map<String, dynamic> mapDevice = {
'LanguageID': languageID,
'RequestAncillaryOrderInvoice': [
{
'MemberID': 102,
'ProjectID': projectID,
'AppointmentNo': appointmentNo,
'OrderNo': orderNo,
'AncillaryOrderInvoiceProcList': selectedProcedures,
}
],
'ProcedureOrderIds': procedureOrderIDs,
};
try {
dynamic apiResponse;
Failure? failure;
await apiClient.post(
ApiConsts.autoGenerateAncillaryOrdersInvoice,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
apiResponse = response;
},
);
if (failure != null) return Left(failure!);
return Right(apiResponse);
} catch (e) {
loggerService.logError("Unknown error in autoGenerateAncillaryOrdersInvoice: ${e.toString()}");
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, dynamic>> applePayInsertRequest({required dynamic applePayInsertRequest}) async {
Map<String, dynamic> mapDevice = {
'ApplePayInsertRequest': applePayInsertRequest,
};
try {
dynamic apiResponse;
Failure? failure;
await apiClient.post(
ApiConsts.applePayInsertRequest,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
apiResponse = response;
},
);
if (failure != null) return Left(failure!);
return Right(apiResponse);
} catch (e) {
loggerService.logError("Unknown error in applePayInsertRequest: ${e.toString()}");
return Left(UnknownFailure(e.toString()));
}
}
}

@ -0,0 +1,242 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/features/todo_section/models/resp_models/ancillary_order_list_response_model.dart';
import 'package:hmg_patient_app_new/features/todo_section/models/resp_models/ancillary_order_procedures_detail_response_model.dart';
import 'package:hmg_patient_app_new/features/todo_section/todo_section_repo.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
class TodoSectionViewModel extends ChangeNotifier {
TodoSectionRepo todoSectionRepo;
ErrorHandlerService errorHandlerService;
TodoSectionViewModel({required this.todoSectionRepo, required this.errorHandlerService});
initializeTodoSectionViewModel() async {
patientAncillaryOrdersList.clear();
isAncillaryOrdersLoading = true;
isAncillaryDetailsProceduresLoading = true;
await getPatientOnlineAncillaryOrderList();
}
bool isAncillaryOrdersLoading = false;
bool isAncillaryDetailsProceduresLoading = false;
bool isProcessingPayment = false;
List<AncillaryOrderItem> patientAncillaryOrdersList = [];
List<AncillaryOrderProcedureItem> patientAncillaryOrderProceduresList = [];
void setProcessingPayment(bool value) {
isProcessingPayment = value;
notifyListeners();
}
Future<void> getPatientOnlineAncillaryOrderList({Function(dynamic)? onSuccess, Function(String)? onError}) async {
patientAncillaryOrdersList.clear();
isAncillaryOrdersLoading = true;
notifyListeners();
final result = await todoSectionRepo.getOnlineAncillaryOrderList();
result.fold(
(failure) async {
isAncillaryOrdersLoading = false;
await errorHandlerService.handleError(failure: failure);
},
(apiResponse) {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
patientAncillaryOrdersList = apiResponse.data!;
isAncillaryOrdersLoading = false;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
Future<void> getPatientOnlineAncillaryOrderDetailsProceduresList({
Function(dynamic)? onSuccess,
Function(String)? onError,
required int appointmentNoVida,
required int orderNo,
required int projectID,
}) async {
isAncillaryDetailsProceduresLoading = true;
notifyListeners();
final result = await todoSectionRepo.getOnlineAncillaryOrderDetailsProceduresList(
appointmentNoVida: appointmentNoVida,
orderNo: orderNo,
projectID: projectID,
);
result.fold(
(failure) async {
isAncillaryDetailsProceduresLoading = false;
await errorHandlerService.handleError(failure: failure);
},
(apiResponse) {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
patientAncillaryOrderProceduresList = apiResponse.data!;
isAncillaryDetailsProceduresLoading = false;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
Future<void> checkPaymentStatus({
required String transID,
Function(dynamic)? onSuccess,
Function(String)? onError,
}) async {
final result = await todoSectionRepo.checkPaymentStatus(transID: transID);
result.fold(
(failure) async {
await errorHandlerService.handleError(failure: failure);
if (onError != null) {
onError(failure.toString());
}
},
(response) {
if (onSuccess != null) {
onSuccess(response);
}
},
);
}
Future<void> createAdvancePayment({
required int projectID,
required double paymentAmount,
required String paymentReference,
required String paymentMethodName,
required int patientTypeID,
required String patientName,
required int patientID,
required String setupID,
required bool isAncillaryOrder,
Function(dynamic)? onSuccess,
Function(String)? onError,
}) async {
final result = await todoSectionRepo.createAdvancePayment(
projectID: projectID,
paymentAmount: paymentAmount,
paymentReference: paymentReference,
paymentMethodName: paymentMethodName,
patientTypeID: patientTypeID,
patientName: patientName,
patientID: patientID,
setupID: setupID,
isAncillaryOrder: isAncillaryOrder,
);
result.fold(
(failure) async {
await errorHandlerService.handleError(failure: failure);
if (onError != null) {
onError(failure.toString());
}
},
(response) {
if (onSuccess != null) {
onSuccess(response);
}
},
);
}
Future<void> addAdvancedNumberRequest({
required String advanceNumber,
required String paymentReference,
required int appointmentID,
required int patientID,
required int patientTypeID,
required int patientOutSA,
Function(dynamic)? onSuccess,
Function(String)? onError,
}) async {
final result = await todoSectionRepo.addAdvancedNumberRequest(
advanceNumber: advanceNumber,
paymentReference: paymentReference,
appointmentID: appointmentID,
patientID: patientID,
patientTypeID: patientTypeID,
patientOutSA: patientOutSA,
);
result.fold(
(failure) async {
await errorHandlerService.handleError(failure: failure);
if (onError != null) {
onError(failure.toString());
}
},
(response) {
if (onSuccess != null) {
onSuccess(response);
}
},
);
}
Future<void> autoGenerateAncillaryOrdersInvoice({
required int orderNo,
required int projectID,
required int appointmentNo,
required List<dynamic> selectedProcedures,
required int languageID,
Function(dynamic)? onSuccess,
Function(String)? onError,
}) async {
final result = await todoSectionRepo.autoGenerateAncillaryOrdersInvoice(
orderNo: orderNo,
projectID: projectID,
appointmentNo: appointmentNo,
selectedProcedures: selectedProcedures,
languageID: languageID,
);
result.fold(
(failure) async {
await errorHandlerService.handleError(failure: failure);
if (onError != null) {
onError(failure.toString());
}
},
(response) {
if (onSuccess != null) {
onSuccess(response);
}
},
);
}
Future<void> applePayInsertRequest({
required dynamic applePayInsertRequest,
Function(dynamic)? onSuccess,
Function(String)? onError,
}) async {
final result = await todoSectionRepo.applePayInsertRequest(applePayInsertRequest: applePayInsertRequest);
result.fold(
(failure) async {
await errorHandlerService.handleError(failure: failure);
if (onError != null) {
onError(failure.toString());
}
},
(response) {
if (onSuccess != null) {
onSuccess(response);
}
},
);
}
}

@ -26,6 +26,7 @@ import 'package:hmg_patient_app_new/features/payfort/payfort_view_model.dart';
import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart';
import 'package:hmg_patient_app_new/features/profile_settings/profile_settings_view_model.dart';
import 'package:hmg_patient_app_new/features/radiology/radiology_view_model.dart';
import 'package:hmg_patient_app_new/features/todo_section/todo_section_view_model.dart';
import 'package:hmg_patient_app_new/routes/app_routes.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
@ -134,6 +135,9 @@ void main() async {
),ChangeNotifierProvider<LocationViewModel>(
create: (_) => getIt.get<LocationViewModel>(),
),
ChangeNotifierProvider<TodoSectionViewModel>(
create: (_) => getIt.get<TodoSectionViewModel>(),
),
ChangeNotifierProvider<ContactUsViewModel>(
create: (_) => getIt.get<ContactUsViewModel>(),
)

@ -9,21 +9,19 @@ 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/enums.dart';
import 'package:hmg_patient_app_new/features/payfort/models/apple_pay_request_insert_model.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/patient_appointment_history_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/payfort/models/apple_pay_request_insert_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/appointments/my_appointments_page.dart';
import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart';
import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/services/cache_service.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/in_app_browser/InAppBrowser.dart';
@ -110,7 +108,8 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset(AppAssets.mada, width: 72.h, height: 25.h).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading),
Image.asset(AppAssets.mada, width: 72.h, height: 25.h)
.toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading),
SizedBox(height: 16.h),
"Mada".needTranslation.toText16(isBold: true).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading),
],
@ -154,7 +153,10 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
],
).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading),
SizedBox(height: 16.h),
"Visa or Mastercard".needTranslation.toText16(isBold: true).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading),
"Visa or Mastercard"
.needTranslation
.toText16(isBold: true)
.toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading),
],
),
SizedBox(width: 8.h),
@ -182,23 +184,27 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset(AppAssets.tamara_en, width: 72.h, height: 25.h).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading),
SizedBox(height: 16.h),
"Tamara".needTranslation.toText16(isBold: true).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading),
],
),
SizedBox(width: 8.h),
const Spacer(),
Transform.flip(
flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets(
),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset(AppAssets.tamara_en, width: 72.h, height: 25.h)
.toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading),
SizedBox(height: 16.h),
"Tamara"
.needTranslation
.toText16(isBold: true)
.toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading),
],
),
SizedBox(width: 8.h),
const Spacer(),
Transform.flip(
flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon_small,
iconColor: AppColors.blackColor,
width: 18.h,
@ -244,7 +250,10 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"Insurance expired or inactive".needTranslation.toText14(color: AppColors.primaryRedColor, weight: FontWeight.w500).paddingSymmetrical(24.h, 0.h),
"Insurance expired or inactive"
.needTranslation
.toText14(color: AppColors.primaryRedColor, weight: FontWeight.w500)
.paddingSymmetrical(24.h, 0.h),
CustomButton(
text: LocaleKeys.updateInsurance.tr(context: context),
onPressed: () {
@ -274,7 +283,10 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"Amount before tax".needTranslation.toText14(isBold: true),
Utils.getPaymentAmountWithSymbol(myAppointmentsVM.patientAppointmentShareResponseModel!.patientShare!.toString().toText16(isBold: true), AppColors.blackColor, 13,
Utils.getPaymentAmountWithSymbol(
myAppointmentsVM.patientAppointmentShareResponseModel!.patientShare!.toString().toText16(isBold: true),
AppColors.blackColor,
13,
isSaudiCurrency: true),
],
).paddingSymmetrical(24.h, 0.h),
@ -283,7 +295,9 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
children: [
"VAT 15%".needTranslation.toText14(isBold: true, color: AppColors.greyTextColor),
Utils.getPaymentAmountWithSymbol(
myAppointmentsVM.patientAppointmentShareResponseModel!.patientTaxAmount!.toString().toText14(isBold: true, color: AppColors.greyTextColor),
myAppointmentsVM.patientAppointmentShareResponseModel!.patientTaxAmount!
.toString()
.toText14(isBold: true, color: AppColors.greyTextColor),
AppColors.greyTextColor,
13,
isSaudiCurrency: true),
@ -294,7 +308,10 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"".needTranslation.toText14(isBold: true),
Utils.getPaymentAmountWithSymbol(myAppointmentsVM.patientAppointmentShareResponseModel!.patientShareWithTax!.toString().toText24(isBold: true), AppColors.blackColor, 17,
Utils.getPaymentAmountWithSymbol(
myAppointmentsVM.patientAppointmentShareResponseModel!.patientShareWithTax!.toString().toText24(isBold: true),
AppColors.blackColor,
17,
isSaudiCurrency: true),
],
).paddingSymmetrical(24.h, 0.h),
@ -373,9 +390,11 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
onSuccess: (apiResponse) async {
if (apiResponse.data["status"].toString().toLowerCase() == "success") {
tamaraOrderID = apiResponse.data["tamara_order_id"].toString();
await payfortViewModel.updateTamaraRequestStatus(responseMessage: "success", status: "14", clientRequestID: transID, tamaraOrderID: tamaraOrderID);
await payfortViewModel.updateTamaraRequestStatus(
responseMessage: "success", status: "14", clientRequestID: transID, tamaraOrderID: tamaraOrderID);
await payfortViewModel.markAppointmentAsTamaraPaid(
projectID: widget.patientAppointmentHistoryResponseModel.projectID, appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo);
projectID: widget.patientAppointmentHistoryResponseModel.projectID,
appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo);
await myAppointmentsViewModel.addAdvanceNumberRequest(
advanceNumber: "Tamara-Advance-0000",
paymentReference: tamaraOrderID,
@ -418,7 +437,8 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
}
});
} else {
await payfortViewModel.updateTamaraRequestStatus(responseMessage: "Failed", status: "00", clientRequestID: transID, tamaraOrderID: tamaraOrderID);
await payfortViewModel.updateTamaraRequestStatus(
responseMessage: "Failed", status: "00", clientRequestID: transID, tamaraOrderID: tamaraOrderID);
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
@ -536,7 +556,9 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
browser!,
widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false,
"2",
widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? myAppointmentsViewModel.patientAppointmentShareResponseModel!.clinicID.toString() : "",
widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment!
? myAppointmentsViewModel.patientAppointmentShareResponseModel!.clinicID.toString()
: "",
context,
myAppointmentsViewModel.patientAppointmentShareResponseModel!.appointmentDate,
myAppointmentsViewModel.patientAppointmentShareResponseModel!.appointmentNo,
@ -546,7 +568,14 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
}
startApplePay() async {
LoaderBottomSheet.showLoader();
showCommonBottomSheet(context,
child: Utils.getLoadingWidget(),
callBackFunc: (str) {},
title: "",
height: ResponsiveExtension.screenHeight * 0.3,
isCloseButtonVisible: false,
isDismissible: false,
isFullScreen: false);
transID = Utils.getAppointmentTransID(
widget.patientAppointmentHistoryResponseModel.projectID,
widget.patientAppointmentHistoryResponseModel.clinicID,
@ -556,7 +585,9 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
ApplePayInsertRequest applePayInsertRequest = ApplePayInsertRequest();
await payfortViewModel.getPayfortConfigurations(
serviceId: ServiceTypeEnum.appointmentPayment.getIdFromServiceEnum(), projectId: widget.patientAppointmentHistoryResponseModel.projectID, integrationId: 2);
serviceId: ServiceTypeEnum.appointmentPayment.getIdFromServiceEnum(),
projectId: widget.patientAppointmentHistoryResponseModel.projectID,
integrationId: 2);
applePayInsertRequest.clientRequestID = transID;
applePayInsertRequest.clinicID = widget.patientAppointmentHistoryResponseModel.clinicID;

@ -46,7 +46,7 @@ class _SearchDoctorByNameState extends State<SearchDoctorByName> {
body: Column(
children: [
Expanded(
child: CollapsingListView(
child: CollapsingListView(
title: "Choose Doctor".needTranslation,
child: SingleChildScrollView(
child: Padding(

@ -16,11 +16,17 @@ import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
class DoctorCard extends StatelessWidget {
DoctorCard({super.key, required this.doctorsListResponseModel, required this.isLoading, required this.bookAppointmentsViewModel});
const DoctorCard({
super.key,
required this.doctorsListResponseModel,
required this.isLoading,
required this.bookAppointmentsViewModel,
});
DoctorsListResponseModel doctorsListResponseModel;
bool isLoading = false;
BookAppointmentsViewModel bookAppointmentsViewModel;
final DoctorsListResponseModel doctorsListResponseModel;
final bool isLoading;
final BookAppointmentsViewModel bookAppointmentsViewModel;
@override
Widget build(BuildContext context) {
@ -55,10 +61,14 @@ class DoctorCard extends StatelessWidget {
children: [
SizedBox(
width: MediaQuery.of(context).size.width * 0.49,
child: (isLoading ? "Dr John Smith" : "${doctorsListResponseModel.doctorTitle} ${doctorsListResponseModel.name}").toString().toText16(isBold: true, maxlines: 1),
child: (isLoading ? "Dr John Smith" : "${doctorsListResponseModel.doctorTitle} ${doctorsListResponseModel.name}")
.toString()
.toText16(isBold: true, maxlines: 1),
).toShimmer2(isShow: isLoading),
Image.network(
isLoading ? "https://hmgwebservices.com/Images/flag/SYR.png" : doctorsListResponseModel.nationalityFlagURL ?? "https://hmgwebservices.com/Images/flag/SYR.png",
isLoading
? "https://hmgwebservices.com/Images/flag/SYR.png"
: doctorsListResponseModel.nationalityFlagURL ?? "https://hmgwebservices.com/Images/flag/SYR.png",
width: 20.h,
height: 15.h,
fit: BoxFit.fill,
@ -79,7 +89,8 @@ class DoctorCard extends StatelessWidget {
),
Expanded(
flex: 1,
child: Utils.buildSvgWithAssets(icon: AppAssets.doctor_profile_icon, width: 20.h, height: 20.h, fit: BoxFit.scaleDown).toShimmer2(isShow: isLoading),
child: Utils.buildSvgWithAssets(icon: AppAssets.doctor_profile_icon, width: 20.h, height: 20.h, fit: BoxFit.scaleDown)
.toShimmer2(isShow: isLoading),
),
],
),

@ -5,7 +5,7 @@ import 'package:hmg_patient_app_new/presentation/book_appointment/book_appointme
import 'package:hmg_patient_app_new/presentation/hmg_services/services_page.dart';
import 'package:hmg_patient_app_new/presentation/home/landing_page.dart';
import 'package:hmg_patient_app_new/presentation/medical_file/medical_file_page.dart';
import 'package:hmg_patient_app_new/presentation/todo/todo_page.dart';
import 'package:hmg_patient_app_new/presentation/todo_section/todo_page.dart';
import 'package:hmg_patient_app_new/widgets/bottom_navigation/bottom_navigation.dart';
class LandingNavigation extends StatefulWidget {

@ -174,8 +174,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset(appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg,
width: 56.w, height: 56.h),
Image.asset(appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg, width: 56.w, height: 56.h),
SizedBox(width: 8.w),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -222,13 +221,13 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
children: [
AppCustomChipWidget(
labelText: "${appState.getAuthenticatedUser()!.age} Years Old",
labelPadding: EdgeInsetsDirectional.only(start: 8.w, end: 8.w),
labelPadding: EdgeInsetsDirectional.only(start: 8.w, end: 8.w),
),
AppCustomChipWidget(
icon: AppAssets.blood_icon,
labelText: "Blood: ${appState.getUserBloodGroup.isEmpty ? "N/A" : appState.getUserBloodGroup.isEmpty}",
labelText: "Blood: ${appState.getUserBloodGroup.isEmpty ? "N/A" : appState.getUserBloodGroup.isEmpty}",
iconColor: AppColors.primaryRedColor,
labelPadding: EdgeInsetsDirectional.only(end: 8.w),
labelPadding: EdgeInsetsDirectional.only(end: 8.w),
),
Consumer<InsuranceViewModel>(builder: (context, insuranceVM, child) {
return AppCustomChipWidget(
@ -237,7 +236,8 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
iconColor: insuranceVM.isInsuranceExpired ? AppColors.primaryRedColor : AppColors.successColor,
textColor: insuranceVM.isInsuranceExpired ? AppColors.primaryRedColor : AppColors.successColor,
iconSize: 12.w,
backgroundColor: insuranceVM.isInsuranceExpired ? AppColors.primaryRedColor.withOpacity(0.1) : AppColors.successColor.withOpacity(0.1),
backgroundColor:
insuranceVM.isInsuranceExpired ? AppColors.primaryRedColor.withOpacity(0.1) : AppColors.successColor.withOpacity(0.1),
labelPadding: EdgeInsetsDirectional.only(end: 8.w),
);
}),
@ -739,7 +739,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
crossAxisCount: 3,
crossAxisSpacing: 16.h,
mainAxisSpacing: 16.w,
mainAxisExtent: 110.h,
mainAxisExtent: 115.h,
),
physics: NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero,

@ -88,7 +88,7 @@ class MedicalFileAppointmentCard extends StatelessWidget {
myAppointmentsViewModel.isMyAppointmentsLoading
? Container().toShimmer2(isShow: true, height: 40.h, width: 100.w, radius: 12.r)
: Expanded(
flex: 6,
flex: 7,
child: AppointmentType.isArrived(patientAppointmentHistoryResponseModel)
? getArrivedAppointmentButton(context).toShimmer2(isShow: myAppointmentsViewModel.isMyAppointmentsLoading)
: CustomButton(
@ -105,7 +105,8 @@ class MedicalFileAppointmentCard extends StatelessWidget {
},
backgroundColor:
AppointmentType.getNextActionButtonColor(patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.15),
borderColor: AppointmentType.getNextActionButtonColor(patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.01),
borderColor:
AppointmentType.getNextActionButtonColor(patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.01),
textColor: AppointmentType.getNextActionTextColor(patientAppointmentHistoryResponseModel.nextAction),
fontSize: 14.f,
fontWeight: FontWeight.w500,

@ -30,17 +30,16 @@ class MedicalFileCard extends StatelessWidget {
color: backgroundColor,
borderRadius: 12.r,
),
child: Padding(
padding: EdgeInsets.all(12.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Utils.buildSvgWithAssets(icon: svgIcon, width: iconS, height: iconS, fit: BoxFit.contain),
SizedBox(height: 12.h),
isLargeText ? label.toText13(color: textColor, isBold: true, maxLine: 2) : label.toText11(color: textColor, isBold: true, maxLine: 2),
],
),
padding: EdgeInsets.all(12.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Utils.buildSvgWithAssets(icon: svgIcon, width: iconS, height: iconS, fit: BoxFit.contain),
SizedBox(height: 8.h),
isLargeText ? label.toText13(color: textColor, isBold: true, maxLine: 2) : label.toText11(color: textColor, isBold: true, maxLine: 2),
],
),
);
}

@ -59,7 +59,7 @@ class _SplashAnimationScreenState extends State<SplashAnimationScreen> with Sing
}
}
// todo: do-not remove this code,as animation need to test on multiple screen sizes
// todo_section: do-not remove this code,as animation need to test on multiple screen sizes
class AnimatedScreen extends StatefulWidget {
const AnimatedScreen({super.key});

@ -13,15 +13,13 @@ import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/prescriptions/models/resp_models/patient_prescriptions_response_model.dart';
import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/presentation/prescriptions/prescription_item_view.dart';
import 'package:hmg_patient_app_new/presentation/prescriptions/prescription_reminder_view.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/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart';
import 'package:open_filex/open_filex.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';
@ -127,7 +125,8 @@ class _PrescriptionDetailPageState extends State<PrescriptionDetailPage> {
children: [
AppCustomChipWidget(
icon: AppAssets.doctor_calendar_icon,
labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(widget.prescriptionsResponseModel.appointmentDate), false),
labelText: DateUtil.formatDateToDate(
DateUtil.convertStringToDate(widget.prescriptionsResponseModel.appointmentDate), false),
labelPadding: EdgeInsetsDirectional.only(start: -4.h, end: 8.h),
),
AppCustomChipWidget(
@ -214,18 +213,22 @@ class _PrescriptionDetailPageState extends State<PrescriptionDetailPage> {
hasShadow: true,
),
child: CustomButton(
text: widget.prescriptionsResponseModel.isHomeMedicineDeliverySupported! ? LocaleKeys.resendOrder.tr(context: context) : LocaleKeys.prescriptionDeliveryError.tr(context: context),
text: widget.prescriptionsResponseModel.isHomeMedicineDeliverySupported!
? LocaleKeys.resendOrder.tr(context: context)
: LocaleKeys.prescriptionDeliveryError.tr(context: context),
onPressed: () {},
backgroundColor: widget.prescriptionsResponseModel.isHomeMedicineDeliverySupported! ? AppColors.successColor : AppColors.greyF7Color,
borderColor: AppColors.successColor.withOpacity(0.01),
textColor: widget.prescriptionsResponseModel.isHomeMedicineDeliverySupported! ? AppColors.whiteColor : AppColors.textColor.withOpacity(0.35),
textColor:
widget.prescriptionsResponseModel.isHomeMedicineDeliverySupported! ? AppColors.whiteColor : AppColors.textColor.withOpacity(0.35),
fontSize: 16,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 50.h,
icon: AppAssets.prescription_refill_icon,
iconColor: widget.prescriptionsResponseModel.isHomeMedicineDeliverySupported! ? AppColors.whiteColor : AppColors.textColor.withOpacity(0.35),
iconColor:
widget.prescriptionsResponseModel.isHomeMedicineDeliverySupported! ? AppColors.whiteColor : AppColors.textColor.withOpacity(0.35),
iconSize: 20.h,
).paddingSymmetrical(24.h, 24.h),
),

@ -10,15 +10,13 @@ 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/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/presentation/radiology/radiology_result_page.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/routes/custom_page_route.dart';
import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart';
import 'package:provider/provider.dart';
import '../../features/radiology/radiology_view_model.dart';
@ -38,7 +36,7 @@ class _RadiologyOrdersPageState extends State<RadiologyOrdersPage> {
@override
void initState() {
scheduleMicrotask(() {
radiologyViewModel.initRadiologyProvider();
radiologyViewModel.initRadiologyViewModel();
});
super.initState();
}
@ -78,127 +76,136 @@ class _RadiologyOrdersPageState extends State<RadiologyOrdersPage> {
)
: model.patientRadiologyOrders.isNotEmpty
? AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
margin: EdgeInsets.symmetric(vertical: 8.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 20.h, hasShadow: true),
child: InkWell(
onTap: () {
setState(() {
expandedIndex = isExpanded ? null : index;
});
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AppCustomChipWidget(
labelText: LocaleKeys.resultsAvailable.tr(context: context),
backgroundColor: AppColors.successColor.withOpacity(0.15),
textColor: AppColors.successColor,
).toShimmer2(isShow: model.isRadiologyOrdersLoading, width: 100),
SizedBox(height: 8.h),
Row(
children: [
Image.network(
model.isRadiologyOrdersLoading
? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png"
: model.patientRadiologyOrders[index].doctorImageURL!,
width: 24.h,
height: 24.h,
fit: BoxFit.fill,
).circle(100).toShimmer2(isShow: model.isRadiologyOrdersLoading),
SizedBox(width: 4.h),
(model.isRadiologyOrdersLoading ? "Dr John Smith" : model.patientRadiologyOrders[index].doctorName!)
.toText16(isBold: true)
.toShimmer2(isShow: model.isRadiologyOrdersLoading)
],
),
SizedBox(height: 8.h),
Wrap(
direction: Axis.horizontal,
spacing: 3.h,
runSpacing: 4.h,
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
margin: EdgeInsets.symmetric(vertical: 8.h),
decoration: RoundedRectangleBorder()
.toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 20.h, hasShadow: true),
child: InkWell(
onTap: () {
setState(() {
expandedIndex = isExpanded ? null : index;
});
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AppCustomChipWidget(
icon: AppAssets.doctor_calendar_icon,
labelText: model.isRadiologyOrdersLoading ? "01 Jan 2025" : DateUtil.formatDateToDate(model.patientRadiologyOrders[index].orderDate!, false),
).toShimmer2(isShow: model.isRadiologyOrdersLoading),
AppCustomChipWidget(
labelText: model.isRadiologyOrdersLoading ? "01 Jan 2025" : model.patientRadiologyOrders[index].clinicDescription!,
).toShimmer2(isShow: model.isRadiologyOrdersLoading),
labelText: LocaleKeys.resultsAvailable.tr(context: context),
backgroundColor: AppColors.successColor.withOpacity(0.15),
textColor: AppColors.successColor,
).toShimmer2(isShow: model.isRadiologyOrdersLoading, width: 100),
SizedBox(height: 8.h),
Row(
children: [
Image.network(
model.isRadiologyOrdersLoading
? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png"
: model.patientRadiologyOrders[index].doctorImageURL!,
width: 24.h,
height: 24.h,
fit: BoxFit.fill,
).circle(100).toShimmer2(isShow: model.isRadiologyOrdersLoading),
SizedBox(width: 4.h),
(model.isRadiologyOrdersLoading
? "Dr John Smith"
: model.patientRadiologyOrders[index].doctorName!)
.toText16(isBold: true)
.toShimmer2(isShow: model.isRadiologyOrdersLoading)
],
),
SizedBox(height: 8.h),
Wrap(
direction: Axis.horizontal,
spacing: 3.h,
runSpacing: 4.h,
children: [
AppCustomChipWidget(
icon: AppAssets.doctor_calendar_icon,
labelText: model.isRadiologyOrdersLoading
? "01 Jan 2025"
: DateUtil.formatDateToDate(model.patientRadiologyOrders[index].orderDate!, false),
).toShimmer2(isShow: model.isRadiologyOrdersLoading),
AppCustomChipWidget(
labelText: model.isRadiologyOrdersLoading
? "01 Jan 2025"
: model.patientRadiologyOrders[index].clinicDescription!,
).toShimmer2(isShow: model.isRadiologyOrdersLoading),
// AppCustomChipWidget(labelText: "").toShimmer2(isShow: model.isRadiologyOrdersLoading, width: 16.h),
// AppCustomChipWidget(labelText: "").toShimmer2(isShow: model.isRadiologyOrdersLoading, width: 16.h),
// AppCustomChipWidget(labelText: "").toShimmer2(isShow: model.isRadiologyOrdersLoading, width: 16.h),
// AppCustomChipWidget(labelText: "").toShimmer2(isShow: model.isRadiologyOrdersLoading, width: 16.h),
],
),
],
),
],
),
),
model.isRadiologyOrdersLoading
? SizedBox.shrink()
: AnimatedCrossFade(
firstChild: SizedBox.shrink(),
secondChild: Padding(
padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 8.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.only(bottom: 8.h),
child: '${model.patientRadiologyOrders[index].description}'.toText14(weight: FontWeight.w500),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
),
model.isRadiologyOrdersLoading
? SizedBox.shrink()
: AnimatedCrossFade(
firstChild: SizedBox.shrink(),
secondChild: Padding(
padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 8.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(),
CustomButton(
icon: AppAssets.view_report_icon,
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
text: LocaleKeys.viewReport.tr(context: context),
onPressed: () {
Navigator.of(context).push(
CustomPageRoute(
page: RadiologyResultPage(patientRadiologyResponseModel: model.patientRadiologyOrders[index]),
),
);
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14,
fontWeight: FontWeight.bold,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
Padding(
padding: EdgeInsets.only(bottom: 8.h),
child: '${model.patientRadiologyOrders[index].description}'
.toText14(weight: FontWeight.w500),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(),
CustomButton(
icon: AppAssets.view_report_icon,
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
text: LocaleKeys.viewReport.tr(context: context),
onPressed: () {
Navigator.of(context).push(
CustomPageRoute(
page: RadiologyResultPage(
patientRadiologyResponseModel: model.patientRadiologyOrders[index]),
),
);
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14,
fontWeight: FontWeight.bold,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
),
],
),
],
),
],
),
crossFadeState: isExpanded ? CrossFadeState.showSecond : CrossFadeState.showFirst,
duration: Duration(milliseconds: 300),
),
),
crossFadeState: isExpanded ? CrossFadeState.showSecond : CrossFadeState.showFirst,
duration: Duration(milliseconds: 300),
),
],
],
),
),
),
),
),
),
),
)
: Utils.getNoDataWidget(context, noDataText: "You don't have any radiology results yet.".needTranslation);
)
: Utils.getNoDataWidget(context, noDataText: "You don't have any radiology results yet.".needTranslation);
},
),
],

@ -1,31 +0,0 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
class ToDoPage extends StatefulWidget {
const ToDoPage({super.key});
@override
State<ToDoPage> createState() => _ToDoPageState();
}
class _ToDoPageState extends State<ToDoPage> {
@override
Widget build(BuildContext context) {
return CollapsingListView(
title: "ToDo List".needTranslation,
isLeading: false,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 16.h),
"Ancillary Orders".needTranslation.toText18(isBold: true),
],
).paddingSymmetrical(24.w, 0),
);
}
}

@ -0,0 +1,644 @@
import 'dart:async';
import 'dart:developer';
import 'dart:io';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_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/enums.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.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/payfort/models/apple_pay_request_insert_model.dart';
import 'package:hmg_patient_app_new/features/payfort/payfort_view_model.dart';
import 'package:hmg_patient_app_new/features/todo_section/models/resp_models/ancillary_order_procedures_detail_response_model.dart';
import 'package:hmg_patient_app_new/features/todo_section/todo_section_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/home/navigation_screen.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/in_app_browser/InAppBrowser.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:provider/provider.dart';
class AncillaryOrderPaymentPage extends StatefulWidget {
final DateTime? appointmentDate;
final int appointmentNoVida;
final int orderNo;
final int projectID;
final List<AncillaryOrderProcDetail> selectedProcedures;
final double totalAmount;
const AncillaryOrderPaymentPage({
super.key,
required this.appointmentDate,
required this.appointmentNoVida,
required this.orderNo,
required this.projectID,
required this.selectedProcedures,
required this.totalAmount,
});
@override
State<AncillaryOrderPaymentPage> createState() => _AncillaryOrderPaymentPageState();
}
class _AncillaryOrderPaymentPageState extends State<AncillaryOrderPaymentPage> {
late PayfortViewModel payfortViewModel;
late AppState appState;
late TodoSectionViewModel todoSectionViewModel;
MyInAppBrowser? browser;
String selectedPaymentMethod = "";
String transID = "";
@override
void initState() {
scheduleMicrotask(() {
payfortViewModel.initPayfortViewModel();
payfortViewModel.setIsApplePayConfigurationLoading(false);
});
super.initState();
}
@override
Widget build(BuildContext context) {
appState = getIt.get<AppState>();
todoSectionViewModel = Provider.of<TodoSectionViewModel>(context);
payfortViewModel = Provider.of<PayfortViewModel>(context);
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: Consumer<TodoSectionViewModel>(
builder: (context, todoVM, child) {
return Column(
children: [
Expanded(
child: CollapsingListView(
title: "Select Payment Method".needTranslation,
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 24.h),
// Mada Payment Option
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset(AppAssets.mada, width: 72.h, height: 25.h).toShimmer2(isShow: todoVM.isProcessingPayment),
SizedBox(height: 16.h),
"Mada".needTranslation.toText16(isBold: true).toShimmer2(isShow: todoVM.isProcessingPayment),
],
),
SizedBox(width: 8.h),
const Spacer(),
Transform.flip(
flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon_small,
iconColor: AppColors.blackColor,
width: 18.h,
height: 13.h,
fit: BoxFit.contain,
).toShimmer2(isShow: todoVM.isProcessingPayment),
),
],
).paddingSymmetrical(16.h, 16.h),
).paddingSymmetrical(24.h, 0.h).onPress(() {
if (!todoVM.isProcessingPayment) {
selectedPaymentMethod = "MADA";
_openPaymentURL("mada");
}
}),
SizedBox(height: 16.h),
// Visa/Mastercard Payment Option
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Image.asset(AppAssets.visa, width: 50.h, height: 50.h),
SizedBox(width: 8.h),
Image.asset(AppAssets.Mastercard, width: 40.h, height: 40.h),
],
).toShimmer2(isShow: todoVM.isProcessingPayment),
SizedBox(height: 16.h),
"Visa or Mastercard".needTranslation.toText16(isBold: true).toShimmer2(isShow: todoVM.isProcessingPayment),
],
),
SizedBox(width: 8.h),
const Spacer(),
Transform.flip(
flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon_small,
iconColor: AppColors.blackColor,
width: 18.h,
height: 13.h,
fit: BoxFit.contain,
).toShimmer2(isShow: todoVM.isProcessingPayment),
),
],
).paddingSymmetrical(16.h, 16.h),
).paddingSymmetrical(24.h, 0.h).onPress(() {
if (!todoVM.isProcessingPayment) {
selectedPaymentMethod = "VISA";
_openPaymentURL("visa");
}
}),
],
),
),
),
),
// Payment Summary Footer
todoVM.isProcessingPayment ? SizedBox.shrink() : _buildPaymentSummary()
],
);
},
),
);
}
Widget _buildPaymentSummary() {
// Calculate amounts
double amountBeforeTax = 0.0;
double taxAmount = 0.0;
for (var proc in widget.selectedProcedures) {
amountBeforeTax += (proc.patientShare ?? 0);
taxAmount += (proc.patientTaxAmount ?? 0);
}
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: false,
),
child: Consumer<PayfortViewModel>(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 15%
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(
widget.totalAmount.toString().toText24(isBold: true),
AppColors.blackColor,
17,
isSaudiCurrency: true,
),
],
).paddingSymmetrical(24.h, 0.h),
// Apple Pay Button (iOS only)
Platform.isIOS && Utils.havePrivilege(103)
? Utils.buildSvgWithAssets(
icon: AppAssets.apple_pay_button,
width: 200.h,
height: 80.h,
fit: BoxFit.contain,
).paddingSymmetrical(24.h, 0.h).onPress(() {
if (!todoSectionViewModel.isProcessingPayment) {
_startApplePay();
}
})
: SizedBox(height: 12.h),
SizedBox(height: 12.h),
],
);
}),
);
}
void _openPaymentURL(String paymentMethod) {
todoSectionViewModel.setProcessingPayment(true);
browser = MyInAppBrowser(
onExitCallback: _onBrowserExit,
onLoadStartCallback: _onBrowserLoadStart,
);
final user = appState.getAuthenticatedUser();
transID = Utils.getAdvancePaymentTransID(
widget.projectID,
user!.patientId!,
);
browser!.openPaymentBrowser(
widget.totalAmount,
"Ancillary Order Payment",
transID,
widget.projectID.toString(),
user.emailAddress ?? "CustID_${user.patientId}@HMG.com",
paymentMethod,
user.patientType ?? 1,
"${user.firstName} ${user.lastName}",
user.patientId,
user,
browser!,
false,
"3",
ServiceTypeEnum.ancillaryOrder.getIdFromServiceEnum().toString(),
context,
null,
widget.appointmentNoVida,
0,
0,
null,
);
}
void _onBrowserLoadStart(String url) {
log("onBrowserLoadStart: $url");
for (var element in MyInAppBrowser.successURLS) {
if (url.contains(element)) {
if (browser!.isOpened()) browser!.close();
MyInAppBrowser.isPaymentDone = true;
return;
}
}
for (var element in MyInAppBrowser.errorURLS) {
if (url.contains(element)) {
if (browser!.isOpened()) browser!.close();
MyInAppBrowser.isPaymentDone = false;
return;
}
}
}
void _onBrowserExit(bool isPaymentMade) {
log("onBrowserExit Called: $isPaymentMade");
_checkPaymentStatus();
}
void _checkPaymentStatus() {
LoaderBottomSheet.showLoader(loadingText: "Checking payment status, Please wait...".needTranslation);
todoSectionViewModel.checkPaymentStatus(
transID: transID,
onSuccess: (response) {
String paymentInfo = response['Response_Message'];
if (paymentInfo == 'Success') {
// Extract payment details from response
final paymentAmount = response['Amount'] ?? widget.totalAmount;
final fortId = response['Fort_id'] ?? transID;
final paymentMethod = response['PaymentMethod'] ?? selectedPaymentMethod;
// Call createAdvancePayment with the payment details
_createAdvancePayment(
paymentAmount: paymentAmount is String ? double.parse(paymentAmount) : paymentAmount.toDouble(),
paymentReference: fortId,
paymentMethod: paymentMethod,
);
} else {
LoaderBottomSheet.hideLoader();
todoSectionViewModel.setProcessingPayment(false);
Utils.showToast(response['Response_Message']);
}
},
onError: (error) {
LoaderBottomSheet.hideLoader();
todoSectionViewModel.setProcessingPayment(false);
Utils.showToast(error);
},
);
}
void _createAdvancePayment({
required double paymentAmount,
required String paymentReference,
required String paymentMethod,
}) {
LoaderBottomSheet.showLoader(loadingText: "Processing payment, Please wait...".needTranslation);
final user = appState.getAuthenticatedUser();
todoSectionViewModel.createAdvancePayment(
projectID: widget.projectID,
paymentAmount: paymentAmount,
paymentReference: paymentReference,
paymentMethodName: paymentMethod,
patientTypeID: user!.patientType ?? 1,
patientName: "${user.firstName} ${user.lastName}",
patientID: user.patientId!,
setupID: "010266",
isAncillaryOrder: true,
onSuccess: (response) {
// Extract advance number from response
final advanceNumber =
response['OnlineCheckInAppointments']?[0]?['AdvanceNumber'] ?? response['OnlineCheckInAppointments']?[0]?['AdvanceNumber_VP'] ?? '';
if (advanceNumber.isNotEmpty) {
_addAdvancedNumberRequest(
advanceNumber: advanceNumber.toString(),
paymentReference: paymentReference,
);
} else {
LoaderBottomSheet.hideLoader();
todoSectionViewModel.setProcessingPayment(false);
Utils.showToast("Failed to get advance number");
}
},
onError: (error) {
LoaderBottomSheet.hideLoader();
todoSectionViewModel.setProcessingPayment(false);
Utils.showToast(error);
},
);
}
void _addAdvancedNumberRequest({
required String advanceNumber,
required String paymentReference,
}) {
LoaderBottomSheet.showLoader(loadingText: "Finalizing payment, Please wait...".needTranslation);
final user = appState.getAuthenticatedUser();
todoSectionViewModel.addAdvancedNumberRequest(
advanceNumber: advanceNumber,
paymentReference: paymentReference,
appointmentID: 0,
patientID: user!.patientId!,
patientTypeID: user.patientType ?? 1,
patientOutSA: user.outSa ?? 0,
onSuccess: (response) {
// After adding advance number, generate invoice
_autoGenerateInvoice();
},
onError: (error) {
LoaderBottomSheet.hideLoader();
todoSectionViewModel.setProcessingPayment(false);
Utils.showToast(error);
},
);
}
void _autoGenerateInvoice() {
LoaderBottomSheet.showLoader(loadingText: "Generating invoice, Please wait...".needTranslation);
List<dynamic> selectedProcListAPI = widget.selectedProcedures.map((element) {
return {
"ApprovalLineItemNo": element.approvalLineItemNo,
"OrderLineItemNo": element.orderLineItemNo,
"ProcedureID": element.procedureID,
};
}).toList();
todoSectionViewModel.autoGenerateAncillaryOrdersInvoice(
orderNo: widget.orderNo,
projectID: widget.projectID,
appointmentNo: widget.appointmentNoVida,
selectedProcedures: selectedProcListAPI,
languageID: appState.isArabic() ? 1 : 2,
onSuccess: (response) {
LoaderBottomSheet.hideLoader();
final invoiceNo = response['AncillaryOrderInvoiceList']?[0]?['InvoiceNo'];
_showSuccessDialog(invoiceNo);
},
onError: (error) {
LoaderBottomSheet.hideLoader();
todoSectionViewModel.setProcessingPayment(false);
Utils.showToast(error);
},
);
}
void _showSuccessDialog(dynamic invoiceNo) {
todoSectionViewModel.setProcessingPayment(false);
log("Ancillary order payment successful! Invoice #: $invoiceNo");
// Show success message and navigate
Utils.showToast("Payment successful! Invoice #: $invoiceNo");
// Navigate back to home after a short delay
Future.delayed(Duration(seconds: 1), () {
showCommonBottomSheetWithoutHeight(
context,
child: Column(
children: [
Row(
children: [
"Here is your invoice #: ".needTranslation.toText14(
color: AppColors.textColorLight,
weight: FontWeight.w500,
),
SizedBox(width: 4.w),
("12345").toText16(isBold: true),
],
),
SizedBox(height: 24.h),
Row(
children: [
Expanded(
child: CustomButton(
height: 56.h,
text: LocaleKeys.ok.tr(),
onPressed: () {
Navigator.pushAndRemoveUntil(
context,
CustomPageRoute(
page: LandingNavigation(),
),
(r) => false);
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
),
),
],
),
],
),
// title: "Payment Completed Successfully".needTranslation,
titleWidget: Utils.getSuccessWidget(loadingText: "Payment Completed Successfully".needTranslation),
isCloseButtonVisible: false,
isDismissible: false,
isFullScreen: false,
);
});
}
_startApplePay() async {
showCommonBottomSheet(
context,
child: Utils.getLoadingWidget(),
callBackFunc: (str) {},
title: "",
height: ResponsiveExtension.screenHeight * 0.3,
isCloseButtonVisible: false,
isDismissible: false,
isFullScreen: false,
);
final user = appState.getAuthenticatedUser();
transID = Utils.getAdvancePaymentTransID(widget.projectID, user!.patientId!);
ApplePayInsertRequest applePayInsertRequest = ApplePayInsertRequest();
await payfortViewModel.getPayfortConfigurations(
serviceId: ServiceTypeEnum.ancillaryOrder.getIdFromServiceEnum(),
projectId: widget.projectID,
integrationId: 2,
);
applePayInsertRequest.clientRequestID = transID;
applePayInsertRequest.clinicID = 0;
applePayInsertRequest.currency = appState.getAuthenticatedUser()!.outSa! == 0 ? "SAR" : "AED";
applePayInsertRequest.customerEmail = "CustID_${appState.getAuthenticatedUser()!.patientId.toString()}@HMG.com";
applePayInsertRequest.customerID = appState.getAuthenticatedUser()!.patientId.toString();
applePayInsertRequest.customerName = "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}";
applePayInsertRequest.deviceToken = await Utils.getStringFromPrefs(CacheConst.pushToken);
applePayInsertRequest.voipToken = await Utils.getStringFromPrefs(CacheConst.voipToken);
applePayInsertRequest.doctorID = 0;
applePayInsertRequest.projectID = widget.projectID.toString();
applePayInsertRequest.serviceID = ServiceTypeEnum.ancillaryOrder.getIdFromServiceEnum().toString();
applePayInsertRequest.channelID = 3;
applePayInsertRequest.patientID = appState.getAuthenticatedUser()!.patientId.toString();
applePayInsertRequest.patientTypeID = appState.getAuthenticatedUser()!.patientType;
applePayInsertRequest.patientOutSA = appState.getAuthenticatedUser()!.outSa;
applePayInsertRequest.appointmentDate = DateUtil.convertDateToString(widget.appointmentDate ?? DateTime.now());
applePayInsertRequest.appointmentNo = widget.appointmentNoVida;
applePayInsertRequest.orderDescription = "Ancillary Order Payment";
applePayInsertRequest.liveServiceID = "0";
applePayInsertRequest.latitude = "0.0";
applePayInsertRequest.longitude = "0.0";
applePayInsertRequest.amount = widget.totalAmount.toString();
applePayInsertRequest.isSchedule = "0";
applePayInsertRequest.language = appState.isArabic() ? 'ar' : 'en';
applePayInsertRequest.languageID = appState.isArabic() ? 1 : 2;
applePayInsertRequest.userName = appState.getAuthenticatedUser()!.patientId;
applePayInsertRequest.responseContinueURL = "http://hmg.com/Documents/success.html";
applePayInsertRequest.backClickUrl = "http://hmg.com/Documents/success.html";
applePayInsertRequest.paymentOption = "ApplePay";
applePayInsertRequest.isMobSDK = true;
applePayInsertRequest.merchantReference = transID;
applePayInsertRequest.merchantIdentifier = payfortViewModel.payfortProjectDetailsRespModel!.merchantIdentifier;
applePayInsertRequest.commandType = "PURCHASE";
applePayInsertRequest.signature = payfortViewModel.payfortProjectDetailsRespModel!.signature;
applePayInsertRequest.accessCode = payfortViewModel.payfortProjectDetailsRespModel!.accessCode;
applePayInsertRequest.shaRequestPhrase = payfortViewModel.payfortProjectDetailsRespModel!.shaRequest;
applePayInsertRequest.shaResponsePhrase = payfortViewModel.payfortProjectDetailsRespModel!.shaResponse;
applePayInsertRequest.returnURL = "";
try {
await payfortViewModel.applePayRequestInsert(applePayInsertRequest: applePayInsertRequest);
} catch (error) {
log("Apple Pay Insert Request Failed: $error");
Navigator.of(context).pop();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: "Failed to initialize Apple Pay. Please try again.".needTranslation),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
return;
}
// Only proceed with Apple Pay if insert was successful
payfortViewModel.paymentWithApplePay(
customerName: "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}",
customerEmail: "CustID_${appState.getAuthenticatedUser()!.patientId.toString()}@HMG.com",
orderDescription: "Ancillary Order Payment",
orderAmount: widget.totalAmount,
merchantReference: transID,
merchantIdentifier: payfortViewModel.payfortProjectDetailsRespModel!.merchantIdentifier,
applePayAccessCode: payfortViewModel.payfortProjectDetailsRespModel!.accessCode,
applePayShaRequestPhrase: payfortViewModel.payfortProjectDetailsRespModel!.shaRequest,
currency: appState.getAuthenticatedUser()!.outSa! == 0 ? "SAR" : "AED",
onFailed: (failureResult) async {
log("failureResult: ${failureResult.message.toString()}");
Navigator.of(context).pop();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: failureResult.message.toString()),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
},
onSucceeded: (successResult) async {
Navigator.of(context).pop();
log("successResult: ${successResult.responseMessage.toString()}");
selectedPaymentMethod = successResult.paymentOption ?? "VISA";
_checkPaymentStatus();
},
);
}
}

@ -0,0 +1,656 @@
import 'dart:async';
import 'package:collection/collection.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/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/todo_section/models/resp_models/ancillary_order_list_response_model.dart';
import 'package:hmg_patient_app_new/features/todo_section/models/resp_models/ancillary_order_procedures_detail_response_model.dart';
import 'package:hmg_patient_app_new/features/todo_section/todo_section_view_model.dart';
import 'package:hmg_patient_app_new/presentation/todo_section/ancillary_order_payment_page.dart';
import 'package:hmg_patient_app_new/presentation/todo_section/widgets/ancillary_orders_list.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/routes/custom_page_route.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
class AncillaryOrderDetailsList extends StatefulWidget {
final int appointmentNoVida;
final int orderNo;
final int projectID;
final String projectName;
const AncillaryOrderDetailsList({
super.key,
required this.appointmentNoVida,
required this.orderNo,
required this.projectID,
required this.projectName,
});
@override
State<AncillaryOrderDetailsList> createState() => _AncillaryOrderDetailsListState();
}
class _AncillaryOrderDetailsListState extends State<AncillaryOrderDetailsList> {
late TodoSectionViewModel todoSectionViewModel;
late AppState appState;
List<AncillaryOrderProcDetail> selectedProcedures = [];
@override
void initState() {
super.initState();
appState = getIt.get<AppState>();
todoSectionViewModel = context.read<TodoSectionViewModel>();
scheduleMicrotask(() async {
await todoSectionViewModel.getPatientOnlineAncillaryOrderDetailsProceduresList(
appointmentNoVida: widget.appointmentNoVida,
orderNo: widget.orderNo,
projectID: widget.projectID,
onSuccess: (response) {
_autoSelectEligibleProcedures();
},
);
});
}
void _autoSelectEligibleProcedures() {
selectedProcedures.clear();
if (todoSectionViewModel.patientAncillaryOrderProceduresList.isNotEmpty) {
final procedures = todoSectionViewModel.patientAncillaryOrderProceduresList[0].ancillaryOrderProcDetailsList;
if (procedures != null) {
for (var proc in procedures) {
if (!_isProcedureDisabled(proc)) {
selectedProcedures.add(proc);
}
}
}
}
setState(() {});
}
bool _isProcedureDisabled(AncillaryOrderProcDetail procedure) {
// return true;
return (procedure.isApprovalRequired == true && procedure.isApprovalCreated == false) ||
(procedure.isApprovalCreated == true && procedure.approvalNo == 0) ||
(procedure.isApprovalRequired == true && procedure.isApprovalCreated == true && procedure.approvalNo == 0);
}
bool _isProcedureSelected(AncillaryOrderProcDetail procedure) {
return selectedProcedures.contains(procedure);
}
void _toggleProcedureSelection(AncillaryOrderProcDetail procedure) {
setState(() {
if (_isProcedureSelected(procedure)) {
selectedProcedures.remove(procedure);
} else {
selectedProcedures.add(procedure);
}
});
}
String _getApprovalStatusText(AncillaryOrderProcDetail procedure) {
if (procedure.isApprovalRequired == false) {
return "Cash";
} else {
if (procedure.isApprovalCreated == true && procedure.approvalNo != 0) {
return "Approved";
} else if (procedure.isApprovalRequired == true && procedure.isApprovalCreated == true && procedure.approvalNo == 0) {
return "Approval Rejected - Please visit receptionist";
} else {
return "Sent For Approval";
}
}
}
double _getTotalAmount() {
double total = 0.0;
for (var proc in selectedProcedures) {
total += (proc.patientShareWithTax ?? 0);
}
return total;
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: Consumer<TodoSectionViewModel>(builder: (context, viewModel, child) {
AncillaryOrderProcedureItem? orderData;
if (viewModel.patientAncillaryOrderProceduresList.isNotEmpty) {
orderData = viewModel.patientAncillaryOrderProceduresList[0];
}
return Column(
children: [
Expanded(
child: CollapsingListView(
title: "Ancillary Order Details".needTranslation,
child: viewModel.isAncillaryDetailsProceduresLoading
? _buildLoadingShimmer().paddingSymmetrical(24.w, 0)
: viewModel.patientAncillaryOrderProceduresList.isEmpty
? _buildDefaultEmptyState(context).paddingSymmetrical(24.w, 0)
: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 16.h),
if (orderData != null) _buildPatientInfoCard(orderData),
SizedBox(height: 16.h),
if (orderData != null) _buildProceduresSection(orderData),
],
).paddingSymmetrical(24.w, 0),
),
),
),
if (orderData != null) _buildStickyPaymentButton(orderData),
],
);
}),
);
}
Widget _buildLoadingShimmer() {
return ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: 3,
itemBuilder: (context, index) {
return AncillaryOrderCard(
order: AncillaryOrderItem(),
isLoading: true,
);
},
);
}
Widget _buildDefaultEmptyState(BuildContext context) {
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: "No Procedures available for the selected order.".needTranslation,
isSmallWidget: true,
width: 62.w,
height: 62.h,
),
),
),
);
}
Widget _buildPatientInfoCard(AncillaryOrderProcedureItem orderData) {
final user = appState.getAuthenticatedUser();
final patientName = orderData.patientName ?? user?.firstName ?? "N/A";
final patientMRN = orderData.patientID ?? user?.patientId;
final nationalID = user?.patientIdentificationNo ?? "";
// Determine gender for profile image (assuming 1 = male, 2 = female)
final gender = user?.gender ?? 1;
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.r,
hasShadow: true,
),
child: Column(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Header Row with Profile Image, Name, and QR Code
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset(
gender == 1 ? AppAssets.male_img : AppAssets.femaleImg,
width: 56.w,
height: 56.h,
),
SizedBox(width: 12.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
patientName.toText18(
isBold: true,
weight: FontWeight.w600,
textOverflow: TextOverflow.ellipsis,
maxlines: 2,
),
],
),
),
],
),
SizedBox(height: 12.h),
Wrap(
alignment: WrapAlignment.start,
spacing: 4.w,
runSpacing: 4.h,
children: [
AppCustomChipWidget(
// icon: AppAssets.file_icon,
labelText: "MRN: ${patientMRN ?? 'N/A'}",
iconSize: 12.w,
),
// National ID
if (nationalID.isNotEmpty)
AppCustomChipWidget(
// icon: AppAssets.card_user,
labelText: "ID: $nationalID",
iconSize: 12.w,
),
// Appointment Number
if (orderData.appointmentNo != null)
AppCustomChipWidget(
// icon: AppAssets.calendar,
labelText: "Appt #: ${orderData.appointmentNo}",
iconSize: 12.w,
),
// Order Number
if (orderData.ancillaryOrderProcDetailsList?.firstOrNull?.orderNo != null)
AppCustomChipWidget(
labelText: "Order #: ${orderData.ancillaryOrderProcDetailsList!.first.orderNo}",
),
// Blood Group
if (user?.bloodGroup != null && user!.bloodGroup!.isNotEmpty)
AppCustomChipWidget(
// icon: AppAssets.blood_icon,
labelPadding: EdgeInsetsDirectional.only(start: -6.w, end: 8.w),
labelText: "Blood: ${user.bloodGroup}",
iconColor: AppColors.primaryRedColor,
),
// Insurance Company (if applicable)
if (orderData.companyName != null && orderData.companyName!.isNotEmpty)
AppCustomChipWidget(
icon: AppAssets.insurance_active_icon,
labelText: orderData.companyName!,
iconColor: AppColors.successColor,
backgroundColor: AppColors.successColor.withValues(alpha: 0.15),
iconSize: 12.w,
labelPadding: EdgeInsetsDirectional.only(start: -6.w, end: 8.w),
),
// Policy Number
if (orderData.insurancePolicyNo != null && orderData.insurancePolicyNo!.isNotEmpty)
AppCustomChipWidget(
labelText: "Policy: ${orderData.insurancePolicyNo}",
),
AppCustomChipWidget(
labelText: "Doctor: ${orderData.doctorName ?? "N/A"}",
),
if (widget.projectName.isNotEmpty)
AppCustomChipWidget(
labelText: widget.projectName,
),
if (orderData.clinicName != null && orderData.clinicName!.isNotEmpty)
AppCustomChipWidget(
labelText: "Clinic: ${orderData.clinicName!}",
),
if (orderData.clinicName != null && orderData.clinicName!.isNotEmpty)
AppCustomChipWidget(
labelText: "Date: ${DateFormat('MMM dd, yyyy').format(orderData.appointmentDate!)}",
),
],
),
// SizedBox(height: 12.h),
//
// // Additional Details Section
// Container(
// padding: EdgeInsets.all(12.h),
// decoration: BoxDecoration(
// color: AppColors.bgScaffoldColor,
// borderRadius: BorderRadius.circular(12.r),
// ),
// child: Column(
// children: [
// _buildInfoRow(
// "Doctor".needTranslation,
// orderData.doctorName ?? "N/A",
// ),
// if (orderData.clinicName != null && orderData.clinicName!.isNotEmpty)
// _buildInfoRow(
// "Clinic".needTranslation,
// orderData.clinicName!,
// ),
// if (orderData.appointmentDate != null)
// _buildInfoRow(
// "Appointment Date".needTranslation,
// DateFormat('MMM dd, yyyy').format(orderData.appointmentDate!),
// ),
// ],
// ),
// ),
],
).paddingOnly(top: 16.h, right: 16.w, left: 16.w, bottom: 12.h),
// Divider
Container(height: 1, color: AppColors.dividerColor),
// Summary Section
],
),
);
}
Widget _buildSummarySection(AncillaryOrderProcedureItem orderData) {
final totalProcedures = orderData.ancillaryOrderProcDetailsList?.length ?? 0;
final selectedCount = selectedProcedures.length;
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Procedures".needTranslation.toText12(
color: AppColors.textColorLight,
fontWeight: FontWeight.w600,
),
"$selectedCount of $totalProcedures selected".toText14(
isBold: true,
weight: FontWeight.bold,
),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
"Total Amount".needTranslation.toText12(
color: AppColors.textColorLight,
fontWeight: FontWeight.w600,
),
Row(
children: [
Utils.getPaymentAmountWithSymbol(
_getTotalAmount().toStringAsFixed(2).toText14(
isBold: true,
weight: FontWeight.bold,
color: AppColors.primaryRedColor,
),
AppColors.textColorLight,
13,
isSaudiCurrency: true,
),
//
// _getTotalAmount().toStringAsFixed(2).toText14(
// isBold: true,
// weight: FontWeight.bold,
// color: AppColors.primaryRedColor,
// ),
// SizedBox(width: 4.w),
// "SAR".toText12(color: AppColors.textColorLight),
],
),
],
),
],
);
}
Widget _buildInfoRow(String label, String value) {
return Padding(
padding: EdgeInsets.only(bottom: 8.h),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
flex: 2,
child: "$label:".toText12(color: AppColors.textColorLight, fontWeight: FontWeight.w600),
),
SizedBox(width: 8.w),
Expanded(
flex: 3,
child: value.toText12(color: AppColors.textColor, fontWeight: FontWeight.w600),
),
],
),
);
}
Widget _buildProceduresSection(AncillaryOrderProcedureItem orderData) {
if (orderData.ancillaryOrderProcDetailsList == null || orderData.ancillaryOrderProcDetailsList!.isEmpty) {
return SizedBox.shrink();
}
// Group procedures by category
final groupedProcedures = groupBy(
orderData.ancillaryOrderProcDetailsList!,
(AncillaryOrderProcDetail proc) => proc.procedureCategoryName ?? "Other",
);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: groupedProcedures.entries.map((entry) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
entry.key.toText18(isBold: true),
SizedBox(height: 12.h),
...entry.value.map((procedure) => _buildProcedureCard(procedure)),
SizedBox(height: 16.h),
],
);
}).toList(),
);
}
Widget _buildProcedureCard(AncillaryOrderProcDetail procedure) {
final isDisabled = _isProcedureDisabled(procedure);
// final isDisabled = _isProcedureDisabled(procedure);
final isSelected = _isProcedureSelected(procedure);
return AnimationConfiguration.staggeredList(
position: 0,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
margin: EdgeInsets.only(bottom: 12.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: isDisabled ? AppColors.greyColor : AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: !isDisabled,
),
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: isDisabled ? null : () => _toggleProcedureSelection(procedure),
borderRadius: BorderRadius.circular(24.h),
child: Container(
padding: EdgeInsets.all(14.h),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(24.h)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (!isDisabled)
Padding(
padding: EdgeInsets.only(right: 8.w),
child: Checkbox(
value: isSelected,
onChanged: (v) => _toggleProcedureSelection(procedure),
activeColor: AppColors.primaryRedColor,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
visualDensity: VisualDensity.compact,
),
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
(procedure.procedureName ?? "N/A").toText14(isBold: true, maxlines: 2),
],
),
),
],
),
SizedBox(height: 8.h),
Wrap(
direction: Axis.horizontal,
spacing: 3.h,
runSpacing: 8.h,
children: [
AppCustomChipWidget(
labelText: _getApprovalStatusText(procedure),
// backgroundColor: ,
),
// if (procedure.procedureID != null)
// AppCustomChipWidget(
// labelText: "ID: ${procedure.procedureID}",
// ),
if (procedure.isCovered == true)
AppCustomChipWidget(
labelText: "Covered".needTranslation,
backgroundColor: AppColors.successColor.withValues(alpha: 0.1),
textColor: AppColors.successColor,
),
],
),
SizedBox(height: 12.h),
Container(height: 1, color: AppColors.dividerColor),
SizedBox(height: 12.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Price".needTranslation.toText10(color: AppColors.textColorLight),
SizedBox(height: 4.h),
Row(
children: [
Utils.getPaymentAmountWithSymbol(
(procedure.patientShare ?? 0).toStringAsFixed(2).toText13(weight: FontWeight.w600),
AppColors.textColorLight,
13,
isSaudiCurrency: true,
),
],
),
],
),
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"VAT (15%)".needTranslation.toText10(color: AppColors.textColorLight),
SizedBox(height: 4.h),
Row(
children: [
Utils.getPaymentAmountWithSymbol(
(procedure.patientTaxAmount ?? 0).toStringAsFixed(2).toText13(weight: FontWeight.w600),
AppColors.textColorLight,
13,
isSaudiCurrency: true,
),
],
),
],
),
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Total".needTranslation.toText10(color: AppColors.textColorLight),
SizedBox(height: 4.h),
Row(
children: [
Utils.getPaymentAmountWithSymbol(
(procedure.patientShareWithTax ?? 0).toStringAsFixed(2).toText13(weight: FontWeight.w600),
AppColors.textColorLight,
13,
isSaudiCurrency: true,
),
],
),
],
),
),
],
),
],
),
),
),
),
),
),
));
}
Widget _buildStickyPaymentButton(orderData) {
final isButtonEnabled = selectedProcedures.isNotEmpty;
return Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(height: 16.h),
_buildSummarySection(orderData),
SizedBox(height: 16.h),
CustomButton(
borderWidth: 0,
backgroundColor: AppColors.infoLightColor,
text: "Proceed to Payment".needTranslation,
onPressed: () {
// Navigate to payment page with selected procedures
Navigator.of(context).push(
CustomPageRoute(
page: AncillaryOrderPaymentPage(
appointmentNoVida: widget.appointmentNoVida,
orderNo: widget.orderNo,
projectID: widget.projectID,
selectedProcedures: selectedProcedures,
totalAmount: _getTotalAmount(),
appointmentDate: orderData.appointmentDate,
),
),
);
},
isDisabled: !isButtonEnabled,
textColor: AppColors.whiteColor,
borderRadius: 12.r,
borderColor: Colors.transparent,
padding: EdgeInsets.symmetric(vertical: 16.h),
),
SizedBox(height: 22.h),
],
).paddingSymmetrical(24.w, 0);
}
}

@ -0,0 +1,88 @@
import 'dart:async';
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/todo_section/models/resp_models/ancillary_order_list_response_model.dart';
import 'package:hmg_patient_app_new/features/todo_section/todo_section_view_model.dart';
import 'package:hmg_patient_app_new/presentation/todo_section/ancillary_procedures_details_page.dart';
import 'package:hmg_patient_app_new/presentation/todo_section/widgets/ancillary_orders_list.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:provider/provider.dart';
class ToDoPage extends StatefulWidget {
const ToDoPage({super.key});
@override
State<ToDoPage> createState() => _ToDoPageState();
}
class _ToDoPageState extends State<ToDoPage> {
@override
void initState() {
final TodoSectionViewModel todoSectionViewModel = context.read<TodoSectionViewModel>();
scheduleMicrotask(() async {
await todoSectionViewModel.initializeTodoSectionViewModel();
});
super.initState();
}
@override
void dispose() {
super.dispose();
}
Widget _buildLoadingShimmer() {
return ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: 3,
itemBuilder: (context, index) {
return AncillaryOrderCard(
order: AncillaryOrderItem(),
isLoading: true,
);
},
);
}
@override
Widget build(BuildContext context) {
return CollapsingListView(
title: "ToDo List".needTranslation,
isLeading: false,
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 16.h),
"Ancillary Orders".needTranslation.toText18(isBold: true),
Consumer<TodoSectionViewModel>(
builder: (BuildContext context, TodoSectionViewModel todoSectionViewModel, Widget? child) {
return todoSectionViewModel.isAncillaryOrdersLoading
? _buildLoadingShimmer()
: AncillaryOrdersList(
orders: todoSectionViewModel.patientAncillaryOrdersList,
onCheckIn: (order) => log("Check-in for order: ${order.orderNo}"),
onViewDetails: (order) async {
Navigator.of(context).push(CustomPageRoute(
page: AncillaryOrderDetailsList(
appointmentNoVida: order.appointmentNo ?? 0,
orderNo: order.orderNo ?? 0,
projectID: order.projectID ?? 0,
projectName: order.projectName ?? "",
)));
log("View details for order: ${order.orderNo}");
},
);
},
),
],
).paddingSymmetrical(24.w, 0),
),
);
}
}

@ -0,0 +1,284 @@
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/todo_section/models/resp_models/ancillary_order_list_response_model.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
class AncillaryOrdersList extends StatelessWidget {
final List<AncillaryOrderItem> orders;
final Function(AncillaryOrderItem order)? onCheckIn;
final Function(AncillaryOrderItem order)? onViewDetails;
const AncillaryOrdersList({
super.key,
required this.orders,
this.onCheckIn,
this.onViewDetails,
});
@override
Widget build(BuildContext context) {
// Show empty state
if (orders.isEmpty) {
return _buildDefaultEmptyState(context);
}
// Show orders list
return ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: orders.length,
separatorBuilder: (BuildContext context, int index) => SizedBox(height: 12.h),
itemBuilder: (context, index) {
final order = orders[index];
return AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
child: AncillaryOrderCard(
order: order,
isLoading: false,
onCheckIn: onCheckIn != null ? () => onCheckIn!(order) : null,
onViewDetails: onViewDetails != null ? () => onViewDetails!(order) : null,
)),
),
),
);
},
);
}
Widget _buildDefaultEmptyState(BuildContext context) {
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 ancillary orders yet.".needTranslation,
isSmallWidget: true,
width: 62.w,
height: 62.h,
),
),
),
);
}
}
class AncillaryOrderCard extends StatelessWidget {
const AncillaryOrderCard({
super.key,
required this.order,
this.isLoading = false,
this.onCheckIn,
this.onViewDetails,
});
final AncillaryOrderItem order;
final bool isLoading;
final VoidCallback? onCheckIn;
final VoidCallback? onViewDetails;
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.only(bottom: 12.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: false,
),
child: Padding(
padding: EdgeInsets.all(14.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Header Row with Order Number and Date
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// Row(
// children: [
// if (!isLoading)
// "Order #".needTranslation.toText14(
// color: AppColors.textColorLight,
// weight: FontWeight.w500,
// ),
// SizedBox(width: 4.w),
// (isLoading ? "12345" : "${order.orderNo ?? '-'}").toText16(isBold: true).toShimmer2(isShow: isLoading),
// ],
// ),
// if (order.orderDate != null || isLoading)
// (isLoading ? "Jan 15, 2024" : DateFormat('MMM dd, yyyy').format(order.orderDate!))
// .toText12(color: AppColors.textColorLight)
// .toShimmer2(isShow: isLoading),
// ],
// ),
SizedBox(height: 12.h),
// Doctor and Clinic Info
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
if (!isLoading) ...[
Image.network(
"https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown.png",
width: 40.w,
height: 40.h,
fit: BoxFit.cover,
).circle(100.r),
SizedBox(width: 12.w),
],
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Doctor Name
if (order.doctorName != null || isLoading)
(isLoading ? "Dr. John Smith" : order.doctorName!)
.toString()
.toText14(isBold: true, maxlines: 2)
.toShimmer2(isShow: isLoading),
SizedBox(height: 4.h),
],
),
),
],
),
SizedBox(height: 12.h),
// Chips for Appointment Info and Status
Wrap(
direction: Axis.horizontal,
spacing: 3.h,
runSpacing: 4.h,
children: [
// projectName
if (order.projectName != null || isLoading)
AppCustomChipWidget(
labelText: order.projectName ?? '-',
).toShimmer2(isShow: isLoading),
// orderNo
if (order.orderNo != null || isLoading)
AppCustomChipWidget(
// icon: AppAssets.calendar,
labelText: "${"Order# :".needTranslation}${order.orderNo ?? '-'}",
).toShimmer2(isShow: isLoading),
// Appointment Date
if (order.appointmentDate != null || isLoading)
AppCustomChipWidget(
icon: AppAssets.calendar,
labelText:
isLoading ? "Date: Jan 20, 2024" : "Date: ${DateFormat('MMM dd, yyyy').format(order.appointmentDate!)}".needTranslation,
).toShimmer2(isShow: isLoading),
// Appointment Number
if (order.appointmentNo != null || isLoading)
AppCustomChipWidget(
labelText: isLoading ? "Appt# : 98765" : "Appt #: ${order.appointmentNo}".needTranslation,
).toShimmer2(isShow: isLoading),
// Invoice Number
if (order.invoiceNo != null || isLoading)
AppCustomChipWidget(
labelText: isLoading ? "Invoice: 45678" : "Invoice: ${order.invoiceNo}".needTranslation,
).toShimmer2(isShow: isLoading),
// Queued Status
if (order.isQueued == true || isLoading)
AppCustomChipWidget(
labelText: "Queued".needTranslation,
).toShimmer2(isShow: isLoading),
// Check-in Available Status
if (order.isCheckInAllow == true || isLoading)
AppCustomChipWidget(
labelText: "Check-in Ready".needTranslation,
).toShimmer2(isShow: isLoading),
],
),
SizedBox(height: 12.h),
// Action Buttons
Row(
children: [
// Check-in Button (if available)
if (order.isCheckInAllow == true || isLoading)
Expanded(
child: CustomButton(
text: "Check In".needTranslation,
onPressed: () {
if (isLoading) {
return;
} else if (onCheckIn != null) {
onCheckIn!();
}
},
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor,
fontSize: 14.f,
fontWeight: FontWeight.w500,
borderRadius: 10.r,
padding: EdgeInsets.fromLTRB(10.h, 0, 10.h, 0),
height: 40.h,
).toShimmer2(isShow: isLoading),
),
if (order.isCheckInAllow == true || isLoading) SizedBox(width: 8.w),
// View Details Button
Expanded(
child: CustomButton(
text: "View Details".needTranslation,
onPressed: () {
if (isLoading) {
return;
} else if (onViewDetails != null) {
onViewDetails!();
}
},
backgroundColor: Color(0xffFEE9EA),
borderColor: Color(0xffFEE9EA),
textColor: Color(0xffED1C2B),
fontSize: 14.f,
fontWeight: FontWeight.w500,
borderRadius: 10.r,
padding: EdgeInsets.fromLTRB(10.h, 0, 10.h, 0),
height: 40.h,
iconSize: 15.h,
).toShimmer2(isShow: isLoading),
),
],
),
],
),
),
);
}
}

@ -0,0 +1,274 @@
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/todo_section/models/resp_models/ancillary_order_list_response_model.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
class AncillaryProceduresList extends StatelessWidget {
final List<AncillaryOrderItem> orders;
final Function(AncillaryOrderItem order)? onCheckIn;
final Function(AncillaryOrderItem order)? onViewDetails;
const AncillaryProceduresList({
super.key,
required this.orders,
this.onCheckIn,
this.onViewDetails,
});
@override
Widget build(BuildContext context) {
// Show empty state
if (orders.isEmpty) {
return _buildDefaultEmptyState(context);
}
// Show orders list
return ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: orders.length,
itemBuilder: (context, index) {
final order = orders[index];
return AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
child: AncillaryOrderCard(
order: order,
isLoading: false,
onCheckIn: onCheckIn != null ? () => onCheckIn!(order) : null,
onViewDetails: onViewDetails != null ? () => onViewDetails!(order) : null,
)),
),
),
);
},
);
}
Widget _buildDefaultEmptyState(BuildContext context) {
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 ancillary orders yet.".needTranslation,
isSmallWidget: true,
width: 62.w,
height: 62.h,
),
),
),
);
}
}
class AncillaryOrderCard extends StatelessWidget {
const AncillaryOrderCard({
super.key,
required this.order,
this.isLoading = false,
this.onCheckIn,
this.onViewDetails,
});
final AncillaryOrderItem order;
final bool isLoading;
final VoidCallback? onCheckIn;
final VoidCallback? onViewDetails;
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.only(bottom: 12.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: false,
),
child: Padding(
padding: EdgeInsets.all(14.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Header Row with Order Number and Date
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
"Order #".needTranslation.toText14(
color: AppColors.textColorLight,
weight: FontWeight.w500,
),
SizedBox(width: 4.w),
(isLoading ? "12345" : "${order.orderNo ?? '-'}").toText16(isBold: true).toShimmer2(isShow: isLoading),
],
),
if (order.orderDate != null || isLoading)
(isLoading ? "Jan 15, 2024" : DateFormat('MMM dd, yyyy').format(order.orderDate!))
.toText12(color: AppColors.textColorLight)
.toShimmer2(isShow: isLoading),
],
),
SizedBox(height: 12.h),
// Doctor and Clinic Info
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Doctor Name
if (order.doctorName != null || isLoading)
(isLoading ? "Dr. John Smith" : order.doctorName!)
.toString()
.toText14(isBold: true, maxlines: 2)
.toShimmer2(isShow: isLoading),
SizedBox(height: 4.h),
// Clinic Name
if (order.clinicName != null || isLoading)
(isLoading ? "Cardiology Clinic" : order.clinicName!)
.toString()
.toText12(
fontWeight: FontWeight.w500,
color: AppColors.greyTextColor,
maxLine: 2,
)
.toShimmer2(isShow: isLoading),
],
),
),
],
),
SizedBox(height: 12.h),
// Chips for Appointment Info and Status
Wrap(
direction: Axis.horizontal,
spacing: 3.h,
runSpacing: 4.h,
children: [
// Appointment Date
if (order.appointmentDate != null || isLoading)
AppCustomChipWidget(
icon: AppAssets.calendar,
labelText:
isLoading ? "Date: Jan 20, 2024" : "Date: ${DateFormat('MMM dd, yyyy').format(order.appointmentDate!)}".needTranslation,
).toShimmer2(isShow: isLoading),
// Appointment Number
if (order.appointmentNo != null || isLoading)
AppCustomChipWidget(
labelText: isLoading ? "Appt #: 98765" : "Appt #: ${order.appointmentNo}".needTranslation,
).toShimmer2(isShow: isLoading),
// Invoice Number
if (order.invoiceNo != null || isLoading)
AppCustomChipWidget(
labelText: isLoading ? "Invoice: 45678" : "Invoice: ${order.invoiceNo}".needTranslation,
).toShimmer2(isShow: isLoading),
// Queued Status
if (order.isQueued == true || isLoading)
AppCustomChipWidget(
labelText: "Queued".needTranslation,
).toShimmer2(isShow: isLoading),
// Check-in Available Status
if (order.isCheckInAllow == true || isLoading)
AppCustomChipWidget(
labelText: "Check-in Ready".needTranslation,
).toShimmer2(isShow: isLoading),
],
),
SizedBox(height: 12.h),
// Action Buttons
Row(
children: [
// Check-in Button (if available)
if (order.isCheckInAllow == true || isLoading)
Expanded(
child: CustomButton(
text: "Check In".needTranslation,
onPressed: () {
if (isLoading) {
return;
} else if (onCheckIn != null) {
onCheckIn!();
}
},
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor,
fontSize: 14.f,
fontWeight: FontWeight.w500,
borderRadius: 10.r,
padding: EdgeInsets.fromLTRB(10.h, 0, 10.h, 0),
height: 40.h,
).toShimmer2(isShow: isLoading),
),
if (order.isCheckInAllow == true || isLoading) SizedBox(width: 8.w),
// View Details Button
Expanded(
child: CustomButton(
text: "View Details".needTranslation,
onPressed: () {
if (isLoading) {
return;
} else if (onViewDetails != null) {
onViewDetails!();
}
},
backgroundColor: Color(0xffFEE9EA),
borderColor: Color(0xffFEE9EA),
textColor: Color(0xffED1C2B),
fontSize: 14.f,
fontWeight: FontWeight.w500,
borderRadius: 10.r,
padding: EdgeInsets.fromLTRB(10.h, 0, 10.h, 0),
height: 40.h,
icon: AppAssets.arrow_forward,
iconColor: AppColors.primaryRedColor,
iconSize: 15.h,
).toShimmer2(isShow: isLoading),
),
],
),
],
),
),
);
}
}

@ -18,7 +18,7 @@ class AppNav{
if(tabIndex == 3)
nav_name = "my family";
if(tabIndex == 4)
nav_name = "todo list";
nav_name = "todo_section list";
if(tabIndex == 5)
nav_name = "help";

@ -68,7 +68,7 @@ class TodoList{
// to_do_list_confirm_appointment(AppoitmentAllHistoryResultList appointment){
// logger('confirm_appointment', parameters: {
// 'appointment_type' : appointment.isLiveCareAppointment! ? 'livecare' : 'regular',
// 'flow_type' : 'todo list',
// 'flow_type' : 'todo_section list',
// 'clinic_type_online' : appointment.clinicName,
// 'hospital_name' : appointment.projectName,
// 'doctor_name' : (appointment.doctorName == null || appointment.doctorName == '') ? appointment.doctorNameObj : appointment.doctorName,

@ -95,7 +95,7 @@ class CacheServiceImp implements CacheService {
if (string == null) return null;
return json.decode(string);
} catch (ex) {
loggerService.errorLogs(ex.toString());
loggerService.logError(ex.toString());
return null;
}
}
@ -105,7 +105,7 @@ class CacheServiceImp implements CacheService {
try {
await sharedPreferences.setString(key, json.encode(value));
} catch (ex) {
loggerService.errorLogs(ex.toString());
loggerService.logError(ex.toString());
}
}

@ -25,33 +25,33 @@ class ErrorHandlerServiceImp implements ErrorHandlerService {
@override
Future<void> handleError({required Failure failure, Function()? onOkPressed, Function(Failure)? onUnHandledFailure, Function(Failure)? onMessageStatusFailure}) async {
if (failure is APIException) {
loggerService.errorLogs("API Exception: ${failure.message}");
loggerService.logError("API Exception: ${failure.message}");
} else if (failure is ServerFailure) {
loggerService.errorLogs("URL: ${failure.url} \n Server Failure: ${failure.message}");
loggerService.logError("URL: ${failure.url} \n Server Failure: ${failure.message}");
await _showDialog(failure, title: "Server Failure");
} else if (failure is DataParsingFailure) {
loggerService.errorLogs("Data Parsing Failure: ${failure.message}");
loggerService.logError("Data Parsing Failure: ${failure.message}");
await _showDialog(failure, title: "Data Error");
} else if (failure is StatusCodeFailure) {
loggerService.errorLogs("StatusCode Failure: ${failure.message}");
loggerService.logError("StatusCode Failure: ${failure.message}");
await _showDialog(failure, title: "StatusCodeFailure");
} else if (failure is ConnectivityFailure) {
loggerService.errorLogs("ConnectivityFailure : ${failure.message}");
loggerService.logError("ConnectivityFailure : ${failure.message}");
await _showDialog(failure, title: "ConnectivityFailure ", onOkPressed: () {});
} else if (failure is UnAuthenticatedUserFailure) {
loggerService.errorLogs("URL: ${failure.url} \n UnAuthenticatedUser Failure: ${failure.message}");
loggerService.logError("URL: ${failure.url} \n UnAuthenticatedUser Failure: ${failure.message}");
await _showDialog(failure, title: "UnAuthenticatedUser Failure", onOkPressed: () => navigationService.replaceAllRoutesAndNavigateToLanding());
} else if (failure is AppUpdateFailure) {
loggerService.errorLogs("AppUpdateFailure : ${failure.message}");
loggerService.logError("AppUpdateFailure : ${failure.message}");
await _showDialog(failure, title: "AppUpdateFailure Error", onOkPressed: () => navigationService.replaceAllRoutesAndNavigateToLanding());
} else if (failure is HttpException) {
loggerService.errorLogs("Http Exception: ${failure.message}");
loggerService.logError("Http Exception: ${failure.message}");
await _showDialog(failure, title: "Network Error");
} else if (failure is UnknownFailure) {
loggerService.errorLogs("URL: ${failure.url} \n Unknown Failure: ${failure.message}");
loggerService.logError("URL: ${failure.url} \n Unknown Failure: ${failure.message}");
await _showDialog(failure, title: "Unknown Failure");
} else if (failure is InvalidCredentials) {
loggerService.errorLogs("Invalid Credentials : ${failure.message}");
loggerService.logError("Invalid Credentials : ${failure.message}");
await _showDialog(failure, title: "Invalid Credentials ");
} else if (failure is UserIntimationFailure) {
if (onUnHandledFailure != null) {
@ -66,7 +66,7 @@ class ErrorHandlerServiceImp implements ErrorHandlerService {
await _showDialog(failure, title: "MessageStatusFailure", onOkPressed: onOkPressed);
}
} else {
loggerService.errorLogs("Unhandled failure type: $failure");
loggerService.logError("Unhandled failure type: $failure");
await _showDialog(failure, title: "Unhandled Error", onOkPressed: onOkPressed);
}
}

@ -1,7 +1,7 @@
import 'package:logger/logger.dart';
abstract class LoggerService {
void errorLogs(String message);
void logError(String message);
void logInfo(String message);
}
@ -12,7 +12,7 @@ class LoggerServiceImp implements LoggerService {
LoggerServiceImp({required this.logger});
@override
void errorLogs(String message) {
void logError(String message) {
logger.e(message);
}

@ -64,17 +64,17 @@ class CustomButton extends StatelessWidget {
width: width,
padding: padding,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: isDisabled ? backgroundColor.withOpacity(.5) : backgroundColor,
color: isDisabled ? backgroundColor.withValues(alpha: .5) : backgroundColor,
borderRadius: radius,
customBorder: BorderRadius.circular(radius),
side: borderSide ?? BorderSide(width: borderWidth.h, color: isDisabled ? borderColor.withValues(alpha: 0.5) : borderColor)),
side: borderSide ?? BorderSide(width: borderWidth.h, color: borderColor)),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (icon != null)
Padding(
padding: text.isNotEmpty ? EdgeInsets.only(right: 8.h, left: 8.h) : EdgeInsets.zero,
padding: text.isNotEmpty ? EdgeInsets.only(right: 6.w, left: 6.w) : EdgeInsets.zero,
child: Utils.buildSvgWithAssets(icon: icon!, iconColor: iconColor, isDisabled: isDisabled, width: iconS, height: iconS),
),
Visibility(
@ -86,7 +86,7 @@ class CustomButton extends StatelessWidget {
overflow: textOverflow,
style: context.dynamicTextStyle(
fontSize: fontS,
color: isDisabled ? textColor.withOpacity(0.5) : textColor,
color: isDisabled ? textColor.withValues(alpha: 0.5) : textColor,
letterSpacing: 0,
fontWeight: fontWeight,
),

@ -105,15 +105,15 @@ class ButtonSheetContent extends StatelessWidget {
}
void showCommonBottomSheetWithoutHeight(
BuildContext context, {
required Widget child,
required VoidCallback callBackFunc,
String title = "",
bool isCloseButtonVisible = true,
bool isFullScreen = true,
bool isDismissible = true,
Widget? titleWidget,
bool useSafeArea = false,
BuildContext context, {
required Widget child,
VoidCallback? callBackFunc,
String title = "",
bool isCloseButtonVisible = true,
bool isFullScreen = true,
bool isDismissible = true,
Widget? titleWidget,
bool useSafeArea = false,
bool hasBottomPadding = true,
Color backgroundColor = AppColors.bottomSheetBgColor,
VoidCallback? onCloseClicked
@ -158,6 +158,7 @@ void showCommonBottomSheetWithoutHeight(
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@ -167,27 +168,29 @@ void showCommonBottomSheetWithoutHeight(
Expanded(
child: title.toText20(weight: FontWeight.w600),
),
Utils.buildSvgWithAssets(
icon: AppAssets.close_bottom_sheet_icon,
iconColor: Color(0xff2B353E),
).onPress(() {
onCloseClicked?.call();
if (isCloseButtonVisible) ...[
Utils.buildSvgWithAssets(
icon: AppAssets.close_bottom_sheet_icon,
iconColor: Color(0xff2B353E),
).onPress(() {
onCloseClicked?.call();
Navigator.of(context).pop();
}),
}),],
],
),
SizedBox(height: 16.h),
child,
],
),
)
: child,
),
),
),
);
},
).then((value) {
callBackFunc();
if (callBackFunc != null) {
callBackFunc();
}
});
}

Loading…
Cancel
Save