Merge branch 'master' into fatima

fatima
Fatimah.Alshammari 3 weeks ago
commit 0d6d23dbbc

@ -1496,10 +1496,6 @@
"someRemarksAboutPrescription": "ستجدون هنا بعض الملاحظات حول الوصفة الطبية",
"notifyMeBeforeConsumptionTime": "أبلغني قبل وقت الاستهلاك",
"noMedicationsToday": "لا أدوية اليوم",
"route": "Route: {route}",
"frequency": "Frequency: {frequency}",
"instruction": "Instruction: {instruction}",
"duration": "Duration: {days}",
"reminders": "تذكيرات",
"reminderAddedToCalendar": "تمت إضافة تذكير إلى التقويم ✅",
"errorWhileSettingCalendar": "حدث خطأ أثناء ضبط التقويم:{error}",

@ -1487,10 +1487,8 @@
"someRemarksAboutPrescription": "some remarks about the prescription will be here",
"notifyMeBeforeConsumptionTime": "Notify me before the consumption time",
"noMedicationsToday": "No medications today",
"route": "Route: {route}",
"frequency": "Frequency: {frequency}",
"instruction": "Instruction: {instruction}",
"duration": "Duration: {days}",
"route": "Route",
"frequency": "Frequency",
"reminders": "Reminders",
"reminderAddedToCalendar": "Reminder added to calendar ✅",
"errorWhileSettingCalendar": "Error while setting calendar: {error}",

@ -182,9 +182,8 @@ class ApiClientImp implements ApiClient {
}
// body['TokenID'] = "@dm!n";
// body['PatientID'] = 4769038;
// body['PatientID'] = 4768663;
// body['PatientTypeID'] = 1;
//
// body['PatientOutSA'] = 0;
// body['SessionID'] = "45786230487560q";
}

@ -680,7 +680,7 @@ const DASHBOARD = 'Services/Patients.svc/REST/PatientDashboard';
class ApiConsts {
static const maxSmallScreen = 660;
static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.prod;
static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.uat;
// static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT

@ -977,4 +977,5 @@ class Utils {
return checkDate == today;
}
}

@ -605,7 +605,7 @@ class AuthenticationViewModel extends ChangeNotifier {
}
// _appState.setUserBloodGroup = (activation.patientBlodType ?? "");
_appState.setAppAuthToken = activation.authenticationTokenId;
myAppointmentsVM.getActiveAppointmentsCount();
// myAppointmentsVM.getActiveAppointmentsCount();
final request = RequestUtils.getAuthanticatedCommonRequest().toJson();
bool isUserAgreedBefore = await checkIfUserAgreedBefore(request: request);

