Merge branch 'master' into faiz_dev

# Conflicts:
#	lib/core/utils/utils.dart
#	lib/presentation/book_appointment/laser/widgets/body_part_listing.dart
#	lib/widgets/buttons/custom_button.dart
#	lib/widgets/common_bottom_sheet.dart
#	lib/widgets/input_widget.dart
pull/86/head
faizatflutter 3 weeks ago
commit 21be3a2d06

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1,3 @@
<svg width="14" height="15" viewBox="0 0 14 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.99996 1.22925C3.53667 1.22925 0.729126 4.0368 0.729126 7.50008C0.729126 10.9634 3.53667 13.7709 6.99996 13.7709C10.4632 13.7709 13.2708 10.9634 13.2708 7.50008C13.2708 4.0368 10.4632 1.22925 6.99996 1.22925ZM9.74577 5.57923C9.97358 5.35142 9.97358 4.98207 9.74577 4.75427C9.51797 4.52646 9.14862 4.52646 8.92081 4.75427L7 6.67509L5.95409 5.62925C5.72628 5.40146 5.35693 5.40147 5.12913 5.62928C4.90133 5.8571 4.90135 6.22644 5.12916 6.45424L6.17504 7.50004L6.00415 7.67094C5.77634 7.89874 5.77634 8.26809 6.00415 8.49589C6.23195 8.7237 6.6013 8.7237 6.8291 8.49589L7.00002 8.32497L7.17076 8.49569C7.39857 8.72349 7.76792 8.72348 7.99571 8.49567C8.22351 8.26785 8.2235 7.89851 7.99569 7.67071L7.82498 7.50002L9.74577 5.57923Z" fill="#2E3039"/>
</svg>

After

Width:  |  Height:  |  Size: 902 B

