Merge pull request 'haroon_dev' (#83) from haroon_dev into master

Reviewed-on: #83
pull/84/head
Haroon6138 3 weeks ago
commit 97959f7f28

@ -0,0 +1,6 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="40" height="40" rx="10" fill="#ED1C2B"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.9532 11.875H18.5519C19.2167 11.875 19.7897 11.8749 20.2484 11.9366C20.7401 12.0027 21.2113 12.1518 21.5936 12.534C21.9758 12.9163 22.1249 13.3875 22.191 13.8792C22.2297 14.1674 22.2441 14.5006 22.2494 14.8735C22.2495 14.8743 22.2502 14.875 22.251 14.875C22.2515 14.875 22.252 14.8753 22.2523 14.8757C22.2523 14.8753 22.2525 14.8759 22.2523 14.8757L22.2526 18.625C22.2526 19.0392 22.5884 19.375 23.0026 19.375C23.4168 19.375 23.7526 19.0392 23.7526 18.625V15.2062C23.7526 15.037 23.8947 14.9013 24.0631 14.918C24.5229 14.9635 24.9373 15.061 25.3362 15.2869C25.7326 15.5113 26.0356 15.8184 26.3241 16.1918C26.619 16.5736 26.8784 16.9826 27.1369 17.3902L27.1378 17.3915C27.1804 17.4587 27.223 17.5258 27.2657 17.5927C27.7197 18.3045 28.2798 18.8915 28.9758 19.3684C29.649 19.8294 30.1843 20.196 30.4809 20.8008C30.6339 21.1129 30.6966 21.4308 30.7255 21.7686C30.7304 21.8262 30.6845 21.875 30.6267 21.875H29.7526C29.3384 21.875 29.0026 22.2108 29.0026 22.625C29.0026 23.0392 29.3384 23.375 29.7526 23.375H30.1492C30.4367 23.375 30.5804 23.375 30.6689 23.4671C30.7575 23.5592 30.7519 23.6987 30.7408 23.9777C30.7337 24.1584 30.7221 24.3261 30.7035 24.4795C30.6513 24.9105 30.5332 25.3379 30.2155 25.6973C29.8886 26.067 29.4382 26.2535 28.9563 26.3209C28.8177 26.3403 28.7063 26.2142 28.7229 26.0752C28.7406 25.9276 28.7496 25.7774 28.7496 25.625C28.7496 23.5539 27.0707 21.875 24.9996 21.875C22.9435 21.875 21.2441 23.5943 21.2497 25.646C21.2517 26.0141 21.2527 26.1981 21.1648 26.2866C21.0768 26.375 20.9147 26.375 20.5906 26.375H19.4087C19.0845 26.375 18.9225 26.375 18.8345 26.2866C18.7466 26.1981 18.7476 26.0141 18.7496 25.646C18.7552 23.5943 17.0558 21.875 14.9996 21.875C12.9286 21.875 11.2496 23.5539 11.2496 25.625C11.2496 25.8428 11.2682 26.0562 11.3038 26.2638C11.3087 26.2921 11.2851 26.3172 11.2567 26.3134C10.7651 26.2473 10.2938 26.0982 9.91158 25.716C9.52934 25.3338 9.38028 24.8625 9.31418 24.3709C9.25251 23.9122 9.25253 23.3394 9.25256 22.6746V15.5756C9.25253 14.9109 9.25251 14.3379 9.31418 13.8792C9.38028 13.3875 9.52934 12.9163 9.91158 12.534C10.2938 12.1518 10.7651 12.0027 11.2567 11.9366C11.7154 11.8749 12.2884 11.875 12.9532 11.875ZM16.7526 15.625C16.7526 15.2108 16.4168 14.875 16.0026 14.875C15.5884 14.875 15.2526 15.2108 15.2526 15.625V16.875H14.0026C13.5884 16.875 13.2526 17.2108 13.2526 17.625C13.2526 18.0392 13.5884 18.375 14.0026 18.375H15.2526V19.625C15.2526 20.0392 15.5884 20.375 16.0026 20.375C16.4168 20.375 16.7526 20.0392 16.7526 19.625V18.375H18.0026C18.4168 18.375 18.7526 18.0392 18.7526 17.625C18.7526 17.2108 18.4168 16.875 18.0026 16.875H16.7526V15.625Z" fill="white"/>
<path d="M27.5006 25.625C27.5006 27.0057 26.3813 28.125 25.0006 28.125C23.6199 28.125 22.5006 27.0057 22.5006 25.625C22.5006 24.2443 23.6199 23.125 25.0006 23.125C26.3813 23.125 27.5006 24.2443 27.5006 25.625Z" fill="white"/>
<path d="M15.0006 28.125C16.3813 28.125 17.5006 27.0057 17.5006 25.625C17.5006 24.2443 16.3813 23.125 15.0006 23.125C13.6199 23.125 12.5006 24.2443 12.5006 25.625C12.5006 27.0057 13.6199 28.125 15.0006 28.125Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

@ -0,0 +1,4 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="40" height="40" rx="10" fill="#ED1C2B"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M20 9.25C14.9276 9.25 10.75 13.1683 10.75 18.0807C10.75 20.893 11.9428 23.0764 14.2071 24.965C15.6434 26.1629 17.4087 28.18 18.4598 29.8028C18.7996 30.3274 19.3185 30.75 19.9859 30.75C20.6456 30.75 21.1704 30.3361 21.5267 29.8228C22.6504 28.2044 24.3659 26.1551 25.7929 24.965C28.0572 23.0764 29.25 20.893 29.25 18.0807C29.25 13.1683 25.0724 9.25 20 9.25ZM19 15C19 14.4477 18.5523 14 18 14C17.4477 14 17 14.4477 17 15V21C17 21.5523 17.4477 22 18 22C18.5523 22 19 21.5523 19 21V19H21V21C21 21.5523 21.4477 22 22 22C22.5523 22 23 21.5523 23 21V15C23 14.4477 22.5523 14 22 14C21.4477 14 21 14.4477 21 15V17H19V15Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 841 B

@ -0,0 +1,6 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="40" height="40" rx="10" fill="#ED1C2B"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.6588 11.8088C20.0918 11.6244 19.421 11.6246 18.439 11.625L14.9463 11.6251C13.8135 11.625 12.8878 11.625 12.1569 11.7234C11.3929 11.8262 10.7306 12.0488 10.2019 12.578C9.67321 13.1073 9.45093 13.7704 9.34822 14.5352C9.24996 15.2668 9.24998 16.1936 9.25 17.3277V21.4397C9.24998 22.5738 9.24996 23.5005 9.34822 24.2322C9.45093 24.997 9.67321 25.6601 10.2019 26.1893C10.3544 26.3421 10.5238 26.4708 10.7052 26.5793C10.9608 26.7323 11.0886 26.8087 11.1919 26.7453C11.2951 26.682 11.283 26.5048 11.2588 26.1503C11.253 26.0646 11.25 25.9781 11.25 25.8908C11.25 23.8175 12.9289 22.1367 15 22.1367C17.0711 22.1367 18.75 23.8175 18.75 25.8908C18.75 26.1263 18.7283 26.3567 18.6869 26.5802C18.6383 26.8423 18.614 26.9734 18.6733 27.0454C18.7326 27.1175 18.8546 27.1186 19.0985 27.1208C19.6993 27.1264 20.3007 27.1264 20.9015 27.1208C21.1454 27.1186 21.2674 27.1175 21.3267 27.0454C21.386 26.9734 21.3617 26.8423 21.3131 26.5802C21.2717 26.3567 21.25 26.1263 21.25 25.8908C21.25 23.8175 22.9289 22.1367 25 22.1367C27.0711 22.1367 28.75 23.8175 28.75 25.8908C28.75 25.9781 28.747 26.0646 28.7412 26.1503C28.717 26.5047 28.7049 26.682 28.8081 26.7453C28.9114 26.8087 29.0392 26.7323 29.2948 26.5793C29.4762 26.4708 29.6456 26.3421 29.7981 26.1893C30.3268 25.6601 30.5491 24.997 30.6518 24.2322C30.75 23.5006 30.75 22.5738 30.75 21.4398V19.4058C30.7539 19.2381 30.7156 19.0672 30.6305 18.909L29.3317 16.4943C29.0059 15.8883 28.7268 15.3694 28.4518 14.9623C28.1579 14.5272 27.8252 14.1513 27.3593 13.8727C26.8933 13.5941 26.4051 13.4791 25.883 13.4263C25.3946 13.3769 24.8059 13.377 24.1185 13.377L23.0032 13.377C22.847 13.377 22.7689 13.377 22.706 13.3458C22.643 13.3146 22.5899 13.2447 22.4835 13.1049C22.027 12.5047 21.394 12.048 20.6588 11.8088ZM23.6542 15.3792C23.4593 15.3792 23.3618 15.3792 23.3031 15.4387C23.2444 15.4982 23.2456 15.5974 23.2479 15.7958C23.2502 15.9955 23.2501 16.2098 23.25 16.4414C23.25 17.2452 23.2593 17.6082 23.3112 17.7682C23.4348 18.1491 23.7332 18.4478 24.1137 18.5716C24.2735 18.6236 24.4971 18.6329 25.3 18.6329H27.5408C27.8397 18.6329 27.9891 18.6329 28.0473 18.5353C28.1056 18.4378 28.0347 18.306 27.893 18.0426L27.5932 17.4851C27.2387 16.8261 27.0068 16.3974 26.795 16.0839C26.5959 15.7891 26.46 15.6672 26.3337 15.5917C26.2075 15.5162 26.0358 15.4542 25.6821 15.4184C25.306 15.3804 24.819 15.3792 24.0713 15.3792H23.6542ZM14.0001 15.375C14.5524 15.375 15.0001 15.8227 15.0001 16.375V18.375C15.0001 18.9273 14.5524 19.375 14.0001 19.375C13.4478 19.375 13.0001 18.9273 13.0001 18.375V16.375C13.0001 15.8227 13.4478 15.375 14.0001 15.375ZM18.0001 15.375C18.5524 15.375 19.0001 15.8227 19.0001 16.375V18.375C19.0001 18.9273 18.5524 19.375 18.0001 19.375C17.4479 19.375 17.0001 18.9273 17.0001 18.375V16.375C17.0001 15.8227 17.4479 15.375 18.0001 15.375Z" fill="white"/>
<path d="M15 23.375C13.6193 23.375 12.5 24.4943 12.5 25.875C12.5 27.2557 13.6193 28.375 15 28.375C16.3807 28.375 17.5 27.2557 17.5 25.875C17.5 24.4943 16.3807 23.375 15 23.375Z" fill="white"/>
<path d="M25 23.375C23.6193 23.375 22.5 24.4943 22.5 25.875C22.5 27.2557 23.6193 28.375 25 28.375C26.3807 28.375 27.5 27.2557 27.5 25.875C27.5 24.4943 26.3807 23.375 25 23.375Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

@ -174,7 +174,7 @@ class ApiClientImp implements ApiClient {
}
// body['TokenID'] = "@dm!n";
// body['PatientID'] = 4772172;
// body['PatientID'] = 3966014;
// body['PatientTypeID'] = 1;
//
// body['PatientOutSA'] = 0;

@ -724,6 +724,8 @@ const DEACTIVATE_ACCOUNT = 'Services/Patients.svc/REST/PatientAppleActivation_In
const FAMILY_FILES= 'Services/Authentication.svc/REST/GetAllSharedRecordsByStatus';
var GET_PRESCRIPTION_INSTRUCTIONS_PDF = 'Services/ChatBot_Service.svc/REST/Chatbot_SendMedicationInstructionByWhatsApp';
class ApiConsts {
static const maxSmallScreen = 660;

@ -155,6 +155,9 @@ class AppAssets {
static const String switch_user = '$svgBasePath/switch_user.svg';
static const String activeCheck = '$svgBasePath/active-check.svg';
static const String deleteIcon = '$svgBasePath/delete_icon.svg';
static const String call_ambulance_icon = '$svgBasePath/call_ambulance_icon.svg';
static const String nearest_er_icon = '$svgBasePath/nearest_er_icon.svg';
static const String rrt_icon = '$svgBasePath/rrt_icon.svg';
//bottom navigation//
static const String homeBottom = '$svgBasePath/home_bottom.svg';

@ -1,16 +1,20 @@
import 'dart:io';
import 'dart:ui';
import 'package:easy_localization/easy_localization.dart';
import 'package:geolocator/geolocator.dart';
import 'package:gms_check/gms_check.dart';
import 'package:google_maps_flutter/google_maps_flutter.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/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:huawei_location/huawei_location.dart' as HmsLocation show FusedLocationProviderClient, Location, LocationSettingsRequest, LocationRequest;
import 'package:location/location.dart' show Location, PermissionStatus, LocationData;
import 'package:permission_handler/permission_handler.dart' show Permission, PermissionListActions, PermissionStatusGetters;
import 'package:permission_handler/permission_handler.dart' show Permission, PermissionListActions, PermissionStatusGetters, openAppSettings;
class LocationUtils {
NavigationService navigationService;
@ -68,6 +72,26 @@ class LocationUtils {
onFailure?.call();
return;
}
} else if (permissionGranted == LocationPermission.deniedForever) {
if (isShowConfirmDialog) {
showCommonBottomSheetWithoutHeight(
title: LocaleKeys.notice.tr(context: navigationService.navigatorKey.currentContext!),
navigationService.navigatorKey.currentContext!,
child: Utils.getWarningWidget(
loadingText: "Please grant location permission from app settings to see the nearest ER location details".needTranslation,
isShowActionButtons: true,
onCancelTap: () {
navigationService.pop();
},
onConfirmTap: () async {
navigationService.pop();
openAppSettings();
}),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}
}
Position? currentLocation = await Geolocator.getLastKnownPosition();

@ -64,9 +64,11 @@ extension EmailValidator on String {
),
);
Widget toText12({Color? color, bool isUnderLine = false, bool isBold = false, FontWeight? fontWeight, bool isCenter = false, double? height, int maxLine = 0}) => Text(
Widget toText12(
{Color? color, bool isUnderLine = false, TextAlign textAlignment = TextAlign.start, bool isBold = false, FontWeight? fontWeight, bool isCenter = false, double? height, int maxLine = 0}) =>
Text(
this,
textAlign: isCenter ? TextAlign.center : null,
textAlign: isCenter ? TextAlign.center : textAlignment,
maxLines: (maxLine > 0) ? maxLine : null,
style: TextStyle(
fontSize: 12.fSize,

@ -457,6 +457,7 @@ class AuthenticationViewModel extends ChangeNotifier {
int? patientID,
}) async {
bool isForRegister = (_appState.getUserRegistrationPayload.healthId != null || _appState.getUserRegistrationPayload.patientOutSa == true || _appState.getUserRegistrationPayload.patientOutSa == 1);
MyAppointmentsViewModel myAppointmentsVM = getIt<MyAppointmentsViewModel>();
final request = RequestUtils.getCommonRequestWelcome(
phoneNumber: phoneNumberController.text,
@ -587,6 +588,7 @@ class AuthenticationViewModel extends ChangeNotifier {
}
// _appState.setUserBloodGroup = (activation.patientBlodType ?? "");
_appState.setAppAuthToken = activation.authenticationTokenId;
myAppointmentsVM.getActiveAppointmentsCount();
final request = RequestUtils.getAuthanticatedCommonRequest().toJson();
bool isUserAgreedBefore = await checkIfUserAgreedBefore(request: request);
@ -607,7 +609,6 @@ class AuthenticationViewModel extends ChangeNotifier {
LoaderBottomSheet.hideLoader();
navigateToHomeScreen();
} else {
MyAppointmentsViewModel myAppointmentsVM = getIt<MyAppointmentsViewModel>();
myAppointmentsVM.setIsAppointmentDataToBeLoaded(true);
LoaderBottomSheet.hideLoader();
navigateToHomeScreen();

@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/features/insurance/insurance_repo.dart';
import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_approval_response_model.dart';
import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_card_history.dart';
@ -14,9 +15,10 @@ class InsuranceViewModel extends ChangeNotifier {
bool isInsuranceUpdateDetailsLoading = false;
bool isInsuranceDataToBeLoaded = true;
bool isInsuranceApprovalsLoading = false;
bool isInsuranceExpired = false;
InsuranceRepo insuranceRepo;
ErrorHandlerService errorHandlerService;
@ -85,6 +87,11 @@ class InsuranceViewModel extends ChangeNotifier {
patientInsuranceList = apiResponse.data!;
isInsuranceLoading = false;
isInsuranceDataToBeLoaded = false;
isInsuranceExpired = DateTime.now().isAfter(
DateUtil.convertStringToDate(patientInsuranceList.first.cardValidTo),
);
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);

@ -3,6 +3,8 @@ import 'package:hmg_patient_app_new/core/api_consts.dart';
import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart';
import 'package:dartz/dartz.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart';
import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_special_result.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
@ -13,13 +15,12 @@ abstract class LabRepo {
Future<Either<Failure, GenericApiModel<List<PatientLabOrdersResponseModel>>>> getPatientLabOrders();
Future<Either<Failure, GenericApiModel<List<LabResult>>>> getPatientLabResults(PatientLabOrdersResponseModel laborder, bool isVidaPlus, String procedureName);
Future<Either<Failure, GenericApiModel<List<LabResult>>>>
getPatientLabResultsByHospitals(
PatientLabOrdersResponseModel laborder, bool isVidaPlus);
Future<Either<Failure, GenericApiModel<List<LabResult>>>> getPatientLabResultsByHospitals(PatientLabOrdersResponseModel laborder, bool isVidaPlus);
Future<Either<Failure, GenericApiModel<List<PatientLabSpecialResult>>>> getSpecialLabResult(PatientLabOrdersResponseModel laborder, bool isVidaPlus);
Future<Either<Failure, GenericApiModel<String>>> getLabResultReportPDF({required PatientLabOrdersResponseModel labOrder});
Future<Either<Failure, GenericApiModel<List<PatientLabSpecialResult>>>>
getSpecialLabResult(
PatientLabOrdersResponseModel laborder, bool isVidaPlus);
}
class LabRepoImp implements LabRepo {
@ -223,4 +224,58 @@ class LabRepoImp implements LabRepo {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<String>>> getLabResultReportPDF({required PatientLabOrdersResponseModel labOrder}) async {
Map<String, dynamic> mapDevice = {
"InvoiceNo": Utils.isVidaPlusProject(int.parse(labOrder.projectID!)) ? "0" : labOrder.invoiceNo,
"InvoiceNo_VP": Utils.isVidaPlusProject(int.parse(labOrder.projectID!)) ? labOrder.invoiceNo : "0",
// "LineItemNo": labOrder.invoiceLineItemNo,
// "InvoiceLineItemNo": labOrder.invoiceLineItemNo,
"ProjectID": labOrder.projectID!,
"DoctorID": labOrder.doctorID!,
"OrderNo": labOrder.orderNo!,
"InvoiceType": labOrder.invoiceType!,
"SetupID": labOrder.setupID!,
"IsDownload": true,
'ClinicName': labOrder.clinicDescription,
'DateofBirth': Utils.appState.getAuthenticatedUser()!.dateofBirth,
'DoctorName': labOrder.doctorName,
'OrderDate': '${DateUtil.convertStringToDate(labOrder.orderDate!).year}-${DateUtil.convertStringToDate(labOrder.orderDate!).month}-${DateUtil.convertStringToDate(labOrder.orderDate!).day}',
'PatientIditificationNum': Utils.appState.getAuthenticatedUser()!.patientIdentificationNo,
'PatientMobileNumber': Utils.appState.getAuthenticatedUser()!.mobileNumber,
'PatientName': "${Utils.appState.getAuthenticatedUser()!.firstName!} ${Utils.appState.getAuthenticatedUser()!.lastName!}",
'ProjectName': labOrder.projectName,
"To": Utils.appState.getAuthenticatedUser()!.emailAddress
};
try {
GenericApiModel<String>? apiResponse;
Failure? failure;
await apiClient.post(
Utils.isVidaPlusProject(int.parse(labOrder.projectID!)) ? SEND_LAB_RESULT_EMAIL : SEND_LAB_RESULT_EMAIL_NEW,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
apiResponse = GenericApiModel<String>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: response["PdfContent"],
);
} 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()));
}
}
}

@ -36,6 +36,7 @@ class LabViewModel extends ChangeNotifier {
List<PatientLabOrdersResponseModel> tempLabOrdersList = [];
String labSpecialResult = "";
List<String> labOrderTests = [];
String patientLabResultReportPDFBase64 = "";
PatientLabOrdersResponseModel? currentlySelectedPatientOrder;
@ -130,6 +131,31 @@ class LabViewModel extends ChangeNotifier {
);
}
Future<void> getLabResultReportPDF({required PatientLabOrdersResponseModel labOrder, Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await labRepo.getLabResultReportPDF(labOrder: labOrder);
result.fold(
(failure) async => await errorHandlerService.handleError(
failure: failure,
onOkPressed: () {
onError!(failure.message);
},
),
(apiResponse) {
if (apiResponse.messageStatus == 2) {
onError!(apiResponse.errorMessage!);
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
patientLabResultReportPDFBase64 = apiResponse.data!;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
filterSuggestions() {
final List<String> labels = patientLabOrders
.expand((order) => order.testDetails!)

@ -155,10 +155,10 @@ class PatientLabOrdersResponseModel {
status = json['Status'];
statusDesc = json['StatusDesc'];
strOrderDate = json['StrOrderDate'];
if (json['TestDetails'] != dynamic) {
if (json['TestDetails'] != null) {
testDetails = <TestDetails>[];
json['TestDetails'].forEach((v) {
testDetails!.add(new TestDetails.fromJson(v));
testDetails!.add(TestDetails.fromJson(v));
});
}
}

@ -382,7 +382,10 @@ class MedicalFileViewModel extends ChangeNotifier {
final result = await medicalFileRepo.getAllPendingRecordsByResponseId(request: {'ResponseID': appState.getAuthenticatedUser()!.patientId ?? "0", "Status": 2});
result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure),
// (failure) async => await errorHandlerService.handleError(failure: failure),
(failure) async {
log("Error in fetching pending family files: ${failure.message}");
},
(apiResponse) {
if (apiResponse.messageStatus == 2) {
// _dialogService.showErrorBottomSheet(message: apiResponse.errorMessage!, onOkPressed: () {});

@ -47,6 +47,8 @@ abstract class MyAppointmentsRepo {
Future<Either<Failure, GenericApiModel<dynamic>>> insertLiveCareVIDARequest({required clientRequestID, required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel});
Future<Either<Failure, GenericApiModel<GetTamaraInstallmentsDetailsResponseModel>>> getTamaraInstallmentsDetails();
Future<Either<Failure, GenericApiModel<dynamic>>> getActiveAppointmentsCount();
}
class MyAppointmentsRepoImp implements MyAppointmentsRepo {
@ -476,9 +478,6 @@ class MyAppointmentsRepoImp implements MyAppointmentsRepo {
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response['PatientDoctorAppointmentResultList'];
// if (list == null || list.isEmpty) {
// throw Exception("Appointments list is empty");
// }
final appointmentsList = list.map((item) => PatientAppointmentHistoryResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<PatientAppointmentHistoryResponseModel>();
@ -583,4 +582,40 @@ class MyAppointmentsRepoImp implements MyAppointmentsRepo {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel>> getActiveAppointmentsCount() async {
Map<String, dynamic> mapDevice = {};
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(
GET_ACTIVE_APPOINTMENTS_LIST_URL,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final appointmentCount = response['AppointmentActiveNumber'];
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: appointmentCount,
);
} 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()));
}
}
}

@ -17,6 +17,8 @@ class MyAppointmentsViewModel extends ChangeNotifier {
ErrorHandlerService errorHandlerService;
AppState appState;
int activeAppointmentsCount = 0;
bool isMyAppointmentsLoading = false;
bool isAppointmentPatientShareLoading = false;
bool isTimeLineAppointmentsLoading = false;
@ -64,7 +66,6 @@ class MyAppointmentsViewModel extends ChangeNotifier {
isMyAppointmentsLoading = true;
isTimeLineAppointmentsLoading = true;
patientMyDoctorsList.clear();
isPatientMyDoctorsLoading = true;
}
isTamaraDetailsLoading = true;
isAppointmentPatientShareLoading = true;
@ -121,10 +122,12 @@ class MyAppointmentsViewModel extends ChangeNotifier {
patientArrivedAppointmentsHistoryList.clear();
notifyListeners();
final result = await myAppointmentsRepo.getPatientAppointments(isActiveAppointment: isActiveAppointment, isArrivedAppointments: isArrivedAppointments);
final resultArrived = await myAppointmentsRepo.getPatientAppointments(isActiveAppointment: false, isArrivedAppointments: true);
final results = await Future.wait([
myAppointmentsRepo.getPatientAppointments(isActiveAppointment: isActiveAppointment, isArrivedAppointments: isArrivedAppointments),
myAppointmentsRepo.getPatientAppointments(isActiveAppointment: false, isArrivedAppointments: true),
]);
result.fold(
results[0].fold(
(failure) async => await errorHandlerService.handleError(failure: failure),
(apiResponse) {
if (apiResponse.messageStatus == 2) {
@ -141,7 +144,7 @@ class MyAppointmentsViewModel extends ChangeNotifier {
},
);
resultArrived.fold(
results[1].fold(
(failure) async => await errorHandlerService.handleError(failure: failure),
(apiResponse) {
if (apiResponse.messageStatus == 2) {
@ -149,6 +152,7 @@ class MyAppointmentsViewModel extends ChangeNotifier {
} else if (apiResponse.messageStatus == 1) {
patientArrivedAppointmentsHistoryList = apiResponse.data!;
isMyAppointmentsLoading = false;
isAppointmentDataToBeLoaded = false;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
@ -363,6 +367,9 @@ class MyAppointmentsViewModel extends ChangeNotifier {
Future<void> getPatientMyDoctors({Function(dynamic)? onSuccess, Function(String)? onError}) async {
if (!isAppointmentDataToBeLoaded) return;
isPatientMyDoctorsLoading = true;
notifyListeners();
final result = await myAppointmentsRepo.getPatientDoctorsList();
result.fold(
@ -527,17 +534,26 @@ class MyAppointmentsViewModel extends ChangeNotifier {
if (onSuccess != null) {
onSuccess(apiResponse);
}
},
);
}
// if (apiResponse.messageStatus == 2) {
// onError!(apiResponse.errorMessage!);
// // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
// } else if (apiResponse.messageStatus == 1) {
// getTamaraInstallmentsDetailsResponseModel = apiResponse.data!;
// notifyListeners();
// if (onSuccess != null) {
// onSuccess(apiResponse);
// }
// }
Future<void> getActiveAppointmentsCount({Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await myAppointmentsRepo.getActiveAppointmentsCount();
result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure),
(apiResponse) {
if (apiResponse.messageStatus == 2) {
onError!(apiResponse.errorMessage!);
activeAppointmentsCount = 0;
} else if (apiResponse.messageStatus == 1) {
activeAppointmentsCount = apiResponse.data ?? 0;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}

@ -3,6 +3,7 @@ import 'package:hmg_patient_app_new/core/api_consts.dart';
import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart';
import 'package:dartz/dartz.dart';
import 'package:hmg_patient_app_new/core/utils/utils.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/models/resp_models/prescription_detail_response_model.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
@ -11,6 +12,10 @@ abstract class PrescriptionsRepo {
Future<Either<Failure, GenericApiModel<List<PatientPrescriptionsResponseModel>>>> getPatientPrescriptionOrders({required String patientId});
Future<Either<Failure, GenericApiModel<List<PrescriptionDetailResponseModel>>>> getPatientPrescriptionDetails({required PatientPrescriptionsResponseModel prescriptionsResponseModel});
Future<Either<Failure, GenericApiModel<dynamic>>> getPrescriptionInstructionsPDF({required PatientPrescriptionsResponseModel prescriptionsResponseModel});
Future<Either<Failure, GenericApiModel<dynamic>>> getPrescriptionPDF({required PatientPrescriptionsResponseModel prescriptionsResponseModel, required List<PrescriptionDetailResponseModel> prescriptionDetailsList});
}
class PrescriptionsRepoImp implements PrescriptionsRepo {
@ -107,4 +112,98 @@ class PrescriptionsRepoImp implements PrescriptionsRepo {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel>> getPrescriptionInstructionsPDF({required PatientPrescriptionsResponseModel prescriptionsResponseModel}) async {
Map<String, dynamic> mapDevice = {
"AppointmentNo": prescriptionsResponseModel.appointmentNo.toString(),
"SetupID": prescriptionsResponseModel.setupID,
"ClinicID": prescriptionsResponseModel.clinicID.toString(),
"ProjectID": prescriptionsResponseModel.projectID.toString(),
"LocationID": "0",
"SalesInvoiceNo": "0",
"IsTest": false,
"ChannelID": "3",
};
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(
GET_PRESCRIPTION_INSTRUCTIONS_PDF,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final prescriptionPDFURL = response["InvoiceUrl"];
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: prescriptionPDFURL,
);
} 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()));
}
}
@override
Future<Either<Failure, GenericApiModel>> getPrescriptionPDF({required PatientPrescriptionsResponseModel prescriptionsResponseModel, required List<PrescriptionDetailResponseModel> prescriptionDetailsList}) async {
Map<String, dynamic> mapDevice = {
"AppointmentDate": prescriptionsResponseModel.appointmentDate,
"ClinicName": prescriptionsResponseModel.clinicDescription,
"DoctorName": prescriptionsResponseModel.doctorName,
"ProjectID": prescriptionsResponseModel.projectID,
"DoctorID": prescriptionsResponseModel.doctorID,
"ClinicID": prescriptionsResponseModel.clinicID,
"DateofBirth": Utils.appState.getAuthenticatedUser()!.dateofBirth,
"ListPrescriptions": prescriptionDetailsList,
"PatientIditificationNum": Utils.appState.getAuthenticatedUser()!.patientIdentificationNo,
"PatientMobileNumber": Utils.appState.getAuthenticatedUser()!.mobileNumber,
"PatientName": "${Utils.appState.getAuthenticatedUser()!.firstName!} ${Utils.appState.getAuthenticatedUser()!.lastName!}",
"To": Utils.appState.getAuthenticatedUser()!.emailAddress,
"SetupID": prescriptionsResponseModel.setupID,
"IsDownload": true,
};
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(
SEND_PRESCRIPTION_EMAIL,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final prescriptionPDFData = response["Base64Data"];
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: prescriptionPDFData,
);
} 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()));
}
}
}

@ -23,6 +23,10 @@ class PrescriptionsViewModel extends ChangeNotifier {
bool isSortByClinic = true;
String prescriptionInstructionsPDFLink = "";
String prescriptionPDFBase64Data = "";
PrescriptionsViewModel({required this.prescriptionsRepo, required this.errorHandlerService});
initPrescriptionsViewModel() {
@ -126,4 +130,47 @@ class PrescriptionsViewModel extends ChangeNotifier {
},
);
}
Future<void> getPrescriptionInstructionsPDF(PatientPrescriptionsResponseModel prescriptionsResponseModel, {Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await prescriptionsRepo.getPrescriptionInstructionsPDF(prescriptionsResponseModel: prescriptionsResponseModel);
result.fold(
(failure) async {
onError!(failure.message);
},
(apiResponse) {
if (apiResponse.messageStatus == 2) {
onError!(apiResponse.errorMessage!);
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
prescriptionInstructionsPDFLink = apiResponse.data;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
Future<void> getPrescriptionPDFBase64(PatientPrescriptionsResponseModel prescriptionsResponseModel, {Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await prescriptionsRepo.getPrescriptionPDF(prescriptionsResponseModel: prescriptionsResponseModel, prescriptionDetailsList: prescriptionDetailsList);
result.fold(
(failure) async {
onError!(failure.message);
},
(apiResponse) {
if (apiResponse.messageStatus == 2) {
onError!(apiResponse.errorMessage!);
} else if (apiResponse.messageStatus == 1) {
prescriptionPDFBase64Data = apiResponse.data;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
}

@ -86,30 +86,25 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
onAskDoctorTap: () {},
onCancelTap: () async {
myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
showCommonBottomSheet(context,
child: Utils.getLoadingWidget(),
callBackFunc: (str) {},
title: "",
height: ResponsiveExtension.screenHeight * 0.3,
isCloseButtonVisible: false,
isDismissible: false,
isFullScreen: false);
LoaderBottomSheet.showLoader(loadingText: "Cancelling Appointment, Please Wait...".needTranslation);
await myAppointmentsViewModel.cancelAppointment(
patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel,
onSuccess: (apiResponse) {
Navigator.of(context).pop();
showCommonBottomSheet(context,
child: Utils.getSuccessWidget(loadingText: "Appointment Cancelled Successfully".needTranslation),
callBackFunc: (str) {},
title: "",
height: ResponsiveExtension.screenHeight * 0.3,
isCloseButtonVisible: false,
isDismissible: false,
isFullScreen: false,
isSuccessDialog: true);
LoaderBottomSheet.hideLoader();
myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
myAppointmentsViewModel.getPatientAppointments(true, false);
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getSuccessWidget(loadingText: "Appointment Cancelled Successfully".needTranslation),
callBackFunc: () {
Navigator.of(context).pop();
},
title: "",
isCloseButtonVisible: true,
isDismissible: false,
isFullScreen: false,
);
});
Navigator.of(context).pop();
Navigator.of(context).pop();
},
onRescheduleTap: () async {
openDoctorScheduleCalendar();
@ -510,7 +505,7 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
projectName: widget.patientAppointmentHistoryResponseModel.projectName,
);
bookAppointmentsViewModel.setSelectedDoctor(doctor);
LoaderBottomSheet.showLoader();
LoaderBottomSheet.showLoader(loadingText: "Fetching Doctor Schedule, Please Wait...".needTranslation);
await bookAppointmentsViewModel.getDoctorFreeSlots(
isBookingForLiveCare: false,
onSuccess: (dynamic respData) async {
@ -541,24 +536,19 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
case 0:
break;
case 10:
showCommonBottomSheet(context,
child: Utils.getLoadingWidget(), callBackFunc: (str) {}, title: "", height: ResponsiveExtension.screenHeight * 0.3, isCloseButtonVisible: false, isDismissible: false, isFullScreen: false);
LoaderBottomSheet.showLoader(loadingText: "Confirming Appointment, Please Wait...".needTranslation);
await myAppointmentsViewModel.confirmAppointment(
patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel,
onSuccess: (apiResponse) {
Navigator.of(context).pop();
showCommonBottomSheet(context,
child: Utils.getSuccessWidget(loadingText: "Appointment Confirmed Successfully".needTranslation),
callBackFunc: (str) {},
title: "",
height: ResponsiveExtension.screenHeight * 0.3,
isCloseButtonVisible: false,
isDismissible: false,
isFullScreen: false,
LoaderBottomSheet.hideLoader();
myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
myAppointmentsViewModel.getPatientAppointments(true, false);
showCommonBottomSheet(context, child: Utils.getSuccessWidget(loadingText: "Appointment Confirmed Successfully".needTranslation), callBackFunc: (str) {
Navigator.of(context).pop();
}, title: "", height: ResponsiveExtension.screenHeight * 0.3, isCloseButtonVisible: true, isDismissible: false, isFullScreen: false,
isSuccessDialog: true);
});
Navigator.of(context).pop();
Navigator.of(context).pop();
// LoaderBottomSheet.hideLoader();
case 15:
break;
case 20:

@ -8,10 +8,16 @@ 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/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/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/doctor_profile_page.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/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:provider/provider.dart';
import '../../widgets/chip/app_custom_chip_widget.dart';
@ -20,11 +26,13 @@ class MyDoctorsPage extends StatelessWidget {
MyDoctorsPage({super.key});
late MyAppointmentsViewModel myAppointmentsViewModel;
late BookAppointmentsViewModel bookAppointmentsViewModel;
@override
Widget build(BuildContext context) {
AppState appState = getIt.get<AppState>();
myAppointmentsViewModel = Provider.of<MyAppointmentsViewModel>(context, listen: false);
bookAppointmentsViewModel = Provider.of<BookAppointmentsViewModel>(context, listen: false);
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView(
@ -141,13 +149,38 @@ class MyDoctorsPage extends StatelessWidget {
"".toText16(),
Transform.flip(
flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, width: 15.h, height: 15.h, fit: BoxFit.contain, iconColor: AppColors.textColor)),
child:
Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon_small, width: 15.h, height: 15.h, fit: BoxFit.contain, iconColor: AppColors.textColor)),
],
),
],
),
),
),
).onPress(() async {
bookAppointmentsViewModel.setSelectedDoctor(DoctorsListResponseModel(
clinicID: myAppointmentsVM.patientMyDoctorsList[index].clinicID,
projectID: myAppointmentsVM.patientMyDoctorsList[index].projectID,
doctorID: myAppointmentsVM.patientMyDoctorsList[index].doctorID,
));
LoaderBottomSheet.showLoader();
await bookAppointmentsViewModel.getDoctorProfile(onSuccess: (dynamic respData) {
LoaderBottomSheet.hideLoader();
Navigator.of(context).push(
CustomPageRoute(
page: DoctorProfilePage(),
),
);
}, onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: err),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
});
}),
),
),
);

@ -117,26 +117,26 @@ class _AppointmentCardState extends State<AppointmentCard> {
).toShimmer2(isShow: widget.isLoading),
),
// TODO: Implement the logic to enable/disable the switch based on reminder status
AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)
? SizedBox().toShimmer2(isShow: widget.isLoading)
: Switch(
activeColor: AppColors.successColor,
activeTrackColor: AppColors.successColor.withValues(alpha: .15),
thumbIcon: WidgetStateProperty.resolveWith<Icon?>(
(Set<WidgetState> states) {
if (states.contains(WidgetState.selected)) {
return const Icon(Icons.check); // Icon when switch is ON
}
return const Icon(Icons.close); // Icon when switch is OFF
},
),
value: widget.isLoading ? false : widget.patientAppointmentHistoryResponseModel.hasReminder!,
onChanged: (newValue) {
setState(() {
widget.myAppointmentsViewModel.setAppointmentReminder(newValue, widget.patientAppointmentHistoryResponseModel);
});
},
).toShimmer2(isShow: widget.isLoading),
// AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)
// ? SizedBox().toShimmer2(isShow: widget.isLoading)
// : Switch(
// activeColor: AppColors.successColor,
// activeTrackColor: AppColors.successColor.withValues(alpha: .15),
// thumbIcon: WidgetStateProperty.resolveWith<Icon?>(
// (Set<WidgetState> states) {
// if (states.contains(WidgetState.selected)) {
// return const Icon(Icons.check); // Icon when switch is ON
// }
// return const Icon(Icons.close); // Icon when switch is OFF
// },
// ),
// value: widget.isLoading ? false : widget.patientAppointmentHistoryResponseModel.hasReminder!,
// onChanged: (newValue) {
// setState(() {
// widget.myAppointmentsViewModel.setAppointmentReminder(newValue, widget.patientAppointmentHistoryResponseModel);
// });
// },
// ).toShimmer2(isShow: widget.isLoading),
],
),
SizedBox(height: 16.h),
@ -155,7 +155,7 @@ class _AppointmentCardState extends State<AppointmentCard> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
(widget.isLoading ? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png" : widget.patientAppointmentHistoryResponseModel.doctorNameObj!)
.toText16(isBold: true)
.toText16(isBold: true, maxlines: 1)
.toShimmer2(isShow: widget.isLoading),
SizedBox(height: 8.h),
Wrap(

@ -303,7 +303,12 @@ class _SelectClinicPageState extends State<SelectClinicPage> {
id = regionalViewModel.selectedHospital?.patientDoctorAppointmentList?.first.projectID?.toString() ?? "";
}
if (bookAppointmentsViewModel.selectedClinic.clinicID == 17) {
if (!appState.isAuthenticated) {
if (appState.isAuthenticated) {
initDentalAppointmentBookingFlow(int.parse(bookAppointmentsViewModel
.currentlySelectedHospitalFromRegionFlow ??
"0"));
}else {
bookAppointmentsViewModel.setIsChiefComplaintsListLoading(true);
}
}
@ -459,7 +464,11 @@ class _SelectClinicPageState extends State<SelectClinicPage> {
isCloseButtonVisible: true,
);
} else {
// Navigate to Chief Complaint Screen
Navigator.of(context).push(
CustomPageRoute(
page: DentalChiefComplaintsPage(),
),
);
}
});
}

@ -0,0 +1,122 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/location_util.dart';
import 'package:hmg_patient_app_new/core/utils/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/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/emergency_services/nearest_er_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/routes/custom_page_route.dart';
class EmergencyServicesPage extends StatelessWidget {
EmergencyServicesPage({super.key});
LocationUtils? locationUtils;
@override
Widget build(BuildContext context) {
locationUtils = getIt.get<LocationUtils>();
locationUtils!.isShowConfirmDialog = true;
return CollapsingListView(
title: "Emergency Services",
requests: () {},
child: Padding(
padding: EdgeInsets.all(24.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: EdgeInsets.all(16.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
),
child: Row(
children: [
Utils.buildSvgWithAssets(icon: AppAssets.call_ambulance_icon, width: 40.h, height: 40.h),
SizedBox(width: 12.h),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Call Ambulance".needTranslation.toText16(isBold: true, color: AppColors.blackColor),
"Request and ambulance in emergency from home or hospital".needTranslation.toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500),
],
),
),
SizedBox(width: 12.h),
Utils.buildSvgWithAssets(icon: AppAssets.forward_chevron_icon, width: 13.h, height: 13.h),
],
),
),
SizedBox(height: 16.h),
Container(
padding: EdgeInsets.all(16.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
),
child: Row(
children: [
Utils.buildSvgWithAssets(icon: AppAssets.nearest_er_icon, width: 40.h, height: 40.h),
SizedBox(width: 12.h),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Nearest ER Location".needTranslation.toText16(isBold: true, color: AppColors.blackColor),
"Get the details of nearest branch including directions".needTranslation.toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500),
],
),
),
SizedBox(width: 12.h),
Utils.buildSvgWithAssets(icon: AppAssets.forward_chevron_icon, width: 13.h, height: 13.h),
],
).onPress(() {
locationUtils!.getLocation(onSuccess: (position) {
Navigator.of(context).push(
CustomPageRoute(
page: NearestErPage(),
),
);
});
}),
),
SizedBox(height: 16.h),
Container(
padding: EdgeInsets.all(16.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
),
child: Row(
children: [
Utils.buildSvgWithAssets(icon: AppAssets.rrt_icon, width: 40.h, height: 40.h),
SizedBox(width: 12.h),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Rapid Response Team (RRT)".toText16(isBold: true, color: AppColors.blackColor),
"Comprehensive medical service for all sorts of urgent and stable cases".toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500),
],
),
),
SizedBox(width: 12.h),
Utils.buildSvgWithAssets(icon: AppAssets.forward_chevron_icon, width: 13.h, height: 13.h),
],
),
),
],
),
),
);
}
}

@ -0,0 +1,19 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
class NearestErPage extends StatefulWidget {
const NearestErPage({super.key});
@override
State<NearestErPage> createState() => _NearestErPageState();
}
class _NearestErPageState extends State<NearestErPage> {
@override
Widget build(BuildContext context) {
return CollapsingListView(
title: "Nearest ER",
child: Container(),
);
}
}

@ -0,0 +1,25 @@
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/widgets/appbar/collapsing_list_view.dart';
class ServicesPage extends StatelessWidget {
const ServicesPage({super.key});
@override
Widget build(BuildContext context) {
return CollapsingListView(
title: "Explore Services".needTranslation,
isLeading: false,
child: Padding(
padding: EdgeInsets.all(24.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Medical & Care Services".needTranslation.toText18(isBold: true)
],
),
),
);
}
}

@ -436,6 +436,7 @@ class _LandingPageState extends State<LandingPage> {
horizontalOffset: 100.0,
child: FadeInAnimation(
child: SmallServiceCard(
serviceName: LandingPageData.getNotLoggedInServiceCardsList[index].serviceName,
icon: LandingPageData.getNotLoggedInServiceCardsList[index].icon,
title: LandingPageData.getNotLoggedInServiceCardsList[index].title,
subtitle: LandingPageData.getNotLoggedInServiceCardsList[index].subtitle,

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/book_appointment_page.dart';
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/widgets/bottom_navigation/bottom_navigation.dart';
@ -30,7 +31,7 @@ class _LandingNavigationState extends State<LandingNavigation> {
appState.isAuthenticated ? MedicalFilePage() : /* need add feedback page */ const LandingPage(),
BookAppointmentPage(),
const LandingPage(),
appState.isAuthenticated ? /* need add news page */ LandingPage() : const LandingPage(),
appState.isAuthenticated ? /* need add news page */ ServicesPage() : const LandingPage(),
],
),
bottomNavigationBar: BottomNavigation(

@ -3,6 +3,7 @@ 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/presentation/appointments/my_doctors_page.dart';
import 'package:hmg_patient_app_new/presentation/emergency_services/emergency_services_page.dart';
import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_orders_page.dart';
import 'package:hmg_patient_app_new/presentation/medical_file/patient_sickleaves_list_page.dart';
@ -104,6 +105,13 @@ class SmallServiceCard extends StatelessWidget {
),
);
break;
case "emergency":
Navigator.of(context).push(
CustomPageRoute(
page: EmergencyServicesPage(),
),
);
break;
default:
// Handle unknown service
break;

@ -111,8 +111,6 @@ class InsuranceApprovalDetailsPage extends StatelessWidget {
),
SizedBox(height: 16.h),
Container(
width: double.infinity,
height: 200.h,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
@ -124,6 +122,33 @@ class InsuranceApprovalDetailsPage extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Approval Details".toText16(isBold: true),
SizedBox(height: 16.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
LocaleKeys.procedureName.tr(context: context).toText14(isBold: true),
Expanded(
child: insuranceApprovalResponseModel.apporvalDetails!.procedureName!
.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor, textAlignment: TextAlign.end, maxLine: 2),
),
],
),
SizedBox(height: 8.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"Status:".needTranslation.toText14(isBold: true),
insuranceApprovalResponseModel.apporvalDetails!.status!.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor),
],
),
SizedBox(height: 8.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"${LocaleKeys.usageStatus.tr(context: context)}: ".toText14(isBold: true),
insuranceApprovalResponseModel.apporvalDetails!.isInvoicedDesc!.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor),
],
),
],
),
),