@ -30,6 +30,8 @@ abstract class BookAppointmentsRepo {
Future<Either<Failure, GenericApiModel<DoctorsProfileResponseModel>>> getDoctorProfile(int clinicID, int projectID, int doctorId, {Function(dynamic)? onSuccess, Function(String)? onError});
Future<Either<Failure, GenericApiModel<dynamic>>> getDoctorRatingDetails(int doctorId, {Function(dynamic)? onSuccess, Function(String)? onError});
Future<Either<Failure, GenericApiModel<dynamic>>> getDoctorFreeSlots(int clinicID, int projectID, int doctorId, bool isBookingForLiveCare,
{bool continueDentalPlan = false, Function(dynamic)? onSuccess, Function(String)? onError});
@ -298,6 +300,52 @@ class BookAppointmentsRepoImp implements BookAppointmentsRepo {
}
}
@override
Future<Either<Failure, GenericApiModel<dynamic>>> getDoctorRatingDetails(int doctorId, {Function(dynamic)? onSuccess, Function(String)? onError}) async {
Map<String, dynamic> mapDevice = {
"DoctorID": doctorId,
"PatientID": 0,
"License": true,
"IsRegistered": true,
"isDentalAllowedBackend": false,
};
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(
GET_DOCTOR_RATING_DETAILS,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
if (onError != null) {
onError(error);
}
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: response,
);
if (onSuccess != null) {
onSuccess(response);
}
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
//TODO: Implement the logic for Dental & laser clinics
@override
Future<Either<Failure, GenericApiModel<dynamic>>> getDoctorFreeSlots(int clinicID, int projectID, int doctorId, bool isBookingForLiveCare,

@ -17,6 +17,7 @@ import 'package:hmg_patient_app_new/features/book_appointments/models/free_slot.
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/appointment_nearest_gate_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/dental_chief_complaints_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctor_profile_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctor_rating_details_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_clinic_list_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_patient_dental_plan_response_model.dart';
@ -92,6 +93,9 @@ class BookAppointmentsViewModel extends ChangeNotifier {
late DoctorsProfileResponseModel doctorsProfileResponseModel;
bool isDoctorRatingDetailsLoading = false;
List<DoctorRateDetails> doctorDetailsList = [];
List<FreeSlot> slotsList = [];
List<TimeSlot> docFreeSlots = [];
List<TimeSlot> dayEvents = [];
@ -340,6 +344,14 @@ class BookAppointmentsViewModel extends ChangeNotifier {
notifyListeners();
}
setIsLiveCareDoctorsListLoading(bool value) {
if (value) {
liveCareDoctorsList.clear();
}
isDoctorsListLoading = value;
notifyListeners();
}
setIsClinicsListLoading(bool value) {
if (value) {
clinicsList.clear();
@ -467,10 +479,10 @@ class BookAppointmentsViewModel extends ChangeNotifier {
}
Future<void> getLiveCareDoctorsList({Function(dynamic)? onSuccess, Function(String)? onError}) async {
doctorsList.clear();
liveCareDoctorsList.clear();
notifyListeners();
final result =
await bookAppointmentsRepo.getLiveCareDoctorsList(selectedLiveCareClinic.serviceID!, _appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!, onError: onError);
result.fold(
(failure) async {
onError!(LocaleKeys.noDoctorFound.tr());
@ -614,6 +626,46 @@ class BookAppointmentsViewModel extends ChangeNotifier {
);
}
Future<void> getDoctorRatingDetails({Function(dynamic)? onSuccess, Function(String)? onError}) async {
isDoctorRatingDetailsLoading = true;
doctorDetailsList.clear();
notifyListeners();
final result = await bookAppointmentsRepo.getDoctorRatingDetails(selectedDoctor.doctorID ?? 0, onSuccess: onSuccess, onError: onError);
result.fold(
(failure) async {
if (onError != null) {
onError("Failed to load doctor rating details");
}
},
(apiResponse) {
if (apiResponse.messageStatus == 2) {
if (onError != null) {
onError(apiResponse.errorMessage ?? "Unknown error occurred");
}
isDoctorRatingDetailsLoading = false;
} else if (apiResponse.messageStatus == 1) {
try {
apiResponse.data['DoctorRatingDetailsList'].forEach((v) {
doctorDetailsList.add(DoctorRateDetails.fromJson(v));
});
isDoctorRatingDetailsLoading = false;
// doctorRatingDetails = DoctorRateDetails.fromJson(apiResponse.data);
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse.data);
}
} catch (e) {
if (onError != null) {
onError("Failed to parse rating details: ${e.toString()}");
}
}
}
},
);
}
Future<void> getDoctorFreeSlots({bool isBookingForLiveCare = false, Function(dynamic)? onSuccess, Function(String)? onError}) async {
docFreeSlots.clear();
DateTime date;

@ -0,0 +1,36 @@
class DoctorRateDetails {
dynamic doctorID;
dynamic projectID;
dynamic clinicID;
dynamic rate;
dynamic patientNumber;
dynamic ratio;
DoctorRateDetails(
{this.doctorID,
this.projectID,
this.clinicID,
this.rate,
this.patientNumber,
this.ratio});
DoctorRateDetails.fromJson(Map<String, dynamic> json) {
doctorID = json['DoctorID'];
projectID = json['ProjectID'];
clinicID = json['ClinicID'];
rate = json['Rate'];
patientNumber = json['PatientNumber'];
ratio = json['Ratio'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['DoctorID'] = this.doctorID;
data['ProjectID'] = this.projectID;
data['ClinicID'] = this.clinicID;
data['Rate'] = this.rate;
data['PatientNumber'] = this.patientNumber;
data['Ratio'] = this.ratio;
return data;
}
}

@ -9,6 +9,7 @@ class HabibWalletViewModel extends ChangeNotifier {
bool isWalletAmountLoading = false;
num habibWalletAmount = 0;
num walletRechargeAmount = 0;
String notesText = "";
bool isBottomSheetContentLoading = false;
@ -60,6 +61,11 @@ class HabibWalletViewModel extends ChangeNotifier {
notifyListeners();
}
setNotesText(String notes) {
notesText = notes;
notifyListeners();
}
setDepositorDetails(String fileNum, String depositor, String mobile) {
fileNumber = fileNum;
depositorName = depositor;

@ -7,6 +7,7 @@ import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patien
import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_card_history.dart';
import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_details_response_model.dart';
import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_update_response_model.dart';
import 'package:hmg_patient_app_new/features/insurance/models/resp_models/upload_insurance_card_response_model.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
abstract class InsuranceRepo {
@ -17,6 +18,14 @@ abstract class InsuranceRepo {
Future<Either<Failure, GenericApiModel<PatientInsuranceUpdateResponseModel>>> getPatientInsuranceDetailsForUpdate({required String patientId, required String identificationNo});
Future<Either<Failure, GenericApiModel<List<InsuranceApprovalResponseModel>>>> getPatientInsuranceApprovalsList();
Future<Either<Failure, GenericApiModel<dynamic>>> updatePatientInsuranceCard({
required int patientID,
required int patientType,
required String mobileNo,
required String patientIdentificationID,
required String insuranceCardImage,
});
}
class InsuranceRepoImp implements InsuranceRepo {
@ -183,4 +192,52 @@ class InsuranceRepoImp implements InsuranceRepo {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<dynamic>>> updatePatientInsuranceCard({
required int patientID,
required int patientType,
required String mobileNo,
required String patientIdentificationID,
required String insuranceCardImage,
}) async {
Map<String, dynamic> mapDevice = {
"PatientID": patientID,
"PatientType": patientType,
"MobileNo": mobileNo,
"PatientIdentificationID": patientIdentificationID,
"InsuranceCardImage": insuranceCardImage,
};
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(
UPLOAD_INSURANCE_CARD,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
// final uploadResponse = UploadInsuranceCardResponseModel.fromJson(response);
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: apiResponse,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
}

@ -5,6 +5,7 @@ import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patien
import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_card_history.dart';
import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_details_response_model.dart';
import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_update_response_model.dart';
import 'package:hmg_patient_app_new/features/insurance/models/resp_models/upload_insurance_card_response_model.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
class InsuranceViewModel extends ChangeNotifier {
@ -171,4 +172,44 @@ class InsuranceViewModel extends ChangeNotifier {
},
);
}
Future<void> updatePatientInsuranceCard({
required int patientID,
required int patientType,
required String mobileNo,
required String patientIdentificationID,
required String insuranceCardImage,
Function(dynamic)? onSuccess,
Function(String)? onError,
}) async {
final result = await insuranceRepo.updatePatientInsuranceCard(
patientID: patientID,
patientType: patientType,
mobileNo: mobileNo,
patientIdentificationID: patientIdentificationID,
insuranceCardImage: insuranceCardImage,
);
result.fold(
(failure) async {
notifyListeners();
if (onError != null) {
onError(failure.toString());
}
},
(apiResponse) {
if (apiResponse.messageStatus == 2) {
notifyListeners();
if (onError != null) {
onError(apiResponse.errorMessage ?? "Error updating insurance card");
}
} else if (apiResponse.messageStatus == 1) {
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
}

@ -240,6 +240,9 @@ class LabViewModel extends ChangeNotifier {
model: item))
};
uniqueTestsList = uniqueTests.toList();
uniqueTestsList = Utils.uniqueBy(uniqueTestsList, (p) => p.testCode);
uniqueTestsList.sort((a, b) {
return a.description!.toLowerCase().compareTo(b.description!.toLowerCase());
});

@ -41,6 +41,7 @@ class MedicalFileViewModel extends ChangeNotifier {
List<SickLeaveList> patientSickLeavesViewList = [];
bool isSickLeavesSortByClinic = true;
bool isSickLeavesDataNeedsReloading = true;
List<GetAllergiesResponseModel> patientAllergiesList = [];
@ -163,15 +164,15 @@ class MedicalFileViewModel extends ChangeNotifier {
}
setIsPatientSickLeaveListLoading(bool val) {
if (val) {
if (val && isSickLeavesDataNeedsReloading) {
patientSickLeaveList.clear();
patientSickLeavesByClinic.clear();
patientSickLeavesByHospital.clear();
patientSickLeavesViewList.clear();
patientSickLeavePDFBase64 = "";
isSickLeavesSortByClinic = true;
isPatientSickLeaveListLoading = val;
}
isPatientSickLeaveListLoading = val;
notifyListeners();
}
@ -267,6 +268,10 @@ class MedicalFileViewModel extends ChangeNotifier {
}
Future<void> getPatientSickLeaveList({Function(dynamic)? onSuccess, Function(String)? onError}) async {
if (!isSickLeavesDataNeedsReloading) {
return;
}
patientSickLeaveList.clear();
final result = await medicalFileRepo.getPatientSickLeavesList();
@ -309,6 +314,7 @@ class MedicalFileViewModel extends ChangeNotifier {
}
}
patientSickLeavesViewList = patientSickLeavesByClinic;
isSickLeavesDataNeedsReloading = false;
notifyListeners();
if (onSuccess != null) {

@ -47,20 +47,24 @@ class PrescriptionsViewModel extends ChangeNotifier {
late GeocodeResponse locationGeocodeResponse;
bool isPrescriptionsDeliveryOrdersLoading = false;
bool isPrescriptionsDataNeedsReloading = true;
List<PrescriptionDeliveryResponseModel> prescriptionsOrderList = [];
PrescriptionsViewModel({required this.prescriptionsRepo, required this.errorHandlerService, required this.navServices});
initPrescriptionsViewModel() {
patientPrescriptionOrders.clear();
patientPrescriptionOrdersByClinic.clear();
patientPrescriptionOrdersByHospital.clear();
patientPrescriptionOrdersViewList.clear();
prescriptionsOrderList.clear();
isPrescriptionsOrdersLoading = true;
if (isPrescriptionsDataNeedsReloading) {
patientPrescriptionOrders.clear();
patientPrescriptionOrdersByClinic.clear();
patientPrescriptionOrdersByHospital.clear();
patientPrescriptionOrdersViewList.clear();
prescriptionsOrderList.clear();
isPrescriptionsOrdersLoading = true;
getPatientPrescriptionOrders();
}
isSortByClinic = true;
isPrescriptionsDeliveryOrdersLoading = true;
getPatientPrescriptionOrders();
notifyListeners();
}
@ -98,6 +102,10 @@ class PrescriptionsViewModel extends ChangeNotifier {
}
Future<void> getPatientPrescriptionOrders({Function(dynamic)? onSuccess, Function(String)? onError}) async {
if (!isPrescriptionsDataNeedsReloading) {
return;
}
final result = await prescriptionsRepo.getPatientPrescriptionOrders(patientId: "1231755");
result.fold(
@ -131,6 +139,7 @@ class PrescriptionsViewModel extends ChangeNotifier {
}
}
patientPrescriptionOrdersViewList = patientPrescriptionOrdersByClinic;
isPrescriptionsDataNeedsReloading = false;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);

@ -13,17 +13,20 @@ class TodoSectionViewModel extends ChangeNotifier {
String? notificationsCount = "0";
initializeTodoSectionViewModel() async {
patientAncillaryOrdersList.clear();
isAncillaryOrdersLoading = true;
isAncillaryDetailsProceduresLoading = true;
notificationsCount = "0";
getPatientOnlineAncillaryOrderList();
if (isAncillaryOrdersNeedReloading) {
patientAncillaryOrdersList.clear();
isAncillaryOrdersLoading = true;
isAncillaryDetailsProceduresLoading = true;
notificationsCount = "0";
getPatientOnlineAncillaryOrderList();
}
getPatientDashboard();
}
bool isAncillaryOrdersLoading = false;
bool isAncillaryDetailsProceduresLoading = false;
bool isProcessingPayment = false;
bool isAncillaryOrdersNeedReloading = true;
List<AncillaryOrderItem> patientAncillaryOrdersList = [];
List<AncillaryOrderProcedureItem> patientAncillaryOrderProceduresList = [];
@ -32,6 +35,11 @@ class TodoSectionViewModel extends ChangeNotifier {
notifyListeners();
}
void setIsAncillaryOrdersNeedReloading(bool value) {
isAncillaryOrdersNeedReloading = value;
notifyListeners();
}
Future<void> getPatientDashboard({Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await todoSectionRepo.getPatientDashboard();
@ -55,6 +63,10 @@ class TodoSectionViewModel extends ChangeNotifier {
}
Future<void> getPatientOnlineAncillaryOrderList({Function(dynamic)? onSuccess, Function(String)? onError}) async {
if (!isAncillaryOrdersNeedReloading) {
return;
}
patientAncillaryOrdersList.clear();
isAncillaryOrdersLoading = true;
notifyListeners();
@ -71,6 +83,7 @@ class TodoSectionViewModel extends ChangeNotifier {
} else if (apiResponse.messageStatus == 1) {
patientAncillaryOrdersList = apiResponse.data!;
isAncillaryOrdersLoading = false;
isAncillaryOrdersNeedReloading = false;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);

@ -15,6 +15,7 @@ import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_model.dart';
import 'package:hmg_patient_app_new/features/contact_us/contact_us_view_model.dart';
import 'package:hmg_patient_app_new/features/contact_us/models/feedback_type.dart';
import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
@ -92,6 +93,7 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
title: LocaleKeys.appointmentDetails.tr(context: context),
report: AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)
? () {
contactUsViewModel.setSelectedFeedbackType(FeedbackType(id: 1, nameEN: "Complaint for appointment", nameAR: 'شكوى على موعد'));
contactUsViewModel.setPatientFeedbackSelectedAppointment(widget.patientAppointmentHistoryResponseModel);
Navigator.of(context).push(
CustomPageRoute(
@ -214,11 +216,8 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
borderRadius: BorderRadius.circular(24.r),
// Todo: what is this???? Api Key??? 😲
child: Image.network(
"https://maps.googleapis.com/maps/api/staticmap?center=${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&zoom=14&size=${(MediaQuery.of(context).size.width * 1.5).toInt()}x165&maptype=roadmap&markers=color:red%7C${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&key=${ApiKeyConstants.googleMapsApiKey}",
"https://maps.googleapis.com/maps/api/staticmap?center=${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&zoom=14&size=${(MediaQuery.of(context).size.width * 1.5).toInt()}x${(MediaQuery.of(context).size.height * 0.35).toInt()}&maptype=roadmap&markers=color:red%7C${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&key=${ApiKeyConstants.googleMapsApiKey}",
fit: BoxFit.contain,
// errorBuilder: (cxt, child, tr) {
// return SizedBox.shrink();
// },
),
),
Positioned(
@ -756,9 +755,10 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
],
).paddingOnly(left: 16.h, top: 24.h, right: 16.h, bottom: 0.h),
AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)
? CustomButton(
text: LocaleKeys.rebookAppointment.tr(context: context),
onPressed: () {
? !widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment!
? CustomButton(
text: LocaleKeys.rebookAppointment.tr(context: context),
onPressed: () {
openDoctorScheduleCalendar();
},
backgroundColor: AppColors.successColor,
@ -772,7 +772,8 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
icon: AppAssets.add_icon,
iconColor: AppColors.whiteColor,
iconSize: 18.h,
).paddingSymmetrical(16.h, 24.h)
).paddingSymmetrical(16.h, 24.h)
: SizedBox.shrink()
: CustomButton(
text: AppointmentType.getNextActionText(widget.patientAppointmentHistoryResponseModel.nextAction),
onPressed: () {

@ -153,7 +153,7 @@ class AppointmentCard extends StatelessWidget {
SizedBox(height: 2.h),
(isFoldable || isTablet)
? "${patientAppointmentHistoryResponseModel.decimalDoctorRate}".toText9(isBold: true, color: AppColors.textColor)
: "${patientAppointmentHistoryResponseModel.decimalDoctorRate}".toText11(isBold: true, color: AppColors.textColor),
: "${patientAppointmentHistoryResponseModel.decimalDoctorRate ?? "0.0"}".toText11(isBold: true, color: AppColors.textColor),
],
),
).circle(100).toShimmer2(isShow: isLoading),
@ -228,18 +228,27 @@ class AppointmentCard extends StatelessWidget {
Widget _buildActionArea(BuildContext context, AppState appState) {
if ((patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false) && AppointmentType.isArrived(patientAppointmentHistoryResponseModel)) {
return CustomButton(
text: LocaleKeys.viewDetails.tr(context: context),
text: isFromMedicalReport ? LocaleKeys.selectAppointment.tr(context: context) : LocaleKeys.viewDetails.tr(context: context),
onPressed: () {
Navigator.of(context)
.push(
CustomPageRoute(
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel),
),
)
.then((_) {
myAppointmentsViewModel.initAppointmentsViewModel();
myAppointmentsViewModel.getPatientAppointments(true, false);
});
if (isFromMedicalReport) {
if (isForFeedback) {
contactUsViewModel!.setPatientFeedbackSelectedAppointment(patientAppointmentHistoryResponseModel);
} else {
medicalFileViewModel!.setSelectedMedicalReportAppointment(patientAppointmentHistoryResponseModel);
}
Navigator.pop(context, false);
} else {
Navigator.of(context)
.push(
CustomPageRoute(
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel),
),
)
.then((_) {
myAppointmentsViewModel.initAppointmentsViewModel();
myAppointmentsViewModel.getPatientAppointments(true, false);
});
}
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
@ -250,6 +259,9 @@ class AppointmentCard extends StatelessWidget {
padding: EdgeInsets.symmetric(horizontal: 10.w),
// height: isTablet || isFoldable ? 46.h : 40.h,
height: 40.h,
icon: isFromMedicalReport ? AppAssets.checkmark_icon : null,
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
);
} else {
if (isFromMedicalReport) {

@ -159,8 +159,9 @@ class AppointmentDoctorCard extends StatelessWidget {
icon: AppAssets.ask_doctor_icon,
iconColor: AppColors.primaryRedColor,
)
: CustomButton(
text: LocaleKeys.rebookSameDoctor.tr(),
: !patientAppointmentHistoryResponseModel.isLiveCareAppointment!
? CustomButton(
text: LocaleKeys.rebookSameDoctor.tr(),
onPressed: () {
onRescheduleTap();
},
@ -175,7 +176,8 @@ class AppointmentDoctorCard extends StatelessWidget {
icon: AppAssets.rebook_appointment_icon,
iconColor: AppColors.blackColor,
iconSize: 14.h,
);
)
: SizedBox.shrink();
} else {
return patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false
? CustomButton(

@ -263,13 +263,20 @@ class _BloodDonationPageState extends State<BloodDonationPage> {
Row(
children: [
Text(
LocaleKeys.iAcceptThe.tr(),
"${LocaleKeys.iAcceptThe.tr()} ",
style: context.dynamicTextStyle(fontSize: 14.f, fontWeight: FontWeight.w500, color: Color(0xFF2E3039)),
),
GestureDetector(
onTap: () {
// Navigate to terms and conditions page
Navigator.of(context).pushNamed('/terms');
// Navigator.of(context).pushNamed('/terms');
appState.isArabic()
? Utils.openWebView(
url: 'https://hmg.com/ar/Pages/Terms.aspx',
)
: Utils.openWebView(
url: 'https://hmg.com/en/Pages/Terms.aspx',
);
},
child: Text(
LocaleKeys.termsConditoins.tr(),

@ -11,6 +11,7 @@ import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/appointment_calendar.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/doctor_rating_details.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
@ -108,9 +109,21 @@ class DoctorProfilePage extends StatelessWidget {
),
SizedBox(height: 16.h),
"Ratings".toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor),
bookAppointmentsViewModel.doctorsProfileResponseModel.decimalDoctorRate.toString().toText16(isBold: true, color: AppColors.textColor),
bookAppointmentsViewModel.doctorsProfileResponseModel.decimalDoctorRate
.toString()
.toText16(isBold: true, color: AppColors.textColor, isUnderLine: true, decorationColor: AppColors.textColor),
],
),
).onPress(() {
bookAppointmentsViewModel.getDoctorRatingDetails();
showCommonBottomSheetWithoutHeight(
title: LocaleKeys.doctorRating.tr(context: context),
context,
child: DoctorRatingDetails(),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}),
SizedBox(width: 36.w),
Column(
children: [
@ -122,15 +135,28 @@ class DoctorProfilePage extends StatelessWidget {
),
SizedBox(height: 16.h),
"Reviews".toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor),
bookAppointmentsViewModel.doctorsProfileResponseModel.noOfPatientsRate.toString().toText16(isBold: true, color: AppColors.textColor),
bookAppointmentsViewModel.doctorsProfileResponseModel.noOfPatientsRate
.toString()
.toText16(isBold: true, color: AppColors.textColor, isUnderLine: true, decorationColor: AppColors.textColor),
],
),
).onPress(() {
bookAppointmentsViewModel.getDoctorRatingDetails();
showCommonBottomSheetWithoutHeight(
title: LocaleKeys.doctorRating.tr(context: context),
context,
child: DoctorRatingDetails(),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}),
],
),
SizedBox(height: 16.h),
Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h),
SizedBox(height: 16.h),
"Biography".toText14(weight: FontWeight.w600, color: AppColors.textColor),
LocaleKeys.docInfo.tr(context: context).toText14(weight: FontWeight.w600, color: AppColors.textColor),
SizedBox(height: 6.h),
bookAppointmentsViewModel.doctorsProfileResponseModel.doctorProfileInfo!.toText12(fontWeight: FontWeight.w600, color: AppColors.greyTextColor),
],
).paddingSymmetrical(24.h, 0.h),

@ -33,71 +33,30 @@ import 'package:smooth_corner/smooth_corner.dart';
class ImmediateLiveCarePaymentDetails extends StatelessWidget {
ImmediateLiveCarePaymentDetails({super.key});
late ImmediateLiveCareViewModel immediateLiveCareViewModel;
// late ImmediateLiveCareViewModel immediateLiveCareViewModel;
late AppState appState;
@override
Widget build(BuildContext context) {
immediateLiveCareViewModel = Provider.of<ImmediateLiveCareViewModel>(context, listen: false);
// immediateLiveCareViewModel = Provider.of<ImmediateLiveCareViewModel>(context, listen: false);
appState = getIt.get<AppState>();
return Scaffold(
backgroundColor: AppColors.scaffoldBgColor,
body: Column(
children: [
Expanded(
child: CollapsingListView(
title: LocaleKeys.reviewLiveCareRequest.tr(context: context),
child: SingleChildScrollView(
padding: EdgeInsets.symmetric(horizontal: 24.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 24.h),
LocaleKeys.patientInfo.tr(context: context).toText16(isBold: true),
SizedBox(height: 16.h),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: false,
),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Row(
children: [
Image.asset(
appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.maleImg : AppAssets.femaleImg,
width: 52.h,
height: 52.h,
),
SizedBox(width: 8.h),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}".toText16(isBold: true),
SizedBox(height: 8.h),
Wrap(
direction: Axis.horizontal,
spacing: 3.h,
runSpacing: 4.h,
children: [
AppCustomChipWidget(labelText: "${appState.getAuthenticatedUser()!.age} ${LocaleKeys.yearsOld.tr(context: context)}"),
AppCustomChipWidget(
labelText:
"${LocaleKeys.clinic.tr()}: ${(appState.isArabic() ? immediateLiveCareViewModel.immediateLiveCareSelectedClinic.serviceNameN : immediateLiveCareViewModel.immediateLiveCareSelectedClinic.serviceName)!}"),
],
),
],
),
],
),
),
),
SizedBox(height: 24.h),
LocaleKeys.selectedLiveCareType.tr(context: context).toText16(isBold: true),
SizedBox(height: 16.h),
Consumer<BookAppointmentsViewModel>(builder: (context, bookAppointmentsVM, child) {
return Container(
body: Consumer<ImmediateLiveCareViewModel>(builder: (context, immediateLiveCareVM, child) {
return Column(
children: [
Expanded(
child: CollapsingListView(
title: LocaleKeys.reviewLiveCareRequest.tr(context: context),
child: SingleChildScrollView(
padding: EdgeInsets.symmetric(horizontal: 24.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 24.h),
LocaleKeys.patientInfo.tr(context: context).toText16(isBold: true),
SizedBox(height: 16.h),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
@ -106,217 +65,262 @@ class ImmediateLiveCarePaymentDetails extends StatelessWidget {
child: Padding(
padding: EdgeInsets.all(16.h),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
Image.asset(
appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.maleImg : AppAssets.femaleImg,
width: 52.h,
height: 52.h,
),
SizedBox(width: 8.h),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Utils.buildSvgWithAssets(icon: AppAssets.livecare_clinic_icon, width: 32.h, height: 32.h, fit: BoxFit.contain),
SizedBox(width: 8.h),
getLiveCareType(context, immediateLiveCareViewModel.liveCareSelectedCallType).toText16(isBold: true),
"${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}".toText16(isBold: true),
SizedBox(height: 8.h),
Wrap(
direction: Axis.horizontal,
spacing: 3.h,
runSpacing: 4.h,
children: [
AppCustomChipWidget(labelText: "${appState.getAuthenticatedUser()!.age} ${LocaleKeys.yearsOld.tr(context: context)}"),
AppCustomChipWidget(
labelText:
"${LocaleKeys.clinic.tr()}: ${(appState.isArabic() ? immediateLiveCareVM.immediateLiveCareSelectedClinic.serviceNameN : immediateLiveCareVM.immediateLiveCareSelectedClinic.serviceName)!}"),
],
),
],
),
Utils.buildSvgWithAssets(icon: AppAssets.edit_icon, width: 24.h, height: 24.h, fit: BoxFit.contain),
],
),
),
).onPress(() {
showCommonBottomSheetWithoutHeight(context, child: SelectLiveCareCallType(immediateLiveCareViewModel: immediateLiveCareViewModel), callBackFunc: () async {
debugPrint("Selected Call Type: ${immediateLiveCareViewModel.liveCareSelectedCallType}");
}, title: LocaleKeys.selectLiveCareCallType.tr(context: context), isCloseButtonVisible: true, isFullScreen: false);
});
}),
SizedBox(height: 24.h)
],
),
SizedBox(height: 24.h),
LocaleKeys.selectedLiveCareType.tr(context: context).toText16(isBold: true),
SizedBox(height: 16.h),
Consumer<BookAppointmentsViewModel>(builder: (context, bookAppointmentsVM, child) {
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: false,
),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Utils.buildSvgWithAssets(icon: AppAssets.livecare_clinic_icon, width: 32.h, height: 32.h, fit: BoxFit.contain),
SizedBox(width: 8.h),
getLiveCareType(context, immediateLiveCareVM.liveCareSelectedCallType).toText16(isBold: true),
],
),
Utils.buildSvgWithAssets(icon: AppAssets.edit_icon, width: 24.h, height: 24.h, fit: BoxFit.contain),
],
),
),
).onPress(() {
showCommonBottomSheetWithoutHeight(context, child: SelectLiveCareCallType(immediateLiveCareViewModel: immediateLiveCareVM), callBackFunc: () async {
debugPrint("Selected Call Type: ${immediateLiveCareVM.liveCareSelectedCallType}");
}, title: LocaleKeys.selectLiveCareCallType.tr(context: context), isCloseButtonVisible: true, isFullScreen: false);
});
}),
SizedBox(height: 24.h)
],
),
),
),
),
),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: false,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
(immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.isCash ?? true)
? Container(
height: 50.h,
decoration: ShapeDecoration(
color: AppColors.secondaryLightRedBorderColor,
shape: SmoothRectangleBorder(
borderRadius: BorderRadius.only(topLeft: Radius.circular(24), topRight: Radius.circular(24)),
smoothness: 1,
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: false,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
(immediateLiveCareVM.liveCareImmediateAppointmentFeesList.isCash ?? true)
? Container(
height: 50.h,
decoration: ShapeDecoration(
color: AppColors.secondaryLightRedBorderColor,
shape: SmoothRectangleBorder(
borderRadius: BorderRadius.only(topLeft: Radius.circular(24), topRight: Radius.circular(24)),
smoothness: 1,
),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
LocaleKeys.insuranceExpiredOrInactive.tr(context: context).toText14(color: AppColors.primaryRedColor, weight: FontWeight.w500).paddingSymmetrical(24.h, 0.h),
CustomButton(
text: LocaleKeys.updateInsurance.tr(context: context),
onPressed: () {
Navigator.of(context).push(
CustomPageRoute(
page: InsuranceHomePage(),
),
);
},
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.secondaryLightRedBorderColor,
textColor: AppColors.whiteColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(15, 0, 15, 0),
height: 30.h,
).paddingSymmetrical(24.h, 0.h),
],
),
)
: const SizedBox(),
SizedBox(height: 24.h),
LocaleKeys.totalAmountToPay.tr(context: context).toText18(isBold: true).paddingSymmetrical(24.h, 0.h),
SizedBox(height: 17.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
LocaleKeys.amountBeforeTax.tr(context: context).toText14(isBold: true),
Utils.getPaymentAmountWithSymbol(immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.amount!.toText16(isBold: true), AppColors.blackColor, 13,
isSaudiCurrency: immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.currency!.toLowerCase() == "sar"),
],
).paddingSymmetrical(24.h, 0.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
LocaleKeys.vat15.tr(context: context).toText14(isBold: true, color: AppColors.greyTextColor),
Utils.getPaymentAmountWithSymbol(
immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.tax!.toText14(isBold: true, color: AppColors.greyTextColor), AppColors.greyTextColor, 13,
isSaudiCurrency: immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.currency!.toLowerCase() == "sar"),
],
).paddingSymmetrical(24.h, 0.h),
SizedBox(height: 17.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(width: 150.h, child: Utils.getPaymentMethods()),
Utils.getPaymentAmountWithSymbol(immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.total!.toText24(isBold: true), AppColors.blackColor, 17,
isSaudiCurrency: immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.currency!.toLowerCase() == "sar"),
],
).paddingSymmetrical(24.h, 0.h),
(immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.total == "0" || immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.total == "0.0")
? CustomButton(
text: LocaleKeys.confirmLiveCare.tr(context: context),
onPressed: () async {
await askVideoCallPermission(context).then((val) async {
if (val) {
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.confirmingLiveCareRequest.tr(context: context));
await immediateLiveCareViewModel.addNewCallRequestForImmediateLiveCare("${appState.getAuthenticatedUser()!.patientId}${DateTime.now().millisecondsSinceEpoch}");
await immediateLiveCareViewModel.getPatientLiveCareHistory();
LoaderBottomSheet.hideLoader();
if (immediateLiveCareViewModel.patientHasPendingLiveCareRequest) {
Navigator.pushAndRemoveUntil(
context,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
LocaleKeys.insuranceExpiredOrInactive.tr(context: context).toText14(color: AppColors.primaryRedColor, weight: FontWeight.w500).paddingSymmetrical(24.h, 0.h),
CustomButton(
text: LocaleKeys.updateInsurance.tr(context: context),
onPressed: () {
Navigator.of(context).push(
CustomPageRoute(
page: LandingNavigation(),
page: InsuranceHomePage(),
),
);
},
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.secondaryLightRedBorderColor,
textColor: AppColors.whiteColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(15, 0, 15, 0),
height: 30.h,
).paddingSymmetrical(24.h, 0.h),
],
),
)
: const SizedBox(),
SizedBox(height: 24.h),
LocaleKeys.totalAmountToPay.tr(context: context).toText18(isBold: true).paddingSymmetrical(24.h, 0.h),
SizedBox(height: 17.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
LocaleKeys.amountBeforeTax.tr(context: context).toText14(isBold: true),
Utils.getPaymentAmountWithSymbol(immediateLiveCareVM.liveCareImmediateAppointmentFeesList.amount!.toText16(isBold: true), AppColors.blackColor, 13,
isSaudiCurrency: immediateLiveCareVM.liveCareImmediateAppointmentFeesList.currency!.toLowerCase() == "sar"),
],
).paddingSymmetrical(24.h, 0.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
LocaleKeys.vat15.tr(context: context).toText14(isBold: true, color: AppColors.greyTextColor),
Utils.getPaymentAmountWithSymbol(
immediateLiveCareVM.liveCareImmediateAppointmentFeesList.tax!.toText14(isBold: true, color: AppColors.greyTextColor), AppColors.greyTextColor, 13,
isSaudiCurrency: immediateLiveCareVM.liveCareImmediateAppointmentFeesList.currency!.toLowerCase() == "sar"),
],
).paddingSymmetrical(24.h, 0.h),
SizedBox(height: 17.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(width: 150.h, child: Utils.getPaymentMethods()),
Utils.getPaymentAmountWithSymbol(immediateLiveCareVM.liveCareImmediateAppointmentFeesList.total!.toText24(isBold: true), AppColors.blackColor, 17,
isSaudiCurrency: immediateLiveCareVM.liveCareImmediateAppointmentFeesList.currency!.toLowerCase() == "sar"),
],
).paddingSymmetrical(24.h, 0.h),
(immediateLiveCareVM.liveCareImmediateAppointmentFeesList.total == "0" || immediateLiveCareVM.liveCareImmediateAppointmentFeesList.total == "0.0")
? CustomButton(
text: LocaleKeys.confirmLiveCare.tr(context: context),
onPressed: () async {
await askVideoCallPermission(context).then((val) async {
if (val) {
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.confirmingLiveCareRequest.tr(context: context));
await immediateLiveCareVM.addNewCallRequestForImmediateLiveCare("${appState.getAuthenticatedUser()!.patientId}${DateTime
.now()
.millisecondsSinceEpoch}");
await immediateLiveCareVM.getPatientLiveCareHistory();
LoaderBottomSheet.hideLoader();
if (immediateLiveCareVM.patientHasPendingLiveCareRequest) {
Navigator.pushAndRemoveUntil(
context,
CustomPageRoute(
page: LandingNavigation(),
),
(r) => false);
Navigator.of(context).push(
CustomPageRoute(
page: ImmediateLiveCarePendingRequestPage(),
),
);
Navigator.of(context).push(
CustomPageRoute(
page: ImmediateLiveCarePendingRequestPage(),
),
);
} else {
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: LocaleKeys.unknownErrorOccurred.tr(context: context)),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}
} else {
showCommonBottomSheetWithoutHeight(
title: LocaleKeys.notice.tr(context: context),
context,
child: Utils.getErrorWidget(loadingText: LocaleKeys.unknownErrorOccurred.tr(context: context)),
child: Utils.getWarningWidget(
loadingText: LocaleKeys.liveCarePermissionsMessage.tr(context: context),
isShowActionButtons: true,
onCancelTap: () {
Navigator.pop(context);
},
onConfirmTap: () async {
openAppSettings();
}),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}
} else {
showCommonBottomSheetWithoutHeight(
title: LocaleKeys.notice.tr(context: context),
context,
child: Utils.getWarningWidget(
loadingText: LocaleKeys.liveCarePermissionsMessage.tr(context: context),
isShowActionButtons: true,
onCancelTap: () {
Navigator.pop(context);
},
onConfirmTap: () async {
openAppSettings();
}),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}
});
},
backgroundColor: AppColors.successColor,
borderColor: AppColors.successColor,
textColor: AppColors.whiteColor,
fontSize: 16,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 50.h,
icon: AppAssets.livecare_book_icon,
iconColor: AppColors.whiteColor,
iconSize: 18.h,
).paddingSymmetrical(24.h, 24.h)
: CustomButton(
text: LocaleKeys.payNow.tr(context: context),
onPressed: () async {
await askVideoCallPermission(context).then((val) {
if (val) {
Navigator.of(context).push(
CustomPageRoute(
page: ImmediateLiveCarePaymentPage(),
),
);
}
// else {
// showCommonBottomSheetWithoutHeight(
// title: LocaleKeys.notice.tr(context: context),
// context,
// child: Utils.getWarningWidget(
// loadingText: LocaleKeys.liveCarePermissionsMessage.tr(context: context),
// isShowActionButtons: true,
// onCancelTap: () {
// Navigator.pop(context);
// },
// onConfirmTap: () async {
// openAppSettings();
// }),
// callBackFunc: () {},
// isFullScreen: false,
// isCloseButtonVisible: true,
// );
// }
});
},
backgroundColor: AppColors.infoColor,
borderColor: AppColors.infoColor,
textColor: AppColors.whiteColor,
fontSize: 16,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 50.h,
icon: AppAssets.appointment_pay_icon,
iconColor: AppColors.whiteColor,
iconSize: 18.h,
).paddingSymmetrical(24.h, 24.h),
],
});
},
backgroundColor: AppColors.successColor,
borderColor: AppColors.successColor,
textColor: AppColors.whiteColor,
fontSize: 16,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 50.h,
icon: AppAssets.livecare_book_icon,
iconColor: AppColors.whiteColor,
iconSize: 18.h,
).paddingSymmetrical(24.h, 24.h)
: CustomButton(
text: LocaleKeys.payNow.tr(context: context),
onPressed: () async {
await askVideoCallPermission(context).then((val) {
if (val) {
Navigator.of(context).push(
CustomPageRoute(
page: ImmediateLiveCarePaymentPage(),
),
);
}
// else {
// showCommonBottomSheetWithoutHeight(
// title: LocaleKeys.notice.tr(context: context),
// context,
// child: Utils.getWarningWidget(
// loadingText: LocaleKeys.liveCarePermissionsMessage.tr(context: context),
// isShowActionButtons: true,
// onCancelTap: () {
// Navigator.pop(context);
// },
// onConfirmTap: () async {
// openAppSettings();
// }),
// callBackFunc: () {},
// isFullScreen: false,
// isCloseButtonVisible: true,
// );
// }
});
},
backgroundColor: AppColors.infoColor,
borderColor: AppColors.infoColor,
textColor: AppColors.whiteColor,
fontSize: 16,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 50.h,
icon: AppAssets.appointment_pay_icon,
iconColor: AppColors.whiteColor,
iconSize: 18.h,
).paddingSymmetrical(24.h, 24.h),
],
),
),
),
],
),
],
);
}),
);
}