@ -735,6 +735,8 @@ class ApiConsts {
static String baseUrl = 'https://hmgwebservices.com/'; // HIS API URL PROD static String baseUrl = 'https://hmgwebservices.com/'; // HIS API URL PROD
static String RCBaseUrl = 'https://rc.hmg.com/'; // RC API URL PROD
static String SELECT_DEVICE_IMEI = 'Services/Patients.svc/REST/Patient_SELECTDeviceIMEIbyIMEI'; static String SELECT_DEVICE_IMEI = 'Services/Patients.svc/REST/Patient_SELECTDeviceIMEIbyIMEI';
static num VERSION_ID = 18.9; static num VERSION_ID = 18.9;
@ -764,6 +766,7 @@ class ApiConsts {
TAMARA_URL = "https://mdlaboratories.com/tamaralive/Home/Checkout"; TAMARA_URL = "https://mdlaboratories.com/tamaralive/Home/Checkout";
GET_TAMARA_INSTALLMENTS_URL = "https://mdlaboratories.com/tamaralive/Home/GetInstallments"; GET_TAMARA_INSTALLMENTS_URL = "https://mdlaboratories.com/tamaralive/Home/GetInstallments";
GET_TAMARA_PAYMENT_STATUS = 'https://mdlaboratories.com/tamaralive/api/OnlineTamara/order_status?orderid='; GET_TAMARA_PAYMENT_STATUS = 'https://mdlaboratories.com/tamaralive/api/OnlineTamara/order_status?orderid=';
RCBaseUrl = 'https://rc.hmg.com/';
break; break;
case AppEnvironmentTypeEnum.dev: case AppEnvironmentTypeEnum.dev:
baseUrl = "https://uat.hmgwebservices.com/"; baseUrl = "https://uat.hmgwebservices.com/";
@ -773,6 +776,7 @@ class ApiConsts {
TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout"; TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout";
GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments"; GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments";
GET_TAMARA_PAYMENT_STATUS = 'https://epharmacy.hmg.com/tamara/api/OnlineTamara/order_status?orderid='; GET_TAMARA_PAYMENT_STATUS = 'https://epharmacy.hmg.com/tamara/api/OnlineTamara/order_status?orderid=';
RCBaseUrl = 'https://rc.hmg.com/test/';
break; break;
case AppEnvironmentTypeEnum.uat: case AppEnvironmentTypeEnum.uat:
baseUrl = "https://uat.hmgwebservices.com/"; baseUrl = "https://uat.hmgwebservices.com/";
@ -782,6 +786,7 @@ class ApiConsts {
TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout"; TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout";
GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments"; GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments";
GET_TAMARA_PAYMENT_STATUS = 'https://epharmacy.hmg.com/tamara/api/OnlineTamara/order_status?orderid='; GET_TAMARA_PAYMENT_STATUS = 'https://epharmacy.hmg.com/tamara/api/OnlineTamara/order_status?orderid=';
RCBaseUrl = 'https://rc.hmg.com/test/';
break; break;
case AppEnvironmentTypeEnum.preProd: case AppEnvironmentTypeEnum.preProd:
@ -792,6 +797,7 @@ class ApiConsts {
TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout"; TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout";
GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments"; GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments";
GET_TAMARA_PAYMENT_STATUS = 'https://epharmacy.hmg.com/tamara/api/OnlineTamara/order_status?orderid='; GET_TAMARA_PAYMENT_STATUS = 'https://epharmacy.hmg.com/tamara/api/OnlineTamara/order_status?orderid=';
RCBaseUrl = 'https://rc.hmg.com/';
break; break;
case AppEnvironmentTypeEnum.qa: case AppEnvironmentTypeEnum.qa:
baseUrl = "https://uat.hmgwebservices.com/"; baseUrl = "https://uat.hmgwebservices.com/";
@ -801,6 +807,7 @@ class ApiConsts {
TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout"; TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout";
GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments"; GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments";
GET_TAMARA_PAYMENT_STATUS = 'https://epharmacy.hmg.com/tamara/api/OnlineTamara/order_status?orderid='; GET_TAMARA_PAYMENT_STATUS = 'https://epharmacy.hmg.com/tamara/api/OnlineTamara/order_status?orderid=';
RCBaseUrl = 'https://rc.hmg.com/test/';
break; break;
case AppEnvironmentTypeEnum.staging: case AppEnvironmentTypeEnum.staging:
baseUrl = "https://uat.hmgwebservices.com/"; baseUrl = "https://uat.hmgwebservices.com/";
@ -810,6 +817,7 @@ class ApiConsts {
TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout"; TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout";
GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments"; GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments";
GET_TAMARA_PAYMENT_STATUS = 'https://epharmacy.hmg.com/tamara/api/OnlineTamara/order_status?orderid='; GET_TAMARA_PAYMENT_STATUS = 'https://epharmacy.hmg.com/tamara/api/OnlineTamara/order_status?orderid=';
RCBaseUrl = 'https://rc.hmg.com/test/';
break; break;
} }
} }
@ -829,18 +837,17 @@ class ApiConsts {
static final String insertPatientDeviceIMEIData = 'Services/Patients.svc/REST/Patient_INSERTDeviceIMEI'; static final String insertPatientDeviceIMEIData = 'Services/Patients.svc/REST/Patient_INSERTDeviceIMEI';
static final String insertPatientMobileData = 'Services/MobileNotifications.svc/REST/Insert_PatientMobileDeviceInfo'; static final String insertPatientMobileData = 'Services/MobileNotifications.svc/REST/Insert_PatientMobileDeviceInfo';
static final String getPatientMobileData = '/Services/Authentication.svc/REST/GetMobileLoginInfo'; static final String getPatientMobileData = '/Services/Authentication.svc/REST/GetMobileLoginInfo';
static final String getPrivileges = 'Services/Patients.svc/REST/Service_Privilege'; static final String getPrivileges = 'Services/Patients.svc/REST/Service_Privilege';
static final String registerUser = 'Services/Authentication.svc/REST/PatientRegistration'; static final String registerUser = 'Services/Authentication.svc/REST/PatientRegistration';
static final String addFamilyFile = 'Services/Patients.svc/REST/ShareFamilyFileService'; static final String addFamilyFile = 'Services/Patients.svc/REST/ShareFamilyFileService';
static final String sendFamilyFileActivation = 'Services/Authentication.svc/REST/SendActivationCodeForFamilyFile'; static final String sendFamilyFileActivation = 'Services/Authentication.svc/REST/SendActivationCodeForFamilyFile';
static final String checkActivationCodeForFamily = 'Services/Authentication.svc/REST/CheckActivationCodeForFamilyFile'; static final String checkActivationCodeForFamily = 'Services/Authentication.svc/REST/CheckActivationCodeForFamilyFile';
static final String getAllPendingRecordsByResponseId = 'Services/Authentication.svc/REST/GetAllPendingRecordsByResponseId'; static final String getAllPendingRecordsByResponseId = 'Services/Authentication.svc/REST/GetAllPendingRecordsByResponseId';
static final String getAllSharedRecordsByStatus = 'Services/Authentication.svc/REST/GetAllSharedRecordsByStatus'; static final String getAllSharedRecordsByStatus = 'Services/Authentication.svc/REST/GetAllSharedRecordsByStatus';
static final String removeFileFromFamilyMembers = 'Services/Authentication.svc/REST/ActiveDeactive_PatientFile'; static final String removeFileFromFamilyMembers = 'Services/Authentication.svc/REST/ActiveDeactive_PatientFile';
static final String acceptAndRejectFamilyFile = 'Services/Authentication.svc/REST/Update_FileStatus'; static final String acceptAndRejectFamilyFile = 'Services/Authentication.svc/REST/Update_FileStatus';
// static values for Api // static values for Api
static final double appVersionID = 18.7; static final double appVersionID = 18.7;

@ -158,6 +158,7 @@ class AppAssets {
static const String call_ambulance_icon = '$svgBasePath/call_ambulance_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 nearest_er_icon = '$svgBasePath/nearest_er_icon.svg';
static const String rrt_icon = '$svgBasePath/rrt_icon.svg'; static const String rrt_icon = '$svgBasePath/rrt_icon.svg';
static const String waiting_time_clock = '$svgBasePath/waiting_time_clock.svg';
//bottom navigation// //bottom navigation//
static const String homeBottom = '$svgBasePath/home_bottom.svg'; static const String homeBottom = '$svgBasePath/home_bottom.svg';
@ -202,4 +203,8 @@ class AppAnimations {
static const String noData = '$lottieBasePath/Nodata.json'; static const String noData = '$lottieBasePath/Nodata.json';
static const String ripple = '$lottieBasePath/Ripple.json'; static const String ripple = '$lottieBasePath/Ripple.json';
static const String pending_loading_animation = '$lottieBasePath/pending_loading_animation.json'; static const String pending_loading_animation = '$lottieBasePath/pending_loading_animation.json';
static const String ambulance = '$lottieBasePath/ambulance.json';
static const String ambulance_alert = '$lottieBasePath/ambulance_alert.json';
static const String rrt_ambulance = '$lottieBasePath/rrt_ambulance.json';
} }

@ -9,6 +9,8 @@ import 'package:hmg_patient_app_new/features/book_appointments/book_appointments
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/common/common_repo.dart'; import 'package:hmg_patient_app_new/features/common/common_repo.dart';
import 'package:hmg_patient_app_new/features/doctor_filter/doctor_filter_view_model.dart'; import 'package:hmg_patient_app_new/features/doctor_filter/doctor_filter_view_model.dart';
import 'package:hmg_patient_app_new/features/emergency_services/emergency_services_repo.dart';
import 'package:hmg_patient_app_new/features/emergency_services/emergency_services_view_model.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_repo.dart'; import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_repo.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart'; import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_repo.dart'; import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_repo.dart';
@ -38,6 +40,7 @@ import 'package:hmg_patient_app_new/services/localauth_service.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart'; import 'package:hmg_patient_app_new/services/logger_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_view_model.dart'; import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_view_model.dart';
import 'package:http/http.dart';
import 'package:local_auth/local_auth.dart'; import 'package:local_auth/local_auth.dart';
import 'package:logger/web.dart'; import 'package:logger/web.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
@ -99,6 +102,7 @@ class AppDependencies {
getIt.registerLazySingleton<HabibWalletRepo>(() => HabibWalletRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt())); getIt.registerLazySingleton<HabibWalletRepo>(() => HabibWalletRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<MedicalFileRepo>(() => MedicalFileRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt())); getIt.registerLazySingleton<MedicalFileRepo>(() => MedicalFileRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<ImmediateLiveCareRepo>(() => ImmediateLiveCareRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt())); getIt.registerLazySingleton<ImmediateLiveCareRepo>(() => ImmediateLiveCareRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<EmergencyServicesRepo>(() => EmergencyServicesRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
// ViewModels // ViewModels
// Global/shared VMs LazySingleton // Global/shared VMs LazySingleton
@ -165,12 +169,6 @@ class AppDependencies {
myAppointmentsViewModel: getIt(), myAppointmentsViewModel: getIt(),
), ),
); );
getIt.registerLazySingleton<AppointmentViaRegionViewmodel>(
() => AppointmentViaRegionViewmodel(
navigationService: getIt(),
appState: getIt(),
),
);
getIt.registerLazySingleton<AuthenticationViewModel>( getIt.registerLazySingleton<AuthenticationViewModel>(
() => AuthenticationViewModel( () => AuthenticationViewModel(
@ -187,7 +185,21 @@ class AppDependencies {
); );
getIt.registerLazySingleton<AppointmentViaRegionViewmodel>( getIt.registerLazySingleton<AppointmentViaRegionViewmodel>(
() => AppointmentViaRegionViewmodel(navigationService: getIt(), appState: getIt()), () =>
AppointmentViaRegionViewmodel(
navigationService: getIt(),
appState: getIt(),
),
);
getIt.registerLazySingleton<EmergencyServicesViewModel>(
() => EmergencyServicesViewModel(
locationUtils: getIt(),
navServices: getIt(),
emergencyServicesRepo: getIt(),
appState: getIt(),
errorHandlerService: getIt(),
),
); );
// Screen-specific VMs Factory // Screen-specific VMs Factory

@ -36,21 +36,60 @@ class LocationUtils {
isGMSDevice = GmsCheck().checkGmsAvailability(); isGMSDevice = GmsCheck().checkGmsAvailability();
} }
void getLocation({Function(LatLng)? onSuccess, VoidCallback? onFailure}) async { // final defaultCallbackForLocationDenied = (){
// 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 better results"
// .needTranslation,
// isShowActionButtons: true,
// onCancelTap: () {
// navigationService.pop();
// },
// onConfirmTap: () async {
// navigationService.pop();
// openAppSettings();
// }),
// callBackFunc: () {},
// isFullScreen: false,
// isCloseButtonVisible: true,
// );
// }
void getLocation(
{Function(LatLng)? onSuccess,
VoidCallback? onFailure,
bool isShowConfirmDialog = false,
VoidCallback? onLocationDeniedForever}) async {
this.isShowConfirmDialog = isShowConfirmDialog;
if (Platform.isIOS) { if (Platform.isIOS) {
getCurrentLocation(onFailure: onFailure, onSuccess: onSuccess); getCurrentLocation(
onFailure: onFailure,
onSuccess: onSuccess,
onLocationDeniedForever: onLocationDeniedForever);
return; return;
} }
if (await isGMSDevice ?? true) { if (await isGMSDevice ?? true) {
getCurrentLocation(onFailure: onFailure, onSuccess: onSuccess); getCurrentLocation(
onFailure: onFailure,
onSuccess: onSuccess,
onLocationDeniedForever: onLocationDeniedForever);
return; return;
} }
getHMSLocation(onFailure: onFailure, onSuccess: onSuccess); getHMSLocation(
onFailure: onFailure,
onSuccess: onSuccess,
onLocationDeniedForever: onLocationDeniedForever);
} }
void getCurrentLocation({Function(LatLng)? onSuccess, VoidCallback? onFailure}) async { void getCurrentLocation(
{Function(LatLng)? onSuccess,
VoidCallback? onFailure,
VoidCallback? onLocationDeniedForever}) async {
var location = Location(); var location = Location();
bool isLocationEnabled = await location.serviceEnabled(); bool isLocationEnabled = await location.serviceEnabled();
@ -73,12 +112,15 @@ class LocationUtils {
return; return;
} }
} else if (permissionGranted == LocationPermission.deniedForever) { } else if (permissionGranted == LocationPermission.deniedForever) {
if (isShowConfirmDialog) { appState.resetLocation();
if(onLocationDeniedForever == null && isShowConfirmDialog){
showCommonBottomSheetWithoutHeight( showCommonBottomSheetWithoutHeight(
title: LocaleKeys.notice.tr(context: navigationService.navigatorKey.currentContext!), title: LocaleKeys.notice.tr(context: navigationService.navigatorKey.currentContext!),
navigationService.navigatorKey.currentContext!, navigationService.navigatorKey.currentContext!,
child: Utils.getWarningWidget( child: Utils.getWarningWidget(
loadingText: "Please grant location permission from app settings to see the nearest ER location details".needTranslation, loadingText:
"Please grant location permission from app settings to see better results"
.needTranslation,
isShowActionButtons: true, isShowActionButtons: true,
onCancelTap: () { onCancelTap: () {
navigationService.pop(); navigationService.pop();
@ -92,6 +134,8 @@ class LocationUtils {
isCloseButtonVisible: true, isCloseButtonVisible: true,
); );
} }
onLocationDeniedForever?.call();
return;
} }
Position? currentLocation = await Geolocator.getLastKnownPosition(); Position? currentLocation = await Geolocator.getLastKnownPosition();
@ -209,7 +253,10 @@ class LocationUtils {
appState.userLong = locationData.longitude; appState.userLong = locationData.longitude;
} }
void getHMSLocation({VoidCallback? onFailure, Function(LatLng p1)? onSuccess}) async { void getHMSLocation(
{VoidCallback? onFailure,
Function(LatLng p1)? onSuccess,
VoidCallback? onLocationDeniedForever}) async {
try { try {
var location = Location(); var location = Location();
HmsLocation.FusedLocationProviderClient locationService = HmsLocation.FusedLocationProviderClient()..initFusedLocationService(); HmsLocation.FusedLocationProviderClient locationService = HmsLocation.FusedLocationProviderClient()..initFusedLocationService();
@ -230,7 +277,32 @@ class LocationUtils {
LocationPermission permissionGranted = await Geolocator.checkPermission(); LocationPermission permissionGranted = await Geolocator.checkPermission();
if (permissionGranted == LocationPermission.denied) { if (permissionGranted == LocationPermission.denied) {
permissionGranted = await Geolocator.requestPermission(); permissionGranted = await Geolocator.requestPermission();
if (permissionGranted != LocationPermission.whileInUse && permissionGranted != LocationPermission.always) { if (permissionGranted == LocationPermission.deniedForever) {
appState.resetLocation();
if(onLocationDeniedForever == null && 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 better results"
.needTranslation,
isShowActionButtons: true,
onCancelTap: () {
navigationService.pop();
},
onConfirmTap: () async {
navigationService.pop();
openAppSettings();
}),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}
onLocationDeniedForever?.call();
return;
} else if (permissionGranted != LocationPermission.whileInUse && permissionGranted != LocationPermission.always) {
appState.resetLocation(); appState.resetLocation();
onFailure?.call(); onFailure?.call();
return; return;

@ -0,0 +1,19 @@
import 'dart:async';
import 'dart:ui';
class Debouncer {
final int milliseconds;
VoidCallback? action;
Timer? _timer;
Debouncer({required this.milliseconds});
void run(VoidCallback action) {
_timer?.cancel();
_timer = Timer(Duration(milliseconds: milliseconds), action);
}
void dispose() {
_timer?.cancel();
}
}

@ -670,13 +670,17 @@ class Utils {
} }
/// Widget to build an SVG from network /// Widget to build an SVG from network
static Widget buildImgWithNetwork( static Widget buildImgWithNetwork({required String url, required Color iconColor, bool isDisabled = false, double width = 24, double height = 24, BoxFit fit = BoxFit.cover, ImageErrorWidgetBuilder? errorBuilder}) {
{required String url, required Color iconColor, bool isDisabled = false, double width = 24, double height = 24, BoxFit fit = BoxFit.cover}) {
return Image.network( return Image.network(
url, url,
width: width, width: width,
height: height, height: height,
fit: fit, fit: fit,
errorBuilder: errorBuilder??(_,__,___){
//todo change the error builder icon that it is returning
return Utils.buildSvgWithAssets(width: width,
height: height,icon: AppAssets.no_visit_icon);
},
); );
} }

@ -734,7 +734,7 @@ class BookAppointmentsRepoImp implements BookAppointmentsRepo {
body: mapDevice, body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) { onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType; failure = failureType;
onError!(error); onError?.call(error);
}, },
onSuccess: (response, statusCode, {messageStatus, errorMessage}) { onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try { try {

@ -1009,7 +1009,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
(failure) async => await errorHandlerService.handleError(failure: failure), (failure) async => await errorHandlerService.handleError(failure: failure),
(apiResponse) { (apiResponse) {
if (apiResponse.messageStatus == 2) { if (apiResponse.messageStatus == 2) {
onError!(apiResponse.errorMessage!); onError?.call(apiResponse.errorMessage!);
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) { } else if (apiResponse.messageStatus == 1) {
dentalChiefComplaintsList = apiResponse.data!; dentalChiefComplaintsList = apiResponse.data!;

@ -0,0 +1,94 @@
import 'package:dartz/dartz.dart';
import 'package:hmg_patient_app_new/core/api/api_client.dart';
import 'package:hmg_patient_app_new/core/api_consts.dart';
import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart';
import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
import 'package:hmg_patient_app_new/features/emergency_services/model/resp_model/ProjectAvgERWaitingTime.dart';
import 'package:hmg_patient_app_new/features/emergency_services/models/resp_models/rrt_procedures_response_model.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
abstract class EmergencyServicesRepo {
Future<Either<Failure, GenericApiModel<List<RRTProceduresResponseModel>>>> getRRTProcedures();
Future<Either<Failure, GenericApiModel<List<ProjectAvgERWaitingTime>>>> getNearestEr({int? id, int? projectID});
}
class EmergencyServicesRepoImp implements EmergencyServicesRepo {
final ApiClient apiClient;
final LoggerService loggerService;
EmergencyServicesRepoImp({required this.loggerService, required this.apiClient});
Future<Either<Failure, GenericApiModel<List<ProjectAvgERWaitingTime>>>> getNearestEr({int? id, int? projectID}) async {
Map<String, dynamic> mapDevice = {'IsForER': true};
try {
GenericApiModel<List<ProjectAvgERWaitingTime>>? apiResponse;
Failure? failure;
await apiClient.post(
GET_NEAREST_HOSPITAL,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response['List_ProjectAvgERWaitingTime'];
final clinicsList = list.map((item) => ProjectAvgERWaitingTime.fromJson(item as Map<String, dynamic>)).toList().cast<ProjectAvgERWaitingTime>();
apiResponse = GenericApiModel<List<ProjectAvgERWaitingTime>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: clinicsList,
);
} 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<List<RRTProceduresResponseModel>>>> getRRTProcedures() async {
Map<String, dynamic> mapDevice = {};
try {
GenericApiModel<List<RRTProceduresResponseModel>>? apiResponse;
Failure? failure;
await apiClient.post(
GET_RRT_PROCEDURE_LIST,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response['Vida_ProcedureList'];
final proceduresList = list.map((item) => RRTProceduresResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<RRTProceduresResponseModel>();
apiResponse = GenericApiModel<List<RRTProceduresResponseModel>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: proceduresList,
);
} 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()));
}
}
}

@ -0,0 +1,137 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/location_util.dart';
import 'package:hmg_patient_app_new/features/emergency_services/emergency_services_repo.dart';
import 'package:hmg_patient_app_new/features/emergency_services/model/resp_model/ProjectAvgERWaitingTime.dart';
import 'package:hmg_patient_app_new/features/emergency_services/models/resp_models/rrt_procedures_response_model.dart';
import 'package:hmg_patient_app_new/presentation/emergency_services/nearest_er_page.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:url_launcher/url_launcher.dart';
class EmergencyServicesViewModel extends ChangeNotifier {
EmergencyServicesRepo emergencyServicesRepo;
ErrorHandlerService errorHandlerService;
final NavigationService navServices;
final LocationUtils? locationUtils;
final AppState appState;
bool isERListLoading = false;
List<ProjectAvgERWaitingTime> nearestERList = [];
List<ProjectAvgERWaitingTime> nearestERFilteredList = [];
List<RRTProceduresResponseModel> RRTProceduresList = [];
late RRTProceduresResponseModel selectedRRTProcedure;
setSelectedRRTProcedure(RRTProceduresResponseModel procedure) {
selectedRRTProcedure = procedure;
notifyListeners();
}
EmergencyServicesViewModel({
required this.emergencyServicesRepo,
required this.errorHandlerService,
required this.navServices,
required this.locationUtils,
required this.appState,
});
Future<void> getRRTProcedures({Function(dynamic)? onSuccess, Function(String)? onError}) async {
RRTProceduresList.clear();
notifyListeners();
final result = await emergencyServicesRepo.getRRTProcedures();
result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure),
(apiResponse) {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
RRTProceduresList = apiResponse.data!;
selectedRRTProcedure = RRTProceduresList.first;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
void navigateToNearestERPage() {
locationUtils!.getLocation(
isShowConfirmDialog: true,
onSuccess: (position) {
navServices.push(
CustomPageRoute(
page: NearestErPage(),
),
);
getNearestER();
});
}
void filterErList(String query) {
print("the query is $query");
if (query.isEmpty) {
nearestERFilteredList = nearestERList;
} else {
nearestERFilteredList = nearestERList.where((er) => er.projectName != null && er.projectName!.toLowerCase().contains(query.toLowerCase())).toList();
}
notifyListeners();
}
// Open directions (navigation) from current location to destination.
Future<void> openDirections({
required double destLat,
required double destLng,
String? travelMode, // driving, walking, bicycling, transit
}) async {
// Try Google Maps app navigation intent (android/iOS)
final modeParam = travelMode == null ? 'driving' : travelMode;
final googleNavUri = Uri.parse('google.navigation:q=$destLat,$destLng&mode=${modeParam.substring(0, 1)}'); // mode: d/w/b/t by scheme
final universalUrl = Uri.parse('https://www.google.com/maps/dir/?api=1&destination=$destLat,$destLng&travelmode=$modeParam');
if (await canLaunchUrl(googleNavUri)) {
await launchUrl(googleNavUri, mode: LaunchMode.externalApplication);
} else {
await launchUrl(universalUrl, mode: LaunchMode.externalApplication);
}
}
Future<void> openDialer(String phoneNumber) async {
final Uri telUri = Uri(scheme: 'tel', path: phoneNumber);
if (await canLaunchUrl(telUri)) {
await launchUrl(telUri, mode: LaunchMode.externalApplication);
} else {
throw 'Could not open dialer for $phoneNumber';
}
}
FutureOr<void> getNearestER() async {
isERListLoading = true;
nearestERList = [];
nearestERFilteredList = [];
notifyListeners();
var response = await emergencyServicesRepo.getNearestEr();
isERListLoading = false;
notifyListeners();
response.fold(
(failure) async {},
(apiResponse) {
isERListLoading = false;
if (apiResponse.messageStatus == 1) {
nearestERList = apiResponse.data!;
nearestERFilteredList = nearestERList;
}
},
);
}
}

@ -0,0 +1,114 @@
class ProjectAvgERWaitingTime {
int? iD;
int? projectID;
int? avgTimeInMinutes;
String? avgTimeInHHMM;
dynamic distanceInKilometers;
String? latitude;
String? longitude;
String? phonenumber;
String? projectImageURL;
String? projectName;
ProjectAvgERWaitingTime(
{this.iD,
this.projectID,
this.avgTimeInMinutes,
this.avgTimeInHHMM,
this.distanceInKilometers,
this.latitude,
this.longitude,
this.phonenumber,
this.projectImageURL,
this.projectName});
ProjectAvgERWaitingTime.fromJson(Map<String, dynamic> json) {
iD = json['ID'];
projectID = json['ProjectID'];
avgTimeInMinutes = json['AvgTimeInMinutes'];
avgTimeInHHMM = json['AvgTimeInHHMM'];
distanceInKilometers = json['DistanceInKilometers'];
latitude = json['Latitude'];
longitude = json['Longitude'];
phonenumber = json['PhoneNumber'];
projectImageURL = json['ProjectImageURL'];
projectName = json['ProjectName'];
}
String getTime(){
print("the name is $projectName");
print("the avgTimeInMinutes is $avgTimeInMinutes");
if(avgTimeInMinutes == null) return "";
int hours = avgTimeInMinutes! ~/ 60;
int minutes = avgTimeInMinutes! % 60;
print("the time is ${"${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}"}");
return "${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}";
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['ID'] = this.iD;
data['ProjectID'] = this.projectID;
data['AvgTimeInMinutes'] = this.avgTimeInMinutes;
data['AvgTimeInHHMM'] = this.avgTimeInHHMM;
data['DistanceInKilometers'] = this.distanceInKilometers;
data['Latitude'] = this.latitude;
data['Longitude'] = this.longitude;
data['PhoneNumber'] = this.phonenumber;
data['ProjectImageURL'] = this.projectImageURL;
data['ProjectName'] = this.projectName;
return data;
}
}
//class ProjectAvgERWaitingTime {
// int? iD;
// int? projectID;
// int? avgTimeInMinutes;
// String? avgTimeInHHMM;
// String? distanceInKilometers;
// String? latitude;
// String? longitude;
// String? phonenum?ber;
// String? projectImageURL;
// String? projectName;
//
// ProjectAvgERWaitingTime(
// {this.iD,
// this.projectID,
// this.avgTimeInMinutes,
// this.avgTimeInHHMM,
// this.distanceInKilometers,
// this.latitude,
// this.longitude,
// this.phonenum?ber,
// this.projectImageURL,
// this.projectName});
//
// ProjectAvgERWaitingTime.fromJson(Map<String, dynamic> json) {
// iD = json['ID'];
// projectID = json['ProjectID'];
// avgTimeInMinutes = json['AvgTimeInMinutes'];
// avgTimeInHHMM = json['AvgTimeInHHMM'];
// distanceInKilometers = json['DistanceInKilometers'];
// latitude = json['Latitude'];
// longitude = json['Longitude'];
// phonenum?ber = json['Phonenum?ber'];
// projectImageURL = json['ProjectImageURL'];
// projectName = json['ProjectName'];
// }
//
// Map<String, dynamic> toJson() {
// final Map<String, dynamic> data = new Map<String, dynamic>();
// data['ID'] = this.iD;
// data['ProjectID'] = this.projectID;
// data['AvgTimeInMinutes'] = this.avgTimeInMinutes;
// data['AvgTimeInHHMM'] = this.avgTimeInHHMM;
// data['DistanceInKilometers'] = this.distanceInKilometers;
// data['Latitude'] = this.latitude;
// data['Longitude'] = this.longitude;
// data['Phonenum?ber'] = this.phonenum?ber;
// data['ProjectImageURL'] = this.projectImageURL;
// data['ProjectName'] = this.projectName;
// return data;
// }
//}

@ -0,0 +1,27 @@
class RRTProceduresResponseModel {
num? patientShare;
num? patientShareWithTax;
num? patientTaxAmount;
String? procedureID;
String? procedureName;
RRTProceduresResponseModel({this.patientShare, this.patientShareWithTax, this.patientTaxAmount, this.procedureID, this.procedureName});
RRTProceduresResponseModel.fromJson(Map<String, dynamic> json) {
patientShare = json['PatientShare'];
patientShareWithTax = json['PatientShareWithTax'];
patientTaxAmount = json['PatientTaxAmount'];
procedureID = json['ProcedureID'];
procedureName = json['ProcedureName'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['PatientShare'] = this.patientShare;
data['PatientShareWithTax'] = this.patientShareWithTax;
data['PatientTaxAmount'] = this.patientTaxAmount;
data['ProcedureID'] = this.procedureID;
data['ProcedureName'] = this.procedureName;
return data;
}
}

@ -110,11 +110,8 @@ class AppointmentViaRegionViewmodel extends ChangeNotifier {
); );
} }
} }
if (clinicId == 253) { if (clinicId == 253) {
{ {
navigationService.push( navigationService.push(
CustomPageRoute( CustomPageRoute(
page: LaserAppointment(), page: LaserAppointment(),

@ -11,6 +11,7 @@ import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart'; import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/doctor_filter/doctor_filter_view_model.dart'; import 'package:hmg_patient_app_new/features/doctor_filter/doctor_filter_view_model.dart';
import 'package:hmg_patient_app_new/features/emergency_services/emergency_services_view_model.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart'; import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_view_model.dart'; import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_view_model.dart';
import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart'; import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart';
@ -125,6 +126,9 @@ void main() async {
), ),
ChangeNotifierProvider<DoctorFilterViewModel>( ChangeNotifierProvider<DoctorFilterViewModel>(
create: (_) => getIt.get<DoctorFilterViewModel>(), create: (_) => getIt.get<DoctorFilterViewModel>(),
),
ChangeNotifierProvider<EmergencyServicesViewModel>(
create: (_) => getIt.get<EmergencyServicesViewModel>(),
) )
], child: MyApp()), ], child: MyApp()),
), ),

@ -43,85 +43,88 @@ class BodyPartsListing extends StatelessWidget {
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
return SizedBox( height: 80, width: 80,).toShimmer2(isShow: isLoading); return SizedBox( height: 80, width: 80,).toShimmer2(isShow: isLoading);
})), })),
GridView.builder( Visibility(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( visible: !isLoading,
crossAxisCount: 3, child: GridView.builder(
childAspectRatio: 85 / 107, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisSpacing: 4.h, crossAxisCount: 3,
mainAxisSpacing: 21.h), childAspectRatio: 85 / 107,
physics: NeverScrollableScrollPhysics(), crossAxisSpacing: 4.h,
shrinkWrap: true, mainAxisSpacing: 21.h),
itemCount: parts.length, physics: NeverScrollableScrollPhysics(),
// padding: fullBody != null ? EdgeInsets.only(top: 16) : EdgeInsets.zero, shrinkWrap: true,
padding: EdgeInsets.zero, itemCount: parts.length,
itemBuilder: (BuildContext context, int index) { // padding: fullBody != null ? EdgeInsets.only(top: 16) : EdgeInsets.zero,
bool _isSelected = padding: EdgeInsets.zero,
selectedPart.any((file) => file.id == parts[index].id); itemBuilder: (BuildContext context, int index) {
return InkWell( bool _isSelected =
onTap: () { selectedPart.any((file) => file.id == parts[index].id);
onPartSelected(parts[index]); return InkWell(
}, onTap: () {
child: AnimatedOpacity( onPartSelected(parts[index]);
opacity: 1.0, },
duration: Duration(milliseconds: 200), child: AnimatedOpacity(
child: Column( opacity: 1.0,
crossAxisAlignment: CrossAxisAlignment.start, duration: Duration(milliseconds: 200),
children: [ child: Column(
AspectRatio( crossAxisAlignment: CrossAxisAlignment.start,
aspectRatio: 97 / 97, children: [
child: FittedBox( AspectRatio(
fit: BoxFit.fitWidth, aspectRatio: 97 / 97,
child: Stack( child: FittedBox(
alignment: Alignment.topRight, fit: BoxFit.fitWidth,
children: [ child: Stack(
Container( alignment: Alignment.topRight,
margin: EdgeInsets.only(top: 5.h, right: 5.h), children: [
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15.0),
border: Border.all(
color: _isSelected
? AppColors.primaryRedColor
: AppColors.borderGrayColor,
width: 2.h),
),
child: LaserBodyParts().getCategoryImage(isMale,
laserCategoryID, parts[index].mappingCode!),
),
if (_isSelected)
Container( Container(
width: 18.h, margin: EdgeInsets.only(top: 5.h, right: 5.h),
height: 18.h,
child: Icon(Icons.done,
color: Colors.white, size: 12.h),
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppColors.primaryRedColor, borderRadius: BorderRadius.circular(15.0),
borderRadius: BorderRadius.circular(30.h), border: Border.all(
color: _isSelected
? AppColors.primaryRedColor
: AppColors.borderGrayColor,
width: 2.h),
), ),
child: LaserBodyParts().getCategoryImage(isMale,
laserCategoryID, parts[index].mappingCode!),
), ),
], if (_isSelected)
Container(
width: 18.h,
height: 18.h,
child: Icon(Icons.done,
color: Colors.white, size: 12.h),
decoration: BoxDecoration(
color: AppColors.primaryRedColor,
borderRadius: BorderRadius.circular(30.h),
),
),
],
),
), ),
), ),
), SizedBox(height: 6.h),
SizedBox(height: 6.h), Expanded(
Expanded( child: Text(
child: Text( context
context .read<BookAppointmentsViewModel>()
.read<BookAppointmentsViewModel>() .getLaserProcedureNameWRTLanguage(parts[index]),
.getLaserProcedureNameWRTLanguage(parts[index]), style: TextStyle(
style: TextStyle( fontSize: 12.f,
fontSize: 12.f, fontWeight: FontWeight.w600,
fontWeight: FontWeight.w600, color: Color(0xff2B353E),
color: Color(0xff2B353E), letterSpacing: -0.48,
letterSpacing: -0.48, ),
maxLines: 1,
), ),
maxLines: 1,
), ),
), ],
], ),
), ),
), );
); },
}, ),
), ),
], ],
); );

@ -281,7 +281,7 @@ class _SelectClinicPageState extends State<SelectClinicPage> {
}); });
} }
Widget getRegionalSelectionWidget(AppointmentViaRegionViewmodel data) { Widget getRegionalSelectionWidget(AppointmentViaRegionViewmodel data) {
if (data.bottomSheetState == AppointmentViaRegionState.REGION_SELECTION) { if (data.bottomSheetState == AppointmentViaRegionState.REGION_SELECTION) {
return RegionBottomSheetBody(); return RegionBottomSheetBody();
} }
@ -303,11 +303,9 @@ class _SelectClinicPageState extends State<SelectClinicPage> {
id = regionalViewModel.selectedHospital?.patientDoctorAppointmentList?.first.projectID?.toString() ?? ""; id = regionalViewModel.selectedHospital?.patientDoctorAppointmentList?.first.projectID?.toString() ?? "";
} }
if (bookAppointmentsViewModel.selectedClinic.clinicID == 17) { if (bookAppointmentsViewModel.selectedClinic.clinicID == 17) {
if (appState.isAuthenticated) { if (appState.isAuthenticated) {
initDentalAppointmentBookingFlow(int.parse(bookAppointmentsViewModel initDentalAppointment();
.currentlySelectedHospitalFromRegionFlow ?? return SizedBox.shrink();
"0"));
}else { }else {
bookAppointmentsViewModel.setIsChiefComplaintsListLoading(true); bookAppointmentsViewModel.setIsChiefComplaintsListLoading(true);
} }
@ -464,6 +462,7 @@ class _SelectClinicPageState extends State<SelectClinicPage> {
isCloseButtonVisible: true, isCloseButtonVisible: true,
); );
} else { } else {
bookAppointmentsViewModel.setIsChiefComplaintsListLoading(true);
Navigator.of(context).push( Navigator.of(context).push(
CustomPageRoute( CustomPageRoute(
page: DentalChiefComplaintsPage(), page: DentalChiefComplaintsPage(),
@ -472,4 +471,12 @@ class _SelectClinicPageState extends State<SelectClinicPage> {
} }
}); });
} }
void initDentalAppointment() async {
await Future.delayed(Duration(milliseconds: 300));
initDentalAppointmentBookingFlow(int.parse(bookAppointmentsViewModel
.currentlySelectedHospitalFromRegionFlow ??
"0"));
return;
}
} }