@ -13,7 +13,7 @@ import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart
import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_approval_response_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/insurance/widgets/insurance_approval_card.dart';
import 'package:hmg_patient_app_new/presentation/insurance/widgets/insurance_approval_details_page.dart';
import 'package:hmg_patient_app_new/presentation/insurance/insurance_approval_details_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/routes/custom_page_route.dart';

@ -120,7 +120,7 @@ class InsuranceApprovalCard extends StatelessWidget {
Transform.flip(
flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon_small, width: 15.h, height: 15.h, fit: BoxFit.contain, iconColor: AppColors.textColor)),
],
),
).toShimmer2(isShow: isLoading),
],
),
),

@ -43,6 +43,7 @@ class PatientInsuranceCard extends StatelessWidget {
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -51,20 +52,14 @@ class PatientInsuranceCard extends StatelessWidget {
"Policy: ${insuranceCardDetailsModel.insurancePolicyNo}".toText12(isBold: true, color: AppColors.lightGrayColor),
],
),
CustomButton(
AppCustomChipWidget(
icon: isInsuranceExpired ? AppAssets.cancel_circle_icon : AppAssets.insurance_active_icon,
labelText: isInsuranceExpired ? "Insurance Expired".needTranslation : "Insurance Active".needTranslation,
iconColor: isInsuranceExpired ? AppColors.primaryRedColor : AppColors.successColor,
iconSize: 13.h,
text: isInsuranceExpired ? "Insurance Expired".needTranslation : "Insurance Active".needTranslation,
onPressed: () {},
backgroundColor: isInsuranceExpired ? AppColors.primaryRedColor.withOpacity(0.15) : AppColors.successColor.withOpacity(0.15),
borderColor: isInsuranceExpired ? AppColors.primaryRedColor.withOpacity(0.01) : AppColors.successColor.withOpacity(0.01),
textColor: isInsuranceExpired ? AppColors.primaryRedColor : AppColors.successColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
iconSize: 12,
backgroundColor: isInsuranceExpired ? AppColors.primaryRedColor.withOpacity(0.1) : AppColors.successColor.withOpacity(0.1),
labelPadding: EdgeInsetsDirectional.only(start: -4.h, end: 8.h),
),
],
),
@ -79,8 +74,10 @@ class PatientInsuranceCard extends StatelessWidget {
children: [
AppCustomChipWidget(
icon: AppAssets.doctor_calendar_icon,
labelText: "${LocaleKeys.expiryDate.tr(context: context)} ${DateUtil.formatDateToDate(DateUtil.convertStringToDate(insuranceCardDetailsModel.cardValidTo), false)}"),
AppCustomChipWidget(labelText: "Patient Card ID: ${insuranceCardDetailsModel.patientCardID}"),
labelText: "${LocaleKeys.expiryDate.tr(context: context)} ${DateUtil.formatDateToDate(DateUtil.convertStringToDate(insuranceCardDetailsModel.cardValidTo), false)}",
labelPadding: EdgeInsetsDirectional.only(start: -4.h, end: 8.h),
),
AppCustomChipWidget(labelText: "Patient Card ID: ${insuranceCardDetailsModel.patientCardID}".needTranslation),
],
),
SizedBox(height: 10.h),