@ -941,7 +941,7 @@ class _SelectClinicPageState extends State<SelectClinicPage> {
void onLiveCareClinicSelected(GetLiveCareClinicsResponseModel clinic) {
bookAppointmentsViewModel.setLiveCareSelectedClinic(clinic);
bookAppointmentsViewModel.setIsDoctorsListLoading(true);
bookAppointmentsViewModel.setIsLiveCareDoctorsListLoading(true);
Navigator.of(context).push(
CustomPageRoute(
page: SelectDoctorPage(),

@ -95,9 +95,59 @@ class _SelectDoctorPageState extends State<SelectDoctorPage> {
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 24.h),
child: Consumer<BookAppointmentsViewModel>(builder: (context, bookAppointmentsVM, child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
return bookAppointmentsViewModel.isLiveCareSchedule
? Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ListView.separated(
padding: EdgeInsets.only(top: 16.h),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: bookAppointmentsVM.isDoctorsListLoading ? 5 : (bookAppointmentsVM.liveCareDoctorsList.isNotEmpty ? bookAppointmentsVM.liveCareDoctorsList.length : 1),
itemBuilder: (context, index) {
// final isExpanded = bookAppointmentsVM.expandedGroupIndex == index;
final isExpanded = true;
return bookAppointmentsVM.isDoctorsListLoading
? DoctorCard(
doctorsListResponseModel: DoctorsListResponseModel(),
isLoading: true,
bookAppointmentsViewModel: bookAppointmentsViewModel,
)
: bookAppointmentsVM.liveCareDoctorsList.isEmpty
? Utils.getNoDataWidget(context, noDataText: LocaleKeys.noDoctorFound.tr())
: 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: Padding(
padding: EdgeInsets.all(16.h),
child: DoctorCard(
isLoading: bookAppointmentsVM.isClinicsListLoading,
doctorsListResponseModel: bookAppointmentsVM.liveCareDoctorsList[index],
bookAppointmentsViewModel: bookAppointmentsViewModel,
).onPress(() {
// onLiveCareClinicSelected(bookAppointmentsVM.liveCareClinicsList[index]);
}),
),
),
),
),
);
},
separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h),
),
SizedBox(height: 24.h),
],
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 16.h),
Row(
spacing: 8.h,

@ -90,7 +90,7 @@ class DoctorCard extends StatelessWidget {
Row(
children: [
SizedBox(
width: MediaQuery.of(context).size.width * 0.49,
width: MediaQuery.of(context).size.width * 0.55,
child: (isLoading ? "Dr John Smith" : "${doctorsListResponseModel.doctorTitle} ${doctorsListResponseModel.name}")
.toString()
.toText16(isBold: true, maxlines: 1),
@ -99,14 +99,14 @@ class DoctorCard extends StatelessWidget {
),
SizedBox(height: 2.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
width: MediaQuery.of(context).size.width * 0.45,
child: (isLoading
? "Consultant Cardiologist"
: doctorsListResponseModel.speciality!.isNotEmpty
? doctorsListResponseModel.speciality!.first
? (doctorsListResponseModel.speciality!.first.length > 32
? '${doctorsListResponseModel.speciality!.first.substring(0, 32)}...'
: doctorsListResponseModel.speciality!.first)
: "")
.toString()
.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor, maxLine: 2)
@ -121,6 +121,29 @@ class DoctorCard extends StatelessWidget {
).toShimmer2(isShow: isLoading),
],
),
SizedBox(height: 6.h),
Wrap(
direction: Axis.horizontal,
spacing: 3.h,
runSpacing: 4.h,
children: [
AppCustomChipWidget(
labelText: "${isLoading ? "Cardiologist" : doctorsListResponseModel.clinicName}",
).toShimmer2(isShow: isLoading),
AppCustomChipWidget(
labelText: "${isLoading ? "Olaya Hospital" : doctorsListResponseModel.projectName}",
).toShimmer2(isShow: isLoading),
bookAppointmentsViewModel.isNearestAppointmentSelected
? doctorsListResponseModel.nearestFreeSlot != null
? AppCustomChipWidget(
labelText: (isLoading ? "Cardiologist" : DateUtil.getDateStringForNearestSlot(doctorsListResponseModel.nearestFreeSlot)),
backgroundColor: AppColors.successColor,
textColor: AppColors.whiteColor,
).toShimmer2(isShow: isLoading)
: SizedBox.shrink()
: SizedBox.shrink(),
],
),
],
),
),
@ -131,28 +154,6 @@ class DoctorCard extends StatelessWidget {
),
],
),
Wrap(
direction: Axis.horizontal,
spacing: 3.h,
runSpacing: 4.h,
children: [
AppCustomChipWidget(
labelText: "${isLoading ? "Cardiologist" : doctorsListResponseModel.clinicName}",
).toShimmer2(isShow: isLoading),
AppCustomChipWidget(
labelText: "${isLoading ? "Olaya Hospital" : doctorsListResponseModel.projectName}",
).toShimmer2(isShow: isLoading),
bookAppointmentsViewModel.isNearestAppointmentSelected
? doctorsListResponseModel.nearestFreeSlot != null
? AppCustomChipWidget(
labelText: (isLoading ? "Cardiologist" : DateUtil.getDateStringForNearestSlot(doctorsListResponseModel.nearestFreeSlot)),
backgroundColor: AppColors.successColor,
textColor: AppColors.whiteColor,
).toShimmer2(isShow: isLoading)
: SizedBox.shrink()
: SizedBox.shrink(),
],
),
SizedBox(height: 12.h),
CustomButton(
text: LocaleKeys.bookAppo.tr(context: context),

@ -0,0 +1,195 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_rating_bar/flutter_rating_bar.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/features/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:provider/provider.dart';
class DoctorRatingDetails extends StatelessWidget {
const DoctorRatingDetails({super.key});
@override
Widget build(BuildContext context) {
return Consumer<BookAppointmentsViewModel>(builder: (context, bookAppointmentsVM, child) {
return bookAppointmentsVM.isDoctorRatingDetailsLoading
? Utils.getLoadingWidget()
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
bookAppointmentsVM.doctorsProfileResponseModel.actualDoctorRate!.ceilToDouble().toString().toText44(isBold: true),
SizedBox(height: 4.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"${bookAppointmentsVM.doctorsProfileResponseModel.noOfPatientsRate} ${LocaleKeys.reviews.tr(context: context)}"
.toText16(weight: FontWeight.w500, color: AppColors.greyInfoTextColor),
RatingBar(
initialRating: bookAppointmentsVM.doctorsProfileResponseModel.actualDoctorRate!.toDouble(),
direction: Axis.horizontal,
allowHalfRating: true,
itemCount: 5,
itemSize: 20.h,
ignoreGestures: true,
ratingWidget: RatingWidget(
full: Icon(
Icons.star,
color: AppColors.ratingColorYellow,
size: 24.h,
),
half: Icon(
Icons.star_half,
color: AppColors.ratingColorYellow,
),
empty: Icon(
Icons.star,
color: AppColors.ratingColorYellow,
),
),
tapOnlyMode: true,
unratedColor: Colors.grey[500],
itemPadding: EdgeInsets.symmetric(horizontal: 4.0),
onRatingUpdate: (rating) {
print(rating);
},
),
],
),
SizedBox(height: 8.h),
Container(
margin: EdgeInsets.only(top: 10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Container(
width: 100.0,
margin: EdgeInsets.only(top: 10.0, left: 15.0, right: 15.0),
child: Text(LocaleKeys.excellent.tr(context: context), style: TextStyle(fontSize: 13.0, color: Colors.black, fontWeight: FontWeight.w600))),
getRatingLine(bookAppointmentsVM.doctorDetailsList[0].ratio, Colors.green[700]!),
],
),
Container(
margin: EdgeInsets.only(top: 10.0, left: 10.0, right: 10.0),
child: Text("${getRatingWidth(bookAppointmentsVM.doctorDetailsList[0].ratio).round()}%", style: TextStyle(fontSize: 14.0, color: Colors.black, fontWeight: FontWeight.w600)),
),
],
),
),
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Container(
width: 100.0,
margin: EdgeInsets.only(top: 10.0, left: 15.0, right: 15.0),
child: Text(LocaleKeys.vGood.tr(context: context), style: TextStyle(fontSize: 13.0, color: Colors.black, fontWeight: FontWeight.w600))),
getRatingLine(bookAppointmentsVM.doctorDetailsList[1].ratio, Color(0xffB7B723)),
],
),
Container(
margin: EdgeInsets.only(top: 10.0, left: 10.0, right: 10.0),
child: Text("${bookAppointmentsVM.doctorDetailsList[1].ratio.round()}%", style: TextStyle(fontSize: 14.0, color: Colors.black, fontWeight: FontWeight.w600)),
),
],
),
),
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Container(
width: 100.0,
margin: EdgeInsets.only(top: 10.0, left: 15.0, right: 15.0),
child: Text(LocaleKeys.good.tr(context: context), style: TextStyle(fontSize: 13.0, color: Colors.black, fontWeight: FontWeight.w600))),
getRatingLine(bookAppointmentsVM.doctorDetailsList[2].ratio, Color(0xffEBA727)),
],
),
Container(
margin: EdgeInsets.only(top: 10.0, left: 10.0, right: 10.0),
child: Text("${bookAppointmentsVM.doctorDetailsList[2].ratio.round()}%", style: TextStyle(fontSize: 14.0, color: Colors.black, fontWeight: FontWeight.w600)),
),
],
),
),
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Container(
width: 100.0,
margin: EdgeInsets.only(top: 10.0, left: 15.0, right: 15.0),
child: Text(LocaleKeys.average.tr(context: context), style: TextStyle(fontSize: 13.0, color: Colors.black, fontWeight: FontWeight.w600))),
getRatingLine(bookAppointmentsVM.doctorDetailsList[3].ratio, Color(0xffEB7227)),
],
),
Container(
margin: EdgeInsets.only(top: 10.0, left: 10.0, right: 10.0),
child: Text("${bookAppointmentsVM.doctorDetailsList[3].ratio.round()}%", style: TextStyle(fontSize: 14.0, color: Colors.black, fontWeight: FontWeight.w600)),
),
],
),
),
Container(
margin: EdgeInsets.only(bottom: 30.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Container(
width: 100.0,
margin: EdgeInsets.only(top: 10.0, left: 15.0, right: 15.0),
child: Text(LocaleKeys.average.tr(context: context), style: TextStyle(fontSize: 13.0, color: Colors.black, fontWeight: FontWeight.w600))),
getRatingLine(bookAppointmentsVM.doctorDetailsList[4].ratio, Color(0xffE20C0C)),
],
),
Container(
margin: EdgeInsets.only(top: 10.0, left: 10.0, right: 10.0),
child: Text("${bookAppointmentsVM.doctorDetailsList[4].ratio.round()}%", style: TextStyle(fontSize: 14.0, color: Colors.black, fontWeight: FontWeight.w600)),
),
],
),
),
],
);
});
}
double getRatingWidth(num patientNumber) {
var width = patientNumber;
return width.roundToDouble();
}
Widget getRatingLine(double patientNumber, Color color) {
return Container(
margin: EdgeInsets.only(top: 10.0),
child: Stack(children: [
SizedBox(
width: 150.0,
height: 7.h,
child: Container(
color: Colors.grey[300],
),
),
SizedBox(
width: patientNumber * 1.55,
height: 7.h,
child: Container(
color: color,
),
),
]),
);
}
}