@ -0,0 +1,46 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_export.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/emergency_services/emergency_services_view_model.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/radio/custom_radio_button.dart';
import 'package:provider/provider.dart';
class RrtRequestTypeSelect extends StatelessWidget {
const RrtRequestTypeSelect({super.key});
@override
Widget build(BuildContext context) {
// return Consumer<EmergencyServicesViewModel>(builder: (context, emergencyServicesVM, child) {
return Column(
children: [
Container(
padding: EdgeInsets.all(16.h),
height: 200.h,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
CustomRadioOption(
value: "",
groupValue: "",
onChanged: (value) {},
text: "Home Visit Emergency",
)
],
),
],
),
),
SizedBox(height: 32.h),
],
);
// });
}
}

@ -1,3 +1,4 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.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/dependencies.dart';
@ -6,23 +7,31 @@ 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/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/emergency_services/emergency_services_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/emergency_services/RRT/rrt_request_type_select.dart';
import 'package:hmg_patient_app_new/presentation/emergency_services/nearest_er_page.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/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:lottie/lottie.dart';
import 'package:provider/provider.dart';
class EmergencyServicesPage extends StatelessWidget { class EmergencyServicesPage extends StatelessWidget {
EmergencyServicesPage({super.key}); EmergencyServicesPage({super.key});
late EmergencyServicesViewModel emergencyServicesViewModel;
LocationUtils? locationUtils; LocationUtils? locationUtils;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
emergencyServicesViewModel = Provider.of<EmergencyServicesViewModel>(context, listen: false);
locationUtils = getIt.get<LocationUtils>(); locationUtils = getIt.get<LocationUtils>();
locationUtils!.isShowConfirmDialog = true; locationUtils!.isShowConfirmDialog = true;
return CollapsingListView( return CollapsingListView(
title: "Emergency Services", title: "Emergency Services".needTranslation,
requests: () {}, requests: () {},
child: Padding( child: Padding(
padding: EdgeInsets.all(24.h), padding: EdgeInsets.all(24.h),
@ -79,13 +88,7 @@ class EmergencyServicesPage extends StatelessWidget {
Utils.buildSvgWithAssets(icon: AppAssets.forward_chevron_icon, width: 13.h, height: 13.h), Utils.buildSvgWithAssets(icon: AppAssets.forward_chevron_icon, width: 13.h, height: 13.h),
], ],
).onPress(() { ).onPress(() {
locationUtils!.getLocation(onSuccess: (position) { context.read<EmergencyServicesViewModel>().navigateToNearestERPage();
Navigator.of(context).push(
CustomPageRoute(
page: NearestErPage(),
),
);
});
}), }),
), ),
SizedBox(height: 16.h), SizedBox(height: 16.h),
@ -112,7 +115,73 @@ class EmergencyServicesPage extends StatelessWidget {
SizedBox(width: 12.h), SizedBox(width: 12.h),
Utils.buildSvgWithAssets(icon: AppAssets.forward_chevron_icon, width: 13.h, height: 13.h), Utils.buildSvgWithAssets(icon: AppAssets.forward_chevron_icon, width: 13.h, height: 13.h),
], ],
), ).onPress(() {
showCommonBottomSheetWithoutHeight(
context,
child: Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.primaryRedColor,
borderRadius: 24.h,
),
child: Padding(
padding: EdgeInsets.all(24.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"".toText14(),
Utils.buildSvgWithAssets(
icon: AppAssets.cancel_circle_icon,
iconColor: AppColors.whiteColor,
width: 24.h,
height: 24.h,
fit: BoxFit.contain,
).onPress(() {
Navigator.of(context).pop();
}),
],
),
Lottie.asset(AppAnimations.ambulance_alert, repeat: false, reverse: false, frameRate: FrameRate(60), width: 120.h, height: 120.h, fit: BoxFit.contain),
SizedBox(height: 8.h),
"Confirmation".needTranslation.toText28(color: AppColors.whiteColor, isBold: true),
SizedBox(height: 8.h),
"Are you sure you want to call Rapid Response Team (RRT)?".needTranslation.toText14(color: AppColors.whiteColor, weight: FontWeight.w500),
SizedBox(height: 24.h),
CustomButton(
text: LocaleKeys.confirm.tr(context: context),
onPressed: () async {
Navigator.of(context).pop();
showCommonBottomSheetWithoutHeight(
title: "Rapid Response Team (RRT)".needTranslation,
context,
child: RrtRequestTypeSelect(),
isFullScreen: false,
isCloseButtonVisible: true,
hasBottomPadding: false,
backgroundColor: AppColors.bottomSheetBgColor,
callBackFunc: () {},
);
},
backgroundColor: AppColors.whiteColor,
borderColor: AppColors.whiteColor,
textColor: AppColors.primaryRedColor,
icon: AppAssets.checkmark_icon,
iconColor: AppColors.primaryRedColor,
),
SizedBox(height: 8.h),
],
),
),
),
isFullScreen: false,
isCloseButtonVisible: false,
hasBottomPadding: false,
backgroundColor: AppColors.primaryRedColor,
callBackFunc: () {},
);
}),
), ),
], ],
), ),