File diff suppressed because one or more lines are too long

@ -2,68 +2,143 @@ import 'package:easy_localization/easy_localization.dart'
show tr, StringTranslateExtension;
import 'package:flutter/material.dart';
import 'package:flutter_widget_from_html/flutter_widget_from_html.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/lab/lab_view_model.dart';
import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_result_via_clinic/LabResultList.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_result_via_clinic/lab_order_specialResult.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:open_filex/open_filex.dart';
import 'package:provider/provider.dart';
class LabResultByClinic extends StatelessWidget {
LabResultByClinic({required this.labOrder, Key? key});
late LabViewModel labViewModel;
PatientLabOrdersResponseModel labOrder;
@override
Widget build(BuildContext context) {
return CollapsingListView(
title: LocaleKeys.labResults.tr(),
child: SingleChildScrollView(
child: Column(
spacing: 8.h,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Selector<LabViewModel, bool>(
selector: (_, model) => model.isLabResultByHospitalLoading,
builder: (_, isLoading, __) {
if (isLoading) {
return Column(
children: [
LabResultItemView(
onTap: () {},
labOrder: null,
index: 0,
isLoading: true,
),
LabResultItemView(
onTap: () {},
labOrder: null,
index: 0,
isLoading: true,
),
LabResultItemView(
onTap: () {},
labOrder: null,
index: 0,
isLoading: true,
),
LabResultItemView(
onTap: () {},
labOrder: null,
index: 0,
isLoading: true,
),
],
);
} else {
return LabResultList();
labViewModel = Provider.of<LabViewModel>(context, listen: false);
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: Column(
children: [
Expanded(
child: CollapsingListView(
title: LocaleKeys.labResults.tr(),
child: SingleChildScrollView(
child: Column(
spacing: 8.h,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Selector<LabViewModel, bool>(
selector: (_, model) => model.isLabResultByHospitalLoading,
builder: (_, isLoading, __) {
if (isLoading) {
return Column(
children: [
LabResultItemView(
onTap: () {},
labOrder: null,
index: 0,
isLoading: true,
),
LabResultItemView(
onTap: () {},
labOrder: null,
index: 0,
isLoading: true,
),
LabResultItemView(
onTap: () {},
labOrder: null,
index: 0,
isLoading: true,
),
LabResultItemView(
onTap: () {},
labOrder: null,
index: 0,
isLoading: true,
),
],
);
} else {
return LabResultList();
}
},
),
LabOrderSpecialResult()
],
).paddingAll(24.h),
)),
),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: true,
),
child: CustomButton(
text: "Download report".needTranslation,
onPressed: () async {
LoaderBottomSheet.showLoader(loadingText: "Generating report, Please wait...".needTranslation);
await labViewModel
.getLabResultReportPDF(
labOrder: labOrder,
onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: err),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
})
.then((val) async {
LoaderBottomSheet.hideLoader();
if (labViewModel.patientLabResultReportPDFBase64.isNotEmpty) {
String path = await Utils.createFileFromString(labViewModel.patientLabResultReportPDFBase64, "pdf");
try {
OpenFilex.open(path);
} catch (ex) {
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: "Cannot open file".needTranslation),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}
}
},
),
LabOrderSpecialResult()
],
).paddingAll(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: 45.h,
icon: AppAssets.download,
iconColor: AppColors.whiteColor,
iconSize: 20.h,
).paddingSymmetrical(24.h, 24.h),
),
],
),
);
}
}