@ -526,8 +526,8 @@ class CallAmbulancePage extends StatelessWidget {
textPlaceInput(context) {
return Consumer<LocationViewModel>(builder: (_, vm, __) {
print("the data is ${vm.geocodeResponse?.results.first.formattedAddress ?? vm.selectedPrediction?.description}");
return SizedBox(
// print("the data is ${vm.geocodeResponse?.results.first.formattedAddress ?? vm.selectedPrediction?.description}");
return (vm.geocodeResponse != null && vm.geocodeResponse!.results.isNotEmpty) ? SizedBox(
width: MediaQuery.sizeOf(context).width,
child: TextInputWidget(
labelText: LocaleKeys.enterPickupLocationManually.tr(context: context),
@ -549,7 +549,7 @@ class CallAmbulancePage extends StatelessWidget {
).onPress(() {
openLocationInputBottomSheet(context);
}),
);
) : SizedBox.shrink();
});
}

@ -159,6 +159,15 @@ class ErOnlineCheckinHome extends StatelessWidget {
await vm.getPatientERPaymentInformation(onSuccess: (response) {
LoaderBottomSheet.hideLoader();
vm.navigateToEROnlineCheckInPaymentPage();
}, onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: err.toString()),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
});
},
onHospitalSearch: (value) {

@ -36,6 +36,7 @@ class _RechargeWalletPageState extends State<RechargeWalletPage> {
late HabibWalletViewModel habibWalletVM;
late AppState appState;
final TextEditingController amountTextController = TextEditingController();
final TextEditingController notesTextController = TextEditingController();
@override
void initState() {
@ -209,23 +210,20 @@ class _RechargeWalletPageState extends State<RechargeWalletPage> {
SizedBox(height: 16.h),
Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h),
SizedBox(height: 16.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Utils.buildSvgWithAssets(icon: AppAssets.notes_icon, width: 40.h, height: 40.h),
SizedBox(width: 8.h),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.notes.tr(context: context).toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500),
"Lorem Ipsum".toText14(color: AppColors.textColor, weight: FontWeight.w500, letterSpacing: -0.2),
],
),
],
),
],
TextInputWidget(
labelText: LocaleKeys.notes.tr(context: context),
hintText: "",
controller: notesTextController,
keyboardType: TextInputType.text,
isEnable: true,
prefix: null,
autoFocus: true,
isAllowRadius: true,
isBorderAllowed: false,
isAllowLeadingIcon: true,
leadingIcon: AppAssets.notes_icon,
errorMessage: LocaleKeys.enterValidIDorIqama.tr(context: context),
hasError: false,
),
SizedBox(height: 8.h),
],
@ -271,6 +269,7 @@ class _RechargeWalletPageState extends State<RechargeWalletPage> {
);
} else {
habibWalletVM.setWalletRechargeAmount(num.parse(amountTextController.text));
habibWalletVM.setNotesText(notesTextController.text);
// habibWalletVM.setDepositorDetails(appState.getAuthenticatedUser()!.patientId.toString(), "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}",
// appState.getAuthenticatedUser()!.mobileNumber!);
Navigator.of(context).push(

@ -28,6 +28,8 @@ 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/habib_wallet/habib_wallet_view_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/models/family_file_response_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/services/dialog_service.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
@ -145,6 +147,10 @@ class _MultiPageBottomSheetState extends State<MultiPageBottomSheet> {
],
).paddingAll(16.h),
).onPress(() {
habibWalletVM.setDepositorDetails(appState.getAuthenticatedUser()!.patientId!.toString(),
"${appState.getAuthenticatedUser()!.firstName.toString()} ${appState.getAuthenticatedUser()!.lastName.toString()}", appState.getAuthenticatedUser()!.mobileNumber!.toString());
habibWalletVM.setCurrentIndex(0);
habibWalletVM.setSelectedRechargeType(1);
Navigator.of(context).pop();
}),
SizedBox(height: 16.h),
@ -167,7 +173,24 @@ class _MultiPageBottomSheetState extends State<MultiPageBottomSheet> {
Utils.buildSvgWithAssets(icon: AppAssets.forward_chevron_icon, iconColor: AppColors.textColor, width: 15.h, height: 15.h),
],
).paddingAll(16.h),
),
).onPress(() {
DialogService dialogService = getIt.get<DialogService>();
dialogService.showFamilyBottomSheetWithoutH(
label: LocaleKeys.familyTitle.tr(context: context),
message: "",
isShowManageButton: false,
isForWalletRecharge: true,
onSwitchPress: (FamilyFileResponseModelLists profile) {
habibWalletVM.setDepositorDetails(profile.responseId.toString(), profile.patientName.toString(), profile.mobileNumber.toString());
habibWalletVM.setCurrentIndex(0);
habibWalletVM.setSelectedRechargeType(2);
Navigator.of(context).pop();
Navigator.of(context).pop();
// medicalFileViewModel.switchFamilyFiles(responseID: profile.responseId, patientID: profile.patientId, phoneNumber: profile.mobileNumber);
},
profiles: getIt.get<MedicalFileViewModel>().patientFamilyFiles);
}),
SizedBox(height: 16.h),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(

@ -204,7 +204,7 @@ class _HealthCalculatorsPageState extends State<HealthCalculatorsPage> {
crossAxisCount: 3, // 4 icons per row
crossAxisSpacing: 16.w,
mainAxisSpacing: 16.w,
childAspectRatio: 0.80),
childAspectRatio: 0.85),
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: type == HealthCalculatorEnum.general ? generalHealthServices.length : womenHealthServices.length,
@ -214,6 +214,7 @@ class _HealthCalculatorsPageState extends State<HealthCalculatorsPage> {
icon: type == HealthCalculatorEnum.general ? generalHealthServices[index].icon : womenHealthServices[index].icon,
labelText: type == HealthCalculatorEnum.general ? generalHealthServices[index].title : womenHealthServices[index].title,
onTap: () {
Navigator.pop(context);
Navigator.of(context).push(
CustomPageRoute(
page: HealthCalculatorDetailedPage(

@ -157,15 +157,15 @@ class ServicesPage extends StatelessWidget {
true,
route: AppRoutes.bloodDonationPage,
),
HmgServicesComponentModel(
3,
"My Child Vaccine".needTranslation,
"".needTranslation,
AppAssets.my_child_vaccine_icon,
bgColor: AppColors.myChildVaccineCardColor,
true,
route: AppRoutes.myChildVaccine,
),
// HmgServicesComponentModel(
// 3,
// "My Child Vaccine".needTranslation,
// "".needTranslation,
// AppAssets.my_child_vaccine_icon,
// bgColor: AppColors.myChildVaccineCardColor,
// true,
// route: AppRoutes.myChildVaccine,
// ),
// HmgServicesComponentModel(
// 11,
// "Covid 19 Test".needTranslation,

@ -1,6 +1,8 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/cupertino.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';
@ -11,16 +13,20 @@ import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.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';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:provider/provider.dart';
class PatientInsuranceCardUpdateCard extends StatelessWidget {
PatientInsuranceCardUpdateCard({super.key});
late InsuranceViewModel insuranceViewModel;
late AppState appState;
@override
Widget build(BuildContext context) {
insuranceViewModel = Provider.of<InsuranceViewModel>(context);
appState = getIt.get<AppState>();
return Column(
mainAxisSize: MainAxisSize.min,
children: [
@ -49,7 +55,7 @@ class PatientInsuranceCardUpdateCard extends StatelessWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Haroon Amjad".toText16(weight: FontWeight.w600),
insuranceViewModel.patientInsuranceUpdateResponseModel!.memberName!.toText16(weight: FontWeight.w600),
"Policy: ${insuranceViewModel.patientInsuranceUpdateResponseModel!.policyNumber}".toText12(isBold: true, color: AppColors.lightGrayColor),
SizedBox(height: 8.h),
Row(
@ -99,7 +105,41 @@ class PatientInsuranceCardUpdateCard extends StatelessWidget {
iconColor: AppColors.whiteColor,
iconSize: 20.w,
text: "${LocaleKeys.updateInsurance.tr(context: context)} ${LocaleKeys.updateInsuranceSubtitle.tr(context: context)}",
onPressed: () {},
onPressed: () {
LoaderBottomSheet.showLoader();
insuranceViewModel.updatePatientInsuranceCard(
patientID: appState.getAuthenticatedUser()!.patientId!,
patientType: appState.getAuthenticatedUser()!.patientType!,
patientIdentificationID: appState.getAuthenticatedUser()!.patientIdentificationNo!,
mobileNo: appState.getAuthenticatedUser()!.mobileNumber!,
insuranceCardImage: "",
onSuccess: (val) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
title: LocaleKeys.success.tr(context: context),
context,
child: Utils.getSuccessWidget(loadingText: LocaleKeys.success.tr(context: context)),
callBackFunc: () {
Navigator.pop(context);
},
isFullScreen: false,
isCloseButtonVisible: true,
);
},
onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
title: LocaleKeys.notice.tr(context: context),
context,
child: Utils.getErrorWidget(loadingText: err.toString()),
callBackFunc: () {
Navigator.pop(context);
},
isFullScreen: false,
isCloseButtonVisible: true,
);
});
},
backgroundColor: insuranceViewModel.patientInsuranceUpdateResponseModel != null ? AppColors.successColor : AppColors.lightGrayBGColor,
borderColor: AppColors.successColor.withOpacity(0.01),
textColor: AppColors.whiteColor,

@ -203,7 +203,6 @@ class _AlphabetScrollPageState extends State<AlphabeticScroll> {
verticalOffset: 100.0,
child: FadeInAnimation(
child: LabOrderByTest(
appState: getIt<AppState>(),
onTap: () {
if (items[itemIndex].model != null) {

@ -17,6 +17,7 @@ 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/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';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:open_filex/open_filex.dart';
import 'package:provider/provider.dart';
@ -197,49 +198,23 @@ class _PatientSickleavesListPageState extends State<PatientSickleavesListPage> {
],
),
SizedBox(height: 8.h),
Row(
Wrap(
direction: Axis.horizontal,
spacing: 6.h,
runSpacing: 6.h,
children: [
CustomButton(
text: DateUtil.formatDateToDate(DateUtil.convertStringToDate(sickLeave.appointmentDate), false),
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 24.h,
AppCustomChipWidget(
labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(sickLeave.appointmentDate), false),
),
SizedBox(width: 8.h),
CustomButton(
text: model.isSickLeavesSortByClinic ? sickLeave.projectName! : sickLeave.clinicName!,
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 24.h,
AppCustomChipWidget(
labelText: model.isSickLeavesSortByClinic ? sickLeave.projectName! : sickLeave.clinicName!,
),
SizedBox(width: 8.h),
CustomButton(
text: "${sickLeave.sickLeaveDays} Days",
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 24.h,
AppCustomChipWidget(
labelText: "${sickLeave.sickLeaveDays} Days",
),
],
),
SizedBox(height: 8.h),
SizedBox(height: 12.h),
Row(
children: [
Expanded(

@ -80,7 +80,10 @@ class MonthlyReport extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.email.tr(context: context).toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500),
"${appState.getAuthenticatedUser()!.emailAddress}".toText16(color: AppColors.textColor, weight: FontWeight.w500),
SizedBox(
width: MediaQuery.of(context).size.width * 0.7,
child: "${appState.getAuthenticatedUser()!.emailAddress}".toText16(color: AppColors.textColor, weight: FontWeight.w500, maxlines: 2),
),
],
),
],

@ -8,6 +8,7 @@ import 'package:hmg_patient_app_new/core/enums.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/habib_wallet/habib_wallet_view_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/models/family_file_response_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/services/dialog_service.dart';
@ -25,6 +26,7 @@ class FamilyCards extends StatefulWidget {
final bool isRequestDesign;
final bool isLeftAligned;
final bool isShowRemoveButton;
final bool isForWalletRecharge;
const FamilyCards(
{super.key,
@ -35,6 +37,7 @@ class FamilyCards extends StatefulWidget {
this.isBottomSheet = false,
this.isRequestDesign = false,
this.isLeftAligned = false,
this.isForWalletRecharge = false,
this.isShowRemoveButton = false});
@override
@ -206,7 +209,21 @@ class _FamilyCardsState extends State<FamilyCards> {
height: 4.h,
),
Spacer(),
CustomButton(
widget.isForWalletRecharge ? CustomButton(
height: 40.h,
onPressed: () {
widget.onSelect(profile);
// if (canSwitch) widget.onSelect(profile);
},
text: LocaleKeys.select.tr(context: context),
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 13.h,
icon: AppAssets.activeCheck,
iconColor: isActive || !canSwitch ? (isActive ? null : AppColors.greyTextColor) : AppColors.primaryRedColor,
padding: EdgeInsets.symmetric(vertical: 0, horizontal: 0),
).paddingOnly(top: 0, bottom: 0) : CustomButton(
height: 40.h,
onPressed: () {
if (canSwitch) widget.onSelect(profile);

@ -148,7 +148,7 @@ class _PrescriptionDetailPageState extends State<PrescriptionDetailPage> {
text: LocaleKeys.downloadPrescription.tr(context: context),
onPressed: () async {
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.fetchingPrescriptionPDFPleaseWait.tr(context: context));
await prescriptionVM.getPrescriptionPDFBase64(widget.prescriptionsResponseModel).then((val) async {
await prescriptionVM.getPrescriptionPDFBase64(widget.prescriptionsResponseModel, onSuccess: (value) async {
LoaderBottomSheet.hideLoader();
if (prescriptionVM.prescriptionPDFBase64Data.isNotEmpty) {
String path = await Utils.createFileFromString(prescriptionVM.prescriptionPDFBase64Data, "pdf");
@ -164,6 +164,15 @@ class _PrescriptionDetailPageState extends State<PrescriptionDetailPage> {
);
}
}
}, onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: err),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
});
},
backgroundColor: AppColors.successColor.withValues(alpha: 0.15),

@ -10,6 +10,7 @@ import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_mo
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/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';
@ -56,74 +57,18 @@ class PrescriptionItemView extends StatelessWidget {
spacing: 6.h,
runSpacing: 6.h,
children: [
Row(
mainAxisSize: MainAxisSize.min,
children: [
CustomButton(
text: "${LocaleKeys.route.tr(context: context)}: ${isLoading ? "" : prescriptionVM.prescriptionDetailsList[index].route}",
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
).toShimmer2(isShow: isLoading),
],
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
CustomButton(
text: "${LocaleKeys.frequency.tr(context: context)}: ${isLoading ? "" : prescriptionVM.prescriptionDetailsList[index].frequency}",
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
).toShimmer2(isShow: isLoading),
],
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
CustomButton(
text: "${LocaleKeys.dailyDoses.tr(context: context)}: ${isLoading ? "" : prescriptionVM.prescriptionDetailsList[index].doseDailyQuantity}",
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
).toShimmer2(isShow: isLoading),
],
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
CustomButton(
text: "${LocaleKeys.days.tr(context: context)}: ${isLoading ? "" : prescriptionVM.prescriptionDetailsList[index].days}",
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
).toShimmer2(isShow: isLoading),
],
),
AppCustomChipWidget(
labelText: "${LocaleKeys.route.tr(context: context)}: ${isLoading ? "" : prescriptionVM.prescriptionDetailsList[index].route}",
).toShimmer2(isShow: isLoading),
AppCustomChipWidget(
labelText: "${LocaleKeys.frequency.tr(context: context)}: ${isLoading ? "" : prescriptionVM.prescriptionDetailsList[index].frequency}",
).toShimmer2(isShow: isLoading),
AppCustomChipWidget(
labelText: "${LocaleKeys.dailyDoses.tr(context: context)}: ${isLoading ? "" : prescriptionVM.prescriptionDetailsList[index].doseDailyQuantity}",
).toShimmer2(isShow: isLoading),
AppCustomChipWidget(
labelText: "${LocaleKeys.days.tr(context: context)}: ${isLoading ? "" : prescriptionVM.prescriptionDetailsList[index].days}",
).toShimmer2(isShow: isLoading),
],
).paddingSymmetrical(16.h, 0.h),
SizedBox(height: 8.h),

@ -19,6 +19,7 @@ import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/presentation/prescriptions/prescription_detail_page.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';
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';
@ -207,32 +208,16 @@ class _PrescriptionsListPageState extends State<PrescriptionsListPage> {
],
),
SizedBox(height: 8.h),
Row(
Wrap(
direction: Axis.horizontal,
spacing: 6.h,
runSpacing: 6.h,
children: [
CustomButton(
text: DateUtil.formatDateToDate(DateUtil.convertStringToDate(prescription.appointmentDate), false),
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 24.h,
AppCustomChipWidget(
labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(prescription.appointmentDate), false),
),
SizedBox(width: 8.h),
CustomButton(
text: model.isSortByClinic ? prescription.name! : prescription.clinicDescription!,
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 24.h,
AppCustomChipWidget(
labelText: model.isSortByClinic ? prescription.name! : prescription.clinicDescription!,
),
],
),

@ -658,6 +658,7 @@ class _AncillaryOrderDetailsListState extends State<AncillaryOrderDetailsList> {
text: LocaleKeys.proceedToPayment.tr(context: context),
onPressed: () {
// Navigate to payment page with selected procedures
todoSectionViewModel.setIsAncillaryOrdersNeedReloading(true);
Navigator.of(context).push(
CustomPageRoute(
page: AncillaryOrderPaymentPage(

@ -71,19 +71,12 @@ class AncillaryOrdersList extends StatelessWidget {
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: LocaleKeys.youDontHaveAnyAncillaryOrdersYet.tr(context: context),
isSmallWidget: true,
width: 62.w,
height: 62.h,
),
child: Utils.getNoDataWidget(
context,
noDataText: LocaleKeys.youDontHaveAnyAncillaryOrdersYet.tr(context: context),
isSmallWidget: true,
width: 62.w,
height: 62.h,
),
),
);

@ -32,7 +32,8 @@ abstract class DialogService {
required String message,
required Function(FamilyFileResponseModelLists response) onSwitchPress,
required List<FamilyFileResponseModelLists> profiles,
bool isShowManageButton = false});
bool isShowManageButton = false,
bool isForWalletRecharge = false});
Future<void> showFamilyBottomSheetWithoutHWithChild({String? label, required String message, Widget? child, required Function() onOkPressed, Function()? onCancelPressed});
@ -143,7 +144,8 @@ class DialogServiceImp implements DialogService {
required String message,
required Function(FamilyFileResponseModelLists response) onSwitchPress,
required List<FamilyFileResponseModelLists> profiles,
bool isShowManageButton = false}) async {
bool isShowManageButton = false,
bool isForWalletRecharge = false}) async {
final context = navigationService.navigatorKey.currentContext;
if (context == null) return;
showCommonBottomSheetWithoutHeight(context,
@ -161,6 +163,7 @@ class DialogServiceImp implements DialogService {
},
onRemove: (FamilyFileResponseModelLists profile) {},
isShowDetails: false,
isForWalletRecharge: isForWalletRecharge,
),
SizedBox(height: isShowManageButton ? 15.h : 24.h),
if (isShowManageButton)

Loading…
Cancel
Save