@ -1,5 +1,21 @@
import 'package:easy_localization/easy_localization.dart' show tr, StringTranslateExtension;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/emergency_services/emergency_services_view_model.dart';
import 'package:hmg_patient_app_new/features/emergency_services/model/resp_model/ProjectAvgERWaitingTime.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/emergency_services/widgets/nearestERItem.dart' show NearestERItem;
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/widgets/input_widget.dart';
import 'package:provider/provider.dart' show Selector, WatchContext, ReadContext;
import '../../core/enums.dart' show SelectionTypeEnum;
import '../../core/utils/debouncer.dart' show Debouncer;
class NearestErPage extends StatefulWidget { class NearestErPage extends StatefulWidget {
const NearestErPage({super.key}); const NearestErPage({super.key});
@ -9,11 +25,80 @@ class NearestErPage extends StatefulWidget {
} }
class _NearestErPageState extends State<NearestErPage> { class _NearestErPageState extends State<NearestErPage> {
final TextEditingController searchText = TextEditingController();
final Debouncer debouncer = Debouncer(milliseconds: 500);
@override
void dispose() {
debouncer.dispose();
super.dispose();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return CollapsingListView( return CollapsingListView(
title: "Nearest ER", title: "Nearest ER".needTranslation,
child: Container(), child: SingleChildScrollView(
); child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: [
TextInputWidget(
labelText: LocaleKeys.search.tr(),
hintText: 'Type any facility name'.needTranslation,
controller: searchText,
onChange: (value) {
debouncer.run(() {
context.read<EmergencyServicesViewModel>().filterErList(value ?? '');
});
},
isEnable: true,
prefix: null,
autoFocus: false,
isBorderAllowed: false,
keyboardType: TextInputType.text,
isAllowLeadingIcon: true,
leadingIcon: AppAssets.search_icon,
padding: EdgeInsets.symmetric(
vertical: ResponsiveExtension(10).h,
horizontal: ResponsiveExtension(15).h,
),
),
Selector<EmergencyServicesViewModel, bool>(
selector: (___, viewModel) => viewModel.isERListLoading,
builder: (_, isLoading, __) => Selector<EmergencyServicesViewModel, List<ProjectAvgERWaitingTime>>(
selector: (___, viewModel) => viewModel.nearestERFilteredList,
shouldRebuild: (previous, next) => previous != next,
builder: (_, value, __) {
if (isLoading || value.isNotEmpty) {
return ListView.separated(
padding: EdgeInsets.only(top: 24.h),
itemCount: isLoading ? 6 : value.length,
separatorBuilder: (____, _____) => SizedBox(
height: 16.h,
),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
return AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: NearestERItem(
isLoading: isLoading,
nearestERItem: isLoading ? ProjectAvgERWaitingTime() : value[index],
))));
},
);
} else {
return Center(child: Utils.getNoDataWidget(context, noDataText: "No nearest Er Arround you".needTranslation));
}
}),
),
],
).paddingAll(16.h),
));
} }
} }