@ -26,6 +26,7 @@ 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/appointments/my_doctors_page.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/book_appointment_page.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/doctor_profile_page.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/appointment_calendar.dart';
import 'package:hmg_patient_app_new/presentation/insurance/insurance_approvals_page.dart';
import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.dart';
@ -200,9 +201,21 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
),
AppCustomChipWidget(
icon: AppAssets.blood_icon,
labelText: "${LocaleKeys.bloodType.tr(context: context)}: ${appState.getUserBloodGroup}",
labelText: "Blood: ${appState.getUserBloodGroup}",
iconColor: AppColors.primaryRedColor,
labelPadding: EdgeInsetsDirectional.only(start: -4.h, end: 8.h),
),
Consumer<InsuranceViewModel>(builder: (context, insuranceVM, child) {
return AppCustomChipWidget(
icon: insuranceVM.isInsuranceExpired ? AppAssets.cancel_circle_icon : AppAssets.insurance_active_icon,
labelText: insuranceVM.isInsuranceExpired ? "Insurance Expired".needTranslation : "Insurance Active".needTranslation,
iconColor: insuranceVM.isInsuranceExpired ? AppColors.primaryRedColor : AppColors.successColor,
textColor: insuranceVM.isInsuranceExpired ? AppColors.primaryRedColor : AppColors.successColor,
iconSize: 12,
backgroundColor: insuranceVM.isInsuranceExpired ? AppColors.primaryRedColor.withOpacity(0.1) : AppColors.successColor.withOpacity(0.1),
labelPadding: EdgeInsetsDirectional.only(start: -4.h, end: 8.h),
);
}),
],
),
],
@ -515,7 +528,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 12.3,
fontSize: 12,
fontWeight: FontWeight.w500,
borderRadius: 12.h,
height: 40.h,
@ -532,7 +545,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 13,
fontSize: 12,
fontWeight: FontWeight.w500,
borderRadius: 12.h,
height: 40.h,
@ -569,7 +582,6 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 10.h),
],
).onPress(() {
myAppointmentsViewModel.setIsPatientMyDoctorsLoading(true);
myAppointmentsViewModel.getPatientMyDoctors();
Navigator.of(context).push(
CustomPageRoute(
@ -639,7 +651,31 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
),
],
),
),
).onPress(() async {
bookAppointmentsViewModel.setSelectedDoctor(DoctorsListResponseModel(
clinicID: myAppointmentsVM.patientMyDoctorsList[index].clinicID,
projectID: myAppointmentsVM.patientMyDoctorsList[index].projectID,
doctorID: myAppointmentsVM.patientMyDoctorsList[index].doctorID,
));
LoaderBottomSheet.showLoader();
await bookAppointmentsViewModel.getDoctorProfile(onSuccess: (dynamic respData) {
LoaderBottomSheet.hideLoader();
Navigator.of(context).push(
CustomPageRoute(
page: DoctorProfilePage(),
),
);
}, onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: err),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
});
}),
),
),
)