@ -0,0 +1,130 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_export.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/emergency_services/emergency_services_view_model.dart';
import 'package:hmg_patient_app_new/features/emergency_services/model/resp_model/ProjectAvgERWaitingTime.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:provider/provider.dart';
class NearestERItem extends StatelessWidget {
final ProjectAvgERWaitingTime nearestERItem;
final bool isLoading;
const NearestERItem({ super.key,
required this.nearestERItem,
required this.isLoading
}) ;
@override
Widget build(BuildContext context) {
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: Colors.white,
customBorder: BorderRadius.only(
topLeft: Radius.circular(24.h),
topRight: Radius.circular(24.h),
),
),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
(isLoading || nearestERItem.projectImageURL?.isEmpty == true)
? Container(
width: 24.h,
height: 24.h,
decoration: BoxDecoration(
color: Colors.grey.shade300,
shape: BoxShape.circle,
),
).toShimmer2(isShow: isLoading)
: Utils.buildImgWithNetwork(
url: nearestERItem.projectImageURL ?? '',
iconColor: Colors.transparent,
).circle(24.h).toShimmer2(isShow: isLoading),
const SizedBox(width: 12),
Expanded(
child: (nearestERItem.projectName?.toText16(
color: AppColors.textColor,
weight: FontWeight.w600,
) ??
SizedBox.shrink()).toShimmer2(isShow: isLoading),
),
// TODO: Add hospital icon logic here if needed
],
),
SizedBox(height: 8.h),
Row(
spacing: 8.h,
children: [
AppCustomChipWidget(
labelText: "${nearestERItem.distanceInKilometers} km".needTranslation,
icon: AppAssets.location,
iconHasColor: false,
labelPadding: EdgeInsetsDirectional.only(start: 4.h, end: 0.h),
padding: EdgeInsets.all(8.h),
).toShimmer2(isShow: isLoading),
AppCustomChipWidget(
labelText: "Expected waiting time: ${nearestERItem.getTime()} mins".needTranslation,
icon: AppAssets.waiting_time_clock,
iconHasColor: false,
labelPadding: EdgeInsetsDirectional.only(start: 4.h, end: 0.h),
padding: EdgeInsets.all(8.h),
).toShimmer2(isShow: isLoading),
],
),
SizedBox(height: 16.h),
Row(
children: [
Expanded(
child: CustomButton(
text: "View Location on Google Maps".needTranslation,
iconSize: 18.h,
icon: AppAssets.location,
onPressed: () {
context.read<EmergencyServicesViewModel>().openDirections(destLat: double.parse(nearestERItem.latitude??"0.0"), destLng: double.parse(nearestERItem.longitude??"0.0") );
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
iconColor: AppColors.primaryRedColor,
height: 40.h,
fontSize: 14,
fontWeight: FontWeight.w500,
).toShimmer2(isShow: isLoading),
),
SizedBox(width: 8.h),
SizedBox(
height: 40.h,
width: 40.h,
child: CustomButton(
text: '',
iconSize: 18.h,
icon: AppAssets.call_fill,
onPressed: () {
context.read<EmergencyServicesViewModel>().openDialer( nearestERItem.phonenumber??"");
},
backgroundColor: AppColors.greyColor,
iconColor: AppColors.textColor,
borderColor: AppColors.greyColor,
height: 40.h,
).toShimmer2(isShow: isLoading),
),
],
),
],
),
),
);
}
}