@ -77,6 +77,7 @@ class PatientSickLeaveCard extends StatelessWidget {
AppCustomChipWidget(
icon: AppAssets.doctor_calendar_icon,
labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientSickLeavesResponseModel.appointmentDate), false),
labelPadding: EdgeInsetsDirectional.only(start: -4.h, end: 8.h),
).toShimmer2(isShow: isLoading),
AppCustomChipWidget(labelText: isLoading ? "Pending Activation" : patientSickLeavesResponseModel.clinicName!).toShimmer2(isShow: isLoading),
],

@ -20,8 +20,11 @@ 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: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';
class PrescriptionDetailPage extends StatefulWidget {
PrescriptionDetailPage({super.key, required this.prescriptionsResponseModel});
@ -59,6 +62,33 @@ class _PrescriptionDetailPageState extends State<PrescriptionDetailPage> {
Expanded(
child: CollapsingListView(
title: LocaleKeys.prescriptions.tr(context: context),
instructions: () async {
LoaderBottomSheet.showLoader(loadingText: "Fetching prescription PDF, Please wait...".needTranslation);
await prescriptionsViewModel.getPrescriptionInstructionsPDF(widget.prescriptionsResponseModel, onSuccess: (val) {
LoaderBottomSheet.hideLoader();
if (prescriptionsViewModel.prescriptionInstructionsPDFLink.isNotEmpty) {
Uri uri = Uri.parse(prescriptionsViewModel.prescriptionInstructionsPDFLink);
launchUrl(uri, mode: LaunchMode.platformDefault, webOnlyWindowName: "");
} else {
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: "Unable to fetch PDF".needTranslation),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}
}, onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: err),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
});
},
child: SingleChildScrollView(
child: Consumer<PrescriptionsViewModel>(builder: (context, prescriptionVM, child) {
return Column(
@ -98,6 +128,7 @@ class _PrescriptionDetailPageState extends State<PrescriptionDetailPage> {
AppCustomChipWidget(
icon: AppAssets.doctor_calendar_icon,
labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(widget.prescriptionsResponseModel.appointmentDate), false),
labelPadding: EdgeInsetsDirectional.only(start: -4.h, end: 8.h),
),
AppCustomChipWidget(
labelText: widget.prescriptionsResponseModel.clinicDescription!,
@ -112,6 +143,41 @@ class _PrescriptionDetailPageState extends State<PrescriptionDetailPage> {
),
],
),
SizedBox(height: 16.h),
CustomButton(
text: "Download Prescription".needTranslation,
onPressed: () async {
LoaderBottomSheet.showLoader();
await prescriptionVM.getPrescriptionPDFBase64(widget.prescriptionsResponseModel).then((val) async {
LoaderBottomSheet.hideLoader();
if (prescriptionVM.prescriptionPDFBase64Data.isNotEmpty) {
String path = await Utils.createFileFromString(prescriptionVM.prescriptionPDFBase64Data, "pdf");
try {
OpenFilex.open(path);
} catch (ex) {
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: "Cannot open file".needTranslation),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}
}
});
},
backgroundColor: AppColors.successColor.withValues(alpha: 0.15),
borderColor: AppColors.successColor.withValues(alpha: 0.01),
textColor: AppColors.successColor,
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
icon: AppAssets.download,
iconColor: AppColors.successColor,
iconSize: 14.h,
),
],
),
),