@ -64,15 +64,14 @@ class DialogServiceImp implements DialogService {
onOkPressed: () { onOkPressed: () {
if (onOkPressed != null) { if (onOkPressed != null) {
onOkPressed(); onOkPressed();
} else {
context.pop();
} }
context.pop(); context.pop();
}, },
onCancelPressed: () { onCancelPressed: () {
if (onCancelPressed != null) { if (onCancelPressed != null) {
context.pop(); onCancelPressed();
} }
context.pop();
}, },
), ),
); );

@ -70,19 +70,23 @@ class CustomButton extends StatelessWidget {
children: [ children: [
if (icon != null) if (icon != null)
Padding( Padding(
padding: EdgeInsets.only(right: 8.h, left: 8.h), padding: text.isNotEmpty ?EdgeInsets.only(right: 8.h, left: 8.h): EdgeInsets.zero,
child: Utils.buildSvgWithAssets(icon: icon!, iconColor: iconColor, isDisabled: isDisabled, width: iconS, height: iconS), child: Utils.buildSvgWithAssets(icon: icon!, iconColor: iconColor, isDisabled: isDisabled, width: iconSize, height: iconSize),
), ),
Padding(
padding: EdgeInsets.only(top: 0), Visibility(
child: Text( visible: text.isNotEmpty,
text, child: Padding(
overflow: textOverflow, padding: EdgeInsets.only(top: 0),
style: context.dynamicTextStyle( child: Text(
fontSize: fontS, text,
color: isDisabled ? textColor.withOpacity(0.5) : textColor, overflow: textOverflow,
letterSpacing: 0, style: context.dynamicTextStyle(
fontWeight: fontWeight, fontSize: fontSize.fSize,
color: isDisabled ? textColor.withOpacity(0.5) : textColor,
letterSpacing: 0,
fontWeight: fontWeight,
),
), ),
), ),
), ),

@ -105,15 +105,17 @@ class ButtonSheetContent extends StatelessWidget {
} }
void showCommonBottomSheetWithoutHeight( void showCommonBottomSheetWithoutHeight(
BuildContext context, { BuildContext context, {
required Widget child, required Widget child,
required VoidCallback callBackFunc, required VoidCallback callBackFunc,
String title = "", String title = "",
bool isCloseButtonVisible = true, bool isCloseButtonVisible = true,
bool isFullScreen = true, bool isFullScreen = true,
bool isDismissible = true, bool isDismissible = true,
Widget? titleWidget, Widget? titleWidget,
bool useSafeArea = false, bool useSafeArea = false,
bool hasBottomPadding = true,
Color backgroundColor = AppColors.bottomSheetBgColor,
}) { }) {
showModalBottomSheet<String>( showModalBottomSheet<String>(
sheetAnimationStyle: AnimationStyle( sheetAnimationStyle: AnimationStyle(
@ -127,13 +129,14 @@ void showCommonBottomSheetWithoutHeight(
isScrollControlled: true, isScrollControlled: true,
showDragHandle: false, showDragHandle: false,
isDismissible: isDismissible, isDismissible: isDismissible,
backgroundColor: AppColors.bottomSheetBgColor, backgroundColor: backgroundColor,
useSafeArea: useSafeArea, useSafeArea: useSafeArea,
builder: (BuildContext context) { builder: (BuildContext context) {
return SafeArea( return SafeArea(
top: false, top: false,
left: false, left: false,
right: false, right: false,
bottom: hasBottomPadding,
child: Padding( child: Padding(
padding: EdgeInsets.only( padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom, bottom: MediaQuery.of(context).viewInsets.bottom,
@ -142,40 +145,40 @@ void showCommonBottomSheetWithoutHeight(
physics: ClampingScrollPhysics(), physics: ClampingScrollPhysics(),
child: isCloseButtonVisible child: isCloseButtonVisible
? Container( ? Container(
padding: EdgeInsets.only( padding: EdgeInsets.only(
left: 24, left: 24,
top: 24, top: 24,
right: 24, right: 24,
bottom: 12, bottom: 12,
), ),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.bottomSheetBgColor, color: AppColors.bottomSheetBgColor,
borderRadius: 24.h, borderRadius: 24.h,
), ),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
titleWidget ?? titleWidget ??
Expanded( Expanded(
child: title.toText20(weight: FontWeight.w600), child: title.toText20(weight: FontWeight.w600),
), ),
Utils.buildSvgWithAssets( Utils.buildSvgWithAssets(
icon: AppAssets.cross_circle, icon: AppAssets.close_bottom_sheet_icon,
height: 24.h, iconColor: Color(0xff2B353E),
).onPress(() { ).onPress(() {
Navigator.of(context).pop(); Navigator.of(context).pop();
}), }),
], ],
), ),
SizedBox(height: 16.h), SizedBox(height: 16.h),
child, child,
], ],
), ),
) )
: child, : child,
), ),
), ),

@ -0,0 +1,49 @@
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/theme/colors.dart';
class CustomRadioOption<T> extends StatelessWidget {
final T value;
final T? groupValue;
final ValueChanged<T?> onChanged;
final String text;
// final Widget child; // The content of your radio option (e.g., Text, Image)
const CustomRadioOption({
super.key,
required this.value,
required this.groupValue,
required this.onChanged,
// required this.child,
required this.text,
});
@override
Widget build(BuildContext context) {
// bool isSelected = value == groupValue;
bool isSelected = false;
return InkWell(
onTap: () => onChanged(value),
child: Container(
padding: EdgeInsets.all(8.h),
child: Row(
children: [
Container(
width: 20.h,
height: 20.h,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: isSelected ? AppColors.primaryRedColor : AppColors.whiteColor,
border: Border.all(color: isSelected ? AppColors.primaryRedColor : AppColors.bottomNAVBorder, width: 2.h),
),
),
SizedBox(width: 8.h),
text.toText16(weight: FontWeight.w500), // The provided content
],
),
),
);
}
}
Loading…
Cancel
Save