@ -209,21 +209,7 @@ class PrescriptionItemView extends StatelessWidget {
height: 40.h,
).toShimmer2(isShow: isLoading),
),
SizedBox(width: 16.h),
Expanded(
child: CustomButton(
text: LocaleKeys.readInstructions.tr(context: context),
onPressed: () {},
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor,
fontSize: 13,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
).toShimmer2(isShow: isLoading),
),
// SizedBox(width: 16.h),
],
).paddingSymmetrical(16.h, 16.h),
],

@ -68,7 +68,7 @@ class _RadiologyResultPageState extends State<RadiologyResultPage> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 16.h),
widget.patientRadiologyResponseModel.description!.toText16(isBold: true),
// widget.patientRadiologyResponseModel.description!.toText16(isBold: true),
SizedBox(height: 8.h),
widget.patientRadiologyResponseModel.reportData!.trim().toText12(isBold: true, color: AppColors.textColorLight),
SizedBox(height: 16.h),

@ -21,12 +21,15 @@ class CollapsingListView extends StatelessWidget {
VoidCallback? report;
VoidCallback? logout;
VoidCallback? history;
VoidCallback? instructions;
VoidCallback? requests;
Widget? bottomChild;
Widget? trailing;
bool isClose;
bool isLeading;
CollapsingListView({required this.title, this.child, this.search, this.isClose = false, this.bottomChild, this.report, this.logout, this.history, this.isLeading = true, this.trailing});
CollapsingListView(
{required this.title, this.child, this.search, this.isClose = false, this.bottomChild, this.report, this.logout, this.history, this.instructions, this.requests, this.isLeading = true, this.trailing});
@override
Widget build(BuildContext context) {
@ -98,6 +101,8 @@ class CollapsingListView extends StatelessWidget {
if (logout != null) actionButton(context, t, title: "Logout".needTranslation, icon: AppAssets.logout).onPress(logout!),
if (report != null) actionButton(context, t, title: "Report".needTranslation, icon: AppAssets.report_icon).onPress(report!),
if (history != null) actionButton(context, t, title: "History".needTranslation, icon: AppAssets.insurance_history_icon).onPress(history!),
if (instructions != null) actionButton(context, t, title: "Instructions".needTranslation, icon: AppAssets.requests).onPress(instructions!),
if (requests != null) actionButton(context, t, title: "Requests".needTranslation, icon: AppAssets.insurance_history_icon).onPress(requests!),
if (search != null) Utils.buildSvgWithAssets(icon: AppAssets.search_icon).onPress(search!).paddingOnly(right: 24),
if (trailing != null) trailing!,
],

@ -58,7 +58,7 @@ class AppCustomChipWidget extends StatelessWidget {
color: Colors.transparent, // Crucially, set color to transparent
style: BorderStyle.none,
),
borderRadius: BorderRadius.circular(8.0), // Apply a border radius of 16.0
borderRadius: BorderRadius.circular(8.h), // Apply a border radius of 16.0
),
),
child: icon.isNotEmpty

Loading…
Cancel
Save