Merge branch 'refs/heads/master' into dev_sikander

# Conflicts:
#	lib/presentation/habib_wallet/recharge_wallet_page.dart
#	lib/presentation/lab/lab_orders_page.dart
#	lib/splashPage.dart
pull/57/head
Sikander Saleem 1 month ago
commit 005157fe5e

@ -0,0 +1,4 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.8333 3.99984C15.8333 3.26346 16.4303 2.6665 17.1667 2.6665H29.1667C29.903 2.6665 30.5 3.26346 30.5 3.99984C30.5 4.73622 29.903 5.33317 29.1667 5.33317L29.1667 23.3332C29.1667 26.6469 26.4804 29.3332 23.1667 29.3332C19.853 29.3332 17.1667 26.6469 17.1667 23.3332L17.1667 9.33405C17.1667 9.33376 17.1667 9.33434 17.1667 9.33405C17.1667 9.33376 17.1667 9.33259 17.1667 9.33229V5.33317C16.4303 5.33317 15.8333 4.73622 15.8333 3.99984ZM19.8333 7.99984V5.33317H26.5L26.5 13.0676C26.1378 13.317 25.8055 13.5125 25.4733 13.6324C25.0059 13.8012 24.5708 13.8095 24.0425 13.4925C22.6945 12.6837 21.4241 12.879 20.3715 13.4052C20.1897 13.4961 20.0098 13.5996 19.8333 13.7111V10.6665H22.5C23.2364 10.6665 23.8333 10.0695 23.8333 9.33317C23.8333 8.59679 23.2364 7.99984 22.5 7.99984H19.8333Z" fill="#2E3039"/>
<path d="M7.14104 12.6116C7.52794 12.2404 8.13873 12.2404 8.52562 12.6116L8.53329 12.6193C8.69353 12.7817 9.1478 13.2422 9.40228 13.5185C9.91854 14.0791 10.6087 14.8721 11.3012 15.7994C11.9917 16.7239 12.6985 17.8007 13.2361 18.9287C13.7693 20.0478 14.1667 21.2805 14.1667 22.4998C14.1667 24.7601 13.4124 26.4319 12.1558 27.5235C10.9246 28.5931 9.33325 28.9998 7.83333 28.9998C6.33342 28.9998 4.74203 28.5931 3.51084 27.5235C2.25425 26.4319 1.5 24.7601 1.5 22.4998C1.5 21.2805 1.89732 20.0478 2.4306 18.9287C2.96813 17.8007 3.67498 16.7239 4.36544 15.7994C5.05795 14.8721 5.74813 14.0791 6.26439 13.5185C6.51888 13.2422 6.97318 12.7817 7.1334 12.6193L7.14104 12.6116Z" fill="#2E3039"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 83 KiB

@ -847,5 +847,8 @@
"pleaseSelectAMaritalStatus": "يرجى اختيار الحالة الاجتماعية",
"pleaseSelectACountry": "يرجى اختيار الدولة",
"pleaseEnterEmail": "يرجى إدخال البريد الإلكتروني",
"pleaseEnterAValidEmailFormat": "يرجى إدخال تنسيق بريد إلكتروني صالح"
"pleaseEnterAValidEmailFormat": "يرجى إدخال تنسيق بريد إلكتروني صالح",
"selectCountry": "اختر الدولة",
"forLoginVerification": "للتحقق من تسجيل الدخول"
}

@ -803,7 +803,6 @@
"loginByOTP": "Login By OTP",
"guest": "Guest",
"switchAccount": "Switch Account",
"lastloginBy": "Last login by",
"allSet": "All Set! Now you can login with Face ID or Biometric",
"enableQuickLogin": "Enable Quick Login",
"enableMsg": "Enabling the quick login will verify through your existing device Face ID / Biometric",
@ -843,6 +842,8 @@
"pleaseSelectAMaritalStatus": "Please select a marital status",
"pleaseSelectACountry": "Please select a country",
"pleaseEnterEmail": "Please enter email",
"pleaseEnterAValidEmailFormat": "Please enter a valid email format"
"pleaseEnterAValidEmailFormat": "Please enter a valid email format",
"selectCountry": "Select Country",
"forLoginVerification": "for login verification",
"lastLoginBy": "Last login by"
}

@ -177,7 +177,7 @@ class ApiClientImp implements ApiClient {
}
// body['TokenID'] = "@dm!n";
// body['PatientID'] = "4767477";
// body['PatientID'] = 3628599;
}
body.removeWhere((key, value) => value == null);

@ -131,6 +131,8 @@ class AppAssets {
static const String smart_phone_fill = '$svgBasePath/smart_phone_fill.svg';
static const String touch_face_id = '$svgBasePath/touch_face_id.svg';
static const String minus = '$svgBasePath/minus.svg';
static const String home_lab_result_icon = '$svgBasePath/home_lab_result_icon.svg';
static const String visa_mastercard_icon = '$svgBasePath/visa_mastercard.svg';
//bottom navigation//
static const String homeBottom = '$svgBasePath/home_bottom.svg';

@ -44,6 +44,7 @@ class AppState {
setIsAuthenticated = true;
_authenticatedRootUser = authenticatedUser;
}
}
AuthenticatedUser? getAuthenticatedUser({bool isFamily = false}) {
@ -97,7 +98,7 @@ class AppState {
set setDeviceTypeID(v) => deviceTypeID = v;
List<VidaPlusProjectListModel> vidaPlusProjectList = [];
List<PrivilegeModel> privilegeModelList = [];
List<ListPrivilege> privilegeModelList = [];
List<HMCProjectListModel> hMCProjectListModel = [];
List<ProjectDetailListModel> projectDetailListModel = [];
@ -105,7 +106,7 @@ class AppState {
vidaPlusProjectList = vidaPlusProjectListModelInput;
}
setPrivilegeModelList(List<PrivilegeModel> privilegeModelListInput) {
setPrivilegeModelList(List<ListPrivilege> privilegeModelListInput) {
privilegeModelList = privilegeModelListInput;
}
@ -133,5 +134,10 @@ class AppState {
_userRegistrationPayload = value;
}
///this will be called if there is any problem in getting the user location
void resetLocation(){
userLong = 0.0;
userLong = 0.0;
}
}

@ -158,7 +158,8 @@ class AppDependencies {
bookAppointmentsRepo: getIt(),
errorHandlerService: getIt(),
navigationService: getIt(),
myAppointmentsViewModel: getIt()
myAppointmentsViewModel: getIt(),
locationUtils: getIt()
),
);

@ -1,12 +1,23 @@
import 'dart:io';
import 'dart:ui';
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/services/navigation_service.dart';
import 'package:permission_handler/permission_handler.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;
class LocationUtils {
NavigationService navigationService;
@ -16,6 +27,7 @@ class LocationUtils {
bool isShowLocationTimeoutDialog;
bool isHuawei;
final GeolocatorPlatform _geolocatorPlatform = GeolocatorPlatform.instance;
Future<bool?>? isGMSDevice;
LocationUtils({
required this.isShowConfirmDialog,
@ -23,49 +35,62 @@ class LocationUtils {
required this.appState,
this.isHuawei = false,
this.isShowLocationTimeoutDialog = true,
});
}) {
isGMSDevice = GmsCheck().checkGmsAvailability();
}
void getCurrentLocation({Function(LatLng)? callBack}) async {
Geolocator.isLocationServiceEnabled().then((value) async {
if (value) {
await Geolocator.checkPermission().then((permission) async {
if (permission == LocationPermission.always || permission == LocationPermission.whileInUse) {
Geolocator.getLastKnownPosition().then((value) {
setLocation(value);
if (callBack != null) callBack(LatLng(value?.latitude ?? 24.7101433, value?.longitude ?? 46.6757709));
}).catchError((err) {
if (isShowConfirmDialog && isShowLocationTimeoutDialog) {}
});
void getLocation(
{Function(LatLng)? onSuccess, VoidCallback? onFailure}) async {
if (Platform.isIOS) {
getCurrentLocation(onFailure: onFailure, onSuccess: onSuccess);
return;
}
if (permission == LocationPermission.denied || permission == LocationPermission.deniedForever) {
if (Platform.isAndroid) {
// Utils.showPermissionConsentDialog(context, TranslationBase.of(context).locationPermissionDialog, () async {
final hasPermission = await _handlePermission();
if (hasPermission) {
// Geolocator.getCurrentPosition(locationSettings: LocationSettings(accuracy: LocationAccuracy.medium, timeLimit: Duration(seconds: 5))).then((value) {
Geolocator.getLastKnownPosition().then((value) {
setLocation(value);
if (callBack != null) callBack(LatLng(value?.latitude ?? 24.7101433, value?.longitude ?? 46.6757709));
});
} else {
// if (isShowConfirmDialog) showErrorLocationDialog(false, failureCallBack: () {});
if (await isGMSDevice ?? true) {
getCurrentLocation(onFailure: onFailure, onSuccess: onSuccess);
return;
}
// });
} else {
if (await Permission.location.request().isGranted) {
getCurrentLocation(callBack: callBack);
} else {
setZeroLocation();
if (isShowConfirmDialog) showErrorLocationDialog(false, failureCallBack: () {});
getHMSLocation(onFailure: onFailure, onSuccess: onSuccess);
}
void getCurrentLocation(
{Function(LatLng)? onSuccess, VoidCallback? onFailure}) async {
var location = Location();
bool isLocationEnabled = await location.serviceEnabled();
//if the location service is not enabled, ask the user to enable it
if (!isLocationEnabled) {
isLocationEnabled = await location.requestService();
if (!isLocationEnabled) {
appState.resetLocation();
onFailure?.call();
return;
}
}
}).catchError((err) {});
} else {
if (isShowConfirmDialog) showErrorLocationDialog(false, failureCallBack: () {});
LocationPermission permissionGranted = await Geolocator.checkPermission();
if (permissionGranted == LocationPermission.denied) {
permissionGranted = await Geolocator.requestPermission();
if (permissionGranted != LocationPermission.whileInUse &&
permissionGranted != LocationPermission.always) {
appState.resetLocation();
onFailure?.call();
return;
}
}
}).catchError((err) {});
Position? currentLocation = await Geolocator.getLastKnownPosition();
if(currentLocation?.latitude == null || currentLocation?.longitude == null){
currentLocation = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.low);
}
LatLng locationData = LatLng(
currentLocation?.latitude ?? 0.0, currentLocation?.longitude ?? 0.0);
saveLatLngToAppState(locationData);
onSuccess?.call(locationData);
}
Future<bool> checkIfGPSIsEnabled() async {
@ -166,4 +191,75 @@ class LocationUtils {
].request();
return (result[Permission.location]!.isGranted || result[Permission.locationAlways]!.isGranted);
}
void saveLatLngToAppState(LatLng locationData) {
appState.userLat = locationData.latitude;
appState.userLong = locationData.longitude;
}
void getHMSLocation(
{VoidCallback? onFailure, Function(LatLng p1)? onSuccess}) async {
try {
var location = Location();
HmsLocation.FusedLocationProviderClient locationService =
HmsLocation.FusedLocationProviderClient()..initFusedLocationService();
bool isLocationEnabled = await Geolocator.isLocationServiceEnabled();
//if the location service is not enabled, ask the user to enable it
if (!isLocationEnabled) {
HmsLocation.LocationRequest locationRequest =
HmsLocation.LocationRequest()
..priority = HmsLocation.LocationRequest.PRIORITY_HIGH_ACCURACY;
HmsLocation.LocationSettingsRequest request =
HmsLocation.LocationSettingsRequest(
alwaysShow: true,
needBle: false,
requests: [locationRequest],
);
await locationService.checkLocationSettings(request);
}
LocationPermission permissionGranted = await Geolocator.checkPermission();
if (permissionGranted == LocationPermission.denied) {
permissionGranted = await Geolocator.requestPermission();
if (permissionGranted != LocationPermission.whileInUse &&
permissionGranted != LocationPermission.always) {
appState.resetLocation();
onFailure?.call();
return;
}
}
HmsLocation.Location data = await locationService.getLastLocation();
if (data.latitude == null || data.longitude == null) {
appState.resetLocation();
HmsLocation.LocationRequest request = HmsLocation.LocationRequest()
..priority = HmsLocation.LocationRequest.PRIORITY_HIGH_ACCURACY
..interval = 1000 // 1 second
..numUpdates = 1;
locationService.requestLocationUpdates(request);
locationService.onLocationData?.listen((location) {
data = location;
if (data.latitude == null || data.longitude == null) {
appState.resetLocation();
onFailure?.call();
return;
}
var locationData =
LatLng(data.latitude ?? 0.0, data.longitude ?? 0.0);
saveLatLngToAppState(locationData);
onSuccess?.call(locationData);
});
} else {
var locationData = LatLng(data.latitude ?? 0.0, data.longitude ?? 0.0);
saveLatLngToAppState(locationData);
onSuccess?.call(locationData);
}
} catch (e) {
appState.resetLocation();
onFailure?.call();
}
}
}

@ -346,9 +346,9 @@ class Utils {
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Lottie.asset(AppAnimations.warningAnimation, repeat: true, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill),
Lottie.asset(AppAnimations.warningAnimation, repeat: true, reverse: false, frameRate: FrameRate(60), width: 128.h, height: 128.h, fit: BoxFit.fill),
SizedBox(height: 8.h),
(loadingText ?? LocaleKeys.loadingText.tr()).toText14(color: AppColors.blackColor),
(loadingText ?? LocaleKeys.loadingText.tr()).toText14(color: AppColors.blackColor, letterSpacing: 0),
SizedBox(height: 16.h),
isShowActionButtons
? Row(
@ -361,11 +361,11 @@ class Utils {
onCancelTap();
}
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor,
icon: AppAssets.cancel,
iconColor: AppColors.primaryRedColor,
iconColor: AppColors.whiteColor,
),
),
SizedBox(width: 8.h),
@ -650,18 +650,19 @@ class Utils {
);
}
static Widget getPaymentAmountWithSymbol2(num habibWalletAmount, Color iconColor, double iconSize, {bool isSaudiCurrency = true, bool isExpanded = true}) {
static Widget getPaymentAmountWithSymbol2(num habibWalletAmount, {double iconSize = 14, Color iconColor = AppColors.textColor, Color textColor = AppColors.blackColor, bool isSaudiCurrency = true, bool isExpanded = true}) {
return RichText(
maxLines: 1,
text: TextSpan(
children: [
WidgetSpan(
alignment: PlaceholderAlignment.baseline,
baseline: TextBaseline.alphabetic,
child: Utils.buildSvgWithAssets(icon: AppAssets.saudi_riyal_icon, width: 14.h, height: 14.h, iconColor: Colors.black.withValues(alpha: 0.31)),
child: Utils.buildSvgWithAssets(icon: AppAssets.saudi_riyal_icon, width: iconSize.h, height: iconSize.h, iconColor: iconColor),
),
TextSpan(
text: " $habibWalletAmount",
style: TextStyle(color: AppColors.blackColor, fontSize: 32.fSize, letterSpacing: -4, fontWeight: FontWeight.w600, height: 1),
style: TextStyle(color: textColor, fontSize: 32.fSize, letterSpacing: -4, fontWeight: FontWeight.w600, height: 1),
),
],
),
@ -707,4 +708,16 @@ class Utils {
await file.writeAsBytes(bytes);
return file.path;
}
static bool havePrivilege(int id) {
bool isHavePrivilege = false;
try {
for (var element in appState.privilegeModelList) {
if (element.id == id) isHavePrivilege = element.previlege!;
}
} catch (e) {
print(e);
}
return isHavePrivilege;
}
}

@ -30,12 +30,11 @@ extension EmailValidator on String {
fontStyle: fontStyle ?? FontStyle.normal,
fontWeight: isBold ? FontWeight.bold : FontWeight.normal,
color: color ?? AppColors.blackColor,
letterSpacing: -1,
letterSpacing: 0,
),
);
Widget toText10({Color? color, FontWeight? weight, bool isBold = false, bool isUnderLine = false, int? maxlines, FontStyle? fontStyle, TextOverflow? textOverflow, double letterSpacing = -1}) =>
Text(
Widget toText10({Color? color, FontWeight? weight, bool isBold = false, bool isUnderLine = false, int? maxlines, FontStyle? fontStyle, TextOverflow? textOverflow, double letterSpacing = 0}) => Text(
this,
maxLines: maxlines,
overflow: textOverflow,
@ -49,7 +48,7 @@ extension EmailValidator on String {
decorationColor: color ?? AppColors.blackColor),
);
Widget toText11({Color? color, FontWeight? weight, bool isUnderLine = false, bool isCenter = false, bool isBold = false, int maxLine = 0, double letterSpacing = -1}) => Text(
Widget toText11({Color? color, FontWeight? weight, bool isUnderLine = false, bool isCenter = false, bool isBold = false, int maxLine = 0, double letterSpacing = 0}) => Text(
this,
textAlign: isCenter ? TextAlign.center : null,
maxLines: (maxLine > 0) ? maxLine : null,
@ -71,7 +70,7 @@ extension EmailValidator on String {
fontSize: 12.fSize,
fontWeight: fontWeight ?? (isBold ? FontWeight.bold : FontWeight.normal),
color: color ?? AppColors.blackColor,
letterSpacing: -1,
letterSpacing: 0,
height: height,
decorationColor: isUnderLine ? AppColors.blackColor : null,
decoration: isUnderLine ? TextDecoration.underline : null,
@ -87,7 +86,7 @@ extension EmailValidator on String {
fontSize: 12.fSize,
fontWeight: isBold ? FontWeight.bold : FontWeight.normal,
color: color ?? AppColors.blackColor,
letterSpacing: -1,
letterSpacing: 0,
decoration: isUnderLine ? TextDecoration.underline : null,
),
);
@ -120,7 +119,7 @@ extension EmailValidator on String {
),
);
Widget toText13({Color? color, bool isUnderLine = false, bool isBold = false, bool isCenter = false, int maxLine = 0, FontWeight? weight, double? letterSpacing = -1}) => Text(
Widget toText13({Color? color, bool isUnderLine = false, bool isBold = false, bool isCenter = false, int maxLine = 0, FontWeight? weight, double? letterSpacing = 0}) => Text(
this,
textAlign: isCenter ? TextAlign.center : null,
maxLines: (maxLine > 0) ? maxLine : null,
@ -139,7 +138,7 @@ extension EmailValidator on String {
bool isCenter = false,
FontWeight? weight,
int? maxlines,
double? letterSpacing = -1,
double? letterSpacing = 0,
double? height,
TextOverflow? textOverflow}) =>
Text(

@ -150,13 +150,14 @@ extension SmoothContainerExtension on ShapeBorder {
bool isDisabled = false,
Color? backgroundColor,
BorderSide? side,
BorderRadius? customBorder,
bool hasShadow = false,
}) {
final bgColor = backgroundColor ?? color;
return ShapeDecoration(
color: isDisabled ? bgColor.withOpacity(0.5) : bgColor,
shape: SmoothRectangleBorder(
borderRadius: BorderRadius.circular(borderRadius ?? 0),
borderRadius: customBorder ?? BorderRadius.circular(borderRadius ?? 0),
smoothness: 1,
side: side ?? BorderSide.none,
),

@ -23,6 +23,7 @@ import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/features/authentication/authentication_repo.dart';
import 'package:hmg_patient_app_new/features/authentication/models/request_models/check_activation_code_register_request_model.dart';
import 'package:hmg_patient_app_new/features/authentication/models/request_models/registration_payload_model.dart';
import 'package:hmg_patient_app_new/features/authentication/models/resp_models/authenticated_user_resp_model.dart';
import 'package:hmg_patient_app_new/features/authentication/models/resp_models/check_activation_code_resp_model.dart';
import 'package:hmg_patient_app_new/features/authentication/models/resp_models/check_user_staus_nhic_response_model.dart';
import 'package:hmg_patient_app_new/features/authentication/models/resp_models/select_device_by_imei.dart';
@ -59,8 +60,7 @@ class AuthenticationViewModel extends ChangeNotifier {
required NavigationService navigationService,
required CacheService cacheService,
required LocalAuthService localAuthService,
})
: _navigationService = navigationService,
}) : _navigationService = navigationService,
_dialogService = dialogService,
_errorHandlerService = errorHandlerService,
_appState = appState,
@ -301,8 +301,7 @@ class AuthenticationViewModel extends ChangeNotifier {
final result = await _authenticationRepo.checkPatientAuthentication(checkPatientAuthenticationReq: checkPatientAuthenticationReq);
result.fold(
(failure) async =>
await _errorHandlerService.handleError(
(failure) async => await _errorHandlerService.handleError(
failure: failure,
onUnHandledFailure: (failure) async {
LoaderBottomSheet.hideLoader();
@ -445,8 +444,7 @@ class AuthenticationViewModel extends ChangeNotifier {
LoaderBottomSheet.hideLoader();
resultEither.fold(
(failure) async =>
await _errorHandlerService.handleError(
(failure) async => await _errorHandlerService.handleError(
failure: failure,
onUnHandledFailure: (failure) async {
LoaderBottomSheet.hideLoader();
@ -454,9 +452,6 @@ class AuthenticationViewModel extends ChangeNotifier {
message: failure.message,
label: LocaleKeys.notice.tr(),
onOkPressed: () {
_navigationService.pushAndReplace(AppRoutes.register);
},
onCancelPressed: () {
_navigationService.pop();
});
}), (apiResponse) {
@ -473,8 +468,7 @@ class AuthenticationViewModel extends ChangeNotifier {
final resultEither = await _authenticationRepo.checkActivationCodeRepo(newRequest: CheckActivationCodeRegisterReq.fromJson(request), activationCode: activationCode, isRegister: false);
resultEither.fold(
(failure) async =>
await _errorHandlerService.handleError(
(failure) async => await _errorHandlerService.handleError(
failure: failure,
onUnHandledFailure: (failure) async {
LoaderBottomSheet.hideLoader();
@ -504,6 +498,7 @@ class AuthenticationViewModel extends ChangeNotifier {
} else {
if (activation.list != null && activation.list!.isNotEmpty) {
_appState.setAuthenticatedUser(activation.list!.first);
_appState.setPrivilegeModelList(activation.list!.first.listPrivilege!);
}
_appState.setUserBloodGroup = (activation.patientBlodType ?? "");
_appState.setAppAuthToken = activation.authenticationTokenId;
@ -885,9 +880,13 @@ class AuthenticationViewModel extends ChangeNotifier {
message: apiResponse.errorMessage ?? "",
label: LocaleKeys.notice.tr(),
onOkPressed: () {
_dialogService.showPhoneNumberPickerSheet(label:"Where would you like to receive OTP?", message:"Please select from the below options to receive OTP.", onSMSPress: () {
_dialogService.showPhoneNumberPickerSheet(
label: "Where would you like to receive OTP?",
message: "Please select from the below options to receive OTP.",
onSMSPress: () {
checkUserAuthentication(otpTypeEnum: OTPTypeEnum.sms);
}, onWhatsappPress: () {
},
onWhatsappPress: () {
checkUserAuthentication(otpTypeEnum: OTPTypeEnum.whatsapp);
});
},
@ -908,7 +907,7 @@ class AuthenticationViewModel extends ChangeNotifier {
Future<void> getServicePrivilege() async {
final resultEither = await _authenticationRepo.getServicePrivilege();
List<PrivilegeModel> privilegeModelList = [];
List<ListPrivilege> privilegeModelList = [];
List<VidaPlusProjectListModel> vidaPlusProjectListModel = [];
List<HMCProjectListModel> hMCProjectListModel = [];
List<ProjectDetailListModel> projectDetailListModel = [];
@ -920,9 +919,9 @@ class AuthenticationViewModel extends ChangeNotifier {
await _dialogService.showErrorBottomSheet(message: apiResponse.errorMessage ?? "ErrorEmpty");
} else {
apiResponse.data["ServicePrivilegeList"].forEach((v) {
privilegeModelList.add(PrivilegeModel.fromJson(v));
privilegeModelList.add(ListPrivilege.fromJson(v));
});
_appState.setPrivilegeModelList(privilegeModelList);
_appState.setPrivilegeModelList(privilegeModelList.cast<ListPrivilege>());
apiResponse.data["ProjectListVidaPlus"].forEach((v) {
vidaPlusProjectListModel.add(VidaPlusProjectListModel.fromJson(v));

@ -613,35 +613,3 @@ class ListElement {
"status": listStatus,
};
}
class ListPrivilege {
int? id;
String? serviceName;
bool? previlege;
dynamic region;
ListPrivilege({
this.id,
this.serviceName,
this.previlege,
this.region,
});
factory ListPrivilege.fromRawJson(String str) => ListPrivilege.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory ListPrivilege.fromJson(Map<String, dynamic> json) => ListPrivilege(
id: json["ID"],
serviceName: json["ServiceName"],
previlege: json["Previlege"],
region: json["Region"],
);
Map<String, dynamic> toJson() => {
"ID": id,
"ServiceName": serviceName,
"Previlege": previlege,
"Region": region,
};
}

@ -533,6 +533,7 @@ class _OTPVerificationScreenState extends State<OTPVerificationScreen> {
@override
Widget build(BuildContext context) {
AuthenticationViewModel authVM = context.read<AuthenticationViewModel>();
AppState appState = getIt<AppState>();
return Scaffold(
backgroundColor: AppColors.scaffoldBgColor,
appBar: CustomAppBar(
@ -552,14 +553,16 @@ class _OTPVerificationScreenState extends State<OTPVerificationScreen> {
LocaleKeys.otpVerification.tr().toText24(isBold: true),
SizedBox(height: 20.h),
Wrap(
spacing: 4.h,
runSpacing: 8.0,
spacing: 2.h,
runSpacing: 2.h,
children: [
LocaleKeys.weHaveSendOTP.tr().toText16(color: AppColors.inputLabelTextColor),
_getMaskedPhoneNumber().toText16(color: AppColors.inputLabelTextColor, isBold: true),
LocaleKeys.via.tr().toText16(color: AppColors.inputLabelTextColor),
authVM.loginTypeEnum.displayName.toText16(color: AppColors.inputLabelTextColor),
LocaleKeys.forRegistrationVerification.tr().toText16(color: AppColors.inputLabelTextColor),
LocaleKeys.weHaveSendOTP.tr().toText15(color: AppColors.inputLabelTextColor, letterSpacing: -0.4),
_getMaskedPhoneNumber().toText15(color: AppColors.inputLabelTextColor, isBold: true),
LocaleKeys.via.tr().toText15(color: AppColors.inputLabelTextColor, letterSpacing: -0.4),
authVM.loginTypeEnum.displayName.toText15(color: AppColors.inputLabelTextColor, isBold: true, letterSpacing: -0.4),
appState.getUserRegistrationPayload.isRegister != null && appState.getUserRegistrationPayload.isRegister == true
? LocaleKeys.forRegistrationVerification.tr().toText15(color: AppColors.inputLabelTextColor, letterSpacing: -0.4)
: LocaleKeys.forLoginVerification.tr().toText15(color: AppColors.inputLabelTextColor, letterSpacing: -0.4),
],
),

@ -42,11 +42,9 @@ abstract class BookAppointmentsRepo {
Function(dynamic)? onSuccess,
Function(String)? onError});
Future<Either<Failure, GenericApiModel<List<HospitalsModel>>>>
getProjectList();
Future<Either<Failure, GenericApiModel<List<HospitalsModel>>>> getProjectList();
Future<Either<Failure, GenericApiModel<List<GetClinicsListResponseModel>>>> getClinicsWithRespectToClinicId(String projectID);
}
class BookAppointmentsRepoImp implements BookAppointmentsRepo {
@ -371,8 +369,7 @@ class BookAppointmentsRepoImp implements BookAppointmentsRepo {
}
@override
Future<Either<Failure, GenericApiModel<List<HospitalsModel>>>>
getProjectList() async {
Future<Either<Failure, GenericApiModel<List<HospitalsModel>>>> getProjectList() async {
Map<String, dynamic> request = {};
try {
@ -388,11 +385,7 @@ class BookAppointmentsRepoImp implements BookAppointmentsRepo {
try {
final list = response['ListProject'];
final appointmentsList = list
.map((item) =>
HospitalsModel.fromJson(item as Map<String, dynamic>))
.toList()
.cast<HospitalsModel>();
final appointmentsList = list.map((item) => HospitalsModel.fromJson(item as Map<String, dynamic>)).toList().cast<HospitalsModel>();
apiResponse = GenericApiModel<List<HospitalsModel>>(
messageStatus: messageStatus,

@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/cache_consts.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/location_util.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/doctor_response_mapper.dart';
import 'package:hmg_patient_app_new/core/utils/loading_utils.dart';
@ -24,6 +25,7 @@ import 'package:hmg_patient_app_new/presentation/home/navigation_screen.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/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
import 'package:location/location.dart' show Location;
@ -37,6 +39,8 @@ class BookAppointmentsViewModel extends ChangeNotifier {
int initialSlotDuration = 0;
LocationUtils locationUtils;
List<GetClinicsListResponseModel> clinicsList = [];
List<GetClinicsListResponseModel> _filteredClinicsList = [];
@ -74,7 +78,9 @@ class BookAppointmentsViewModel extends ChangeNotifier {
bool shouldLoadSpecificClinic = false;
String? currentlySelectedHospitalFromRegionFlow;
BookAppointmentsViewModel({required this.bookAppointmentsRepo, required this.errorHandlerService, required this.navigationService, required this.myAppointmentsViewModel});
BookAppointmentsViewModel({required this.bookAppointmentsRepo, required this.errorHandlerService, required this.navigationService, required this.myAppointmentsViewModel, required this.locationUtils}) {;
initBookAppointmentViewModel();
}
void initializeFilteredList() {
_filteredClinicsList = List.from(clinicsList);
@ -97,6 +103,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
isDoctorProfileLoading = true;
clinicsList.clear();
doctorsList.clear();
// getLocation();
notifyListeners();
}
@ -378,7 +385,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
LoadingUtils.hideFullScreenLoader();
Navigator.pushAndRemoveUntil(
navigationService.navigatorKey.currentContext!,
FadePage(
CustomPageRoute(
page: LandingNavigation(),
),
(r) => false);
@ -408,10 +415,10 @@ class BookAppointmentsViewModel extends ChangeNotifier {
Future<void> getRegionMappedProjectList() async {
//todo handle the case in the location is switch on
if(hospitalList != null && hospitalList!.registeredDoctorMap != null && hospitalList!.registeredDoctorMap!.isNotEmpty){
filteredHospitalList = hospitalList;
return;
}
// if(hospitalList != null && hospitalList!.registeredDoctorMap != null && hospitalList!.registeredDoctorMap!.isNotEmpty){
// filteredHospitalList = hospitalList;
// return;
// }
isRegionListLoading = true;
notifyListeners();
final result = await bookAppointmentsRepo.getProjectList();
@ -429,10 +436,8 @@ class BookAppointmentsViewModel extends ChangeNotifier {
lat: _appState.userLat,
lng: _appState.userLong,
);
var lat = await Utils.getNumFromPrefs(CacheConst.userLat);
var lng = await Utils.getNumFromPrefs(CacheConst.userLong);
var isLocationEnabled = (lat != 0) && (lng != 0);
var isLocationEnabled = (_appState.userLat != 0) && (_appState.userLong != 0);
hospitalList =
await DoctorMapper.sortList(isLocationEnabled, hospitalList!);
@ -480,15 +485,15 @@ class BookAppointmentsViewModel extends ChangeNotifier {
notifyListeners();
}
Future<bool> isLocationEnabled() async{
return await Location().serviceEnabled();
bool isLocationEnabled() {
return _appState.userLong != 0.0 && _appState.userLong != 0.0;
}
bool getLocationStatus() {
bool isLocationAvaiable = false;
isLocationEnabled().then((value) => isLocationAvaiable = value);
return isLocationAvaiable;
}
// bool getLocationStatus() {
// bool isLocationAvaiable = false;
// isLocationEnabled().then((value) => isLocationAvaiable = value);
// return isLocationAvaiable;
// }
void setLoadSpecificClinic(bool status) {
shouldLoadSpecificClinic = status;
@ -518,4 +523,9 @@ class BookAppointmentsViewModel extends ChangeNotifier {
void resetFilterList(){
filteredHospitalList = hospitalList;
}
void getLocation(){
locationUtils.getLocation();
}
}

@ -3,10 +3,20 @@ 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/my_appointments/models/resp_models/hospital_model.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
abstract class HabibWalletRepo {
Future<Either<Failure, GenericApiModel<dynamic>>> getPatientBalanceAmount();
Future<Either<Failure, GenericApiModel<List<HospitalsModel>>>> getProjectList();
Future<Either<Failure, GenericApiModel<dynamic>>> HISCreateAdvancePayment(
{required String paymentMethodName, required num paidAmount, required String paymentReference, required String patientID, required int projectID, required String depositorName});
Future<Either<Failure, GenericApiModel<dynamic>>> addAdvanceNumberRequest({required String advanceNumber, required String paymentReference});
Future<Either<Failure, GenericApiModel<dynamic>>> getPatientInfoByPatientID({required String patientID});
}
class HabibWalletRepoImp implements HabibWalletRepo {
@ -55,4 +65,165 @@ class HabibWalletRepoImp implements HabibWalletRepo {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<List<HospitalsModel>>>> getProjectList() async {
Map<String, dynamic> request = {"IsOnlineCheckIn": true, "IsAdvancePayment": true};
try {
GenericApiModel<List<HospitalsModel>>? apiResponse;
Failure? failure;
await apiClient.post(
GET_PROJECT_LIST,
body: request,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response['ListProject'];
final appointmentsList = list.map((item) => HospitalsModel.fromJson(item as Map<String, dynamic>)).toList().cast<HospitalsModel>();
apiResponse = GenericApiModel<List<HospitalsModel>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: appointmentsList,
);
} 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>> HISCreateAdvancePayment(
{required String paymentMethodName, required num paidAmount, required String paymentReference, required String patientID, required int projectID, required String depositorName}) async {
Map<String, dynamic> request = {
"CustName": depositorName,
"CustID": patientID,
"SetupID": "010266",
"ProjectID": projectID,
"PatientID": patientID,
"AccountID": patientID,
"PaymentAmount": paidAmount,
"NationalityID": null,
"DepositorName": depositorName,
"CreatedBy": 3,
"PaymentMethodName": paymentMethodName,
"PaymentReference": paymentReference,
"PaymentMethod": paymentMethodName,
"IsAncillaryOrder": false
};
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(
HIS_CREATE_ADVANCE_PAYMENT,
body: request,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: response,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel>> addAdvanceNumberRequest({required String advanceNumber, required String paymentReference}) async {
Map<String, dynamic> requestBody = {
"AdvanceNumber": advanceNumber,
"AdvanceNumber_VP": advanceNumber,
"PaymentReferenceNumber": paymentReference,
"AppointmentID": 0,
};
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(
ADD_ADVANCE_NUMBER_REQUEST,
body: requestBody,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: response,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel>> getPatientInfoByPatientID({required String patientID}) async {
Map<String, dynamic> requestBody = {"SearchPatientID": patientID};
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(
GET_PATIENT_INFO_BY_ID,
body: requestBody,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: response,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
}

@ -1,23 +1,94 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_repo.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
class HabibWalletViewModel extends ChangeNotifier {
bool isWalletAmountLoading = false;
num habibWalletAmount = 0;
num walletRechargeAmount = 0;
bool isBottomSheetContentLoading = false;
bool isSearchedFileNumberDataShown = false;
int currentIndex = 0;
List<HospitalsModel> advancePaymentHospitals = [];
HospitalsModel? selectedHospital;
HabibWalletRepo habibWalletRepo;
ErrorHandlerService errorHandlerService;
String fileNumber = '';
String depositorName = '';
String mobileNumber = '';
num selectedRechargeType = 1;
HabibWalletViewModel({required this.habibWalletRepo, required this.errorHandlerService});
initHabibWalletProvider() {
isWalletAmountLoading = true;
isBottomSheetContentLoading = false;
habibWalletAmount = 0;
walletRechargeAmount = 0;
selectedRechargeType = 1;
advancePaymentHospitals.clear();
selectedHospital = null;
fileNumber = '';
depositorName = '';
mobileNumber = '';
notifyListeners();
}
setSelectedRechargeType(num type) {
selectedRechargeType = type;
notifyListeners();
}
setSelectedHospital(HospitalsModel hospital) {
selectedHospital = hospital;
notifyListeners();
}
setWalletRechargeAmount(num amount) {
walletRechargeAmount = amount;
notifyListeners();
}
setDepositorDetails(String fileNum, String depositor, String mobile) {
fileNumber = fileNum;
depositorName = depositor;
mobileNumber = mobile;
notifyListeners();
}
setCurrentIndex(int index) {
currentIndex = index;
notifyListeners();
}
String getSelectedRechargeTypeValue() {
switch (selectedRechargeType) {
case 1:
return LocaleKeys.myAccount.tr();
case 2:
return LocaleKeys.family.tr();
case 3:
return LocaleKeys.otherAccount.tr();
default:
return LocaleKeys.myAccount.tr();
}
}
Future<void> getPatientBalanceAmount({Function(dynamic)? onSuccess, Function(String)? onError}) async {
isWalletAmountLoading = true;
notifyListeners();
final result = await habibWalletRepo.getPatientBalanceAmount();
result.fold(
@ -36,4 +107,92 @@ class HabibWalletViewModel extends ChangeNotifier {
},
);
}
Future<void> getProjectsList() async {
advancePaymentHospitals.clear();
notifyListeners();
final result = await habibWalletRepo.getProjectList();
result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure),
(apiResponse) async {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
advancePaymentHospitals = apiResponse.data!;
notifyListeners();
}
},
);
}
Future<void> HISCreateAdvancePayment(
{required String paymentMethodName,
required num paidAmount,
required String paymentReference,
required String patientID,
required int projectID,
required String depositorName,
Function(dynamic)? onSuccess,
Function(String)? onError}) async {
final result = await habibWalletRepo.HISCreateAdvancePayment(
paymentMethodName: paymentMethodName, paidAmount: paidAmount, paymentReference: paymentReference, patientID: patientID, projectID: projectID, depositorName: depositorName);
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) {
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
Future<void> addAdvanceNumberRequest({required String advanceNumber, required String paymentReference, Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await habibWalletRepo.addAdvanceNumberRequest(advanceNumber: advanceNumber, paymentReference: paymentReference);
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) {
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
Future<void> getPatientInfoByPatientID({required String patientID, Function(dynamic)? onSuccess, Function(String)? onError}) async {
isBottomSheetContentLoading = true;
notifyListeners();
final result = await habibWalletRepo.getPatientInfoByPatientID(patientID: patientID);
result.fold(
(failure) async {
isBottomSheetContentLoading = false;
notifyListeners();
await errorHandlerService.handleError(failure: failure);
},
(apiResponse) {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
isBottomSheetContentLoading = false;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
}

@ -2,6 +2,7 @@ import 'package:flutter/foundation.dart' show ChangeNotifier;
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/doctor_list_api_response.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/select_clinic_page.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:hmg_patient_app_new/widgets/transitions/fade_page.dart';
enum AppointmentViaRegionState {
@ -39,7 +40,7 @@ class AppointmentViaRegionViewmodel extends ChangeNotifier {
void handleLastStep(){
navigationService.pop();
navigationService.push(FadePage(
navigationService.push(CustomPageRoute(
page: SelectClinicPage(),
),);
}

@ -79,6 +79,11 @@ class MyAppointmentsViewModel extends ChangeNotifier {
}
Future<void> getPatientAppointments(bool isActiveAppointment, bool isArrivedAppointments, {Function(dynamic)? onSuccess, Function(String)? onError}) async {
patientAppointmentsHistoryList.clear();
patientUpcomingAppointmentsHistoryList.clear();
patientArrivedAppointmentsHistoryList.clear();
final result = await myAppointmentsRepo.getPatientAppointments(isActiveAppointment: isActiveAppointment, isArrivedAppointments: isArrivedAppointments);
final resultArrived = await myAppointmentsRepo.getPatientAppointments(isActiveAppointment: false, isArrivedAppointments: true);

@ -7,6 +7,7 @@ import 'package:hmg_patient_app_new/features/payfort/models/payfort_project_deta
import 'package:hmg_patient_app_new/features/payfort/models/sdk_token_response_model.dart';
import 'package:hmg_patient_app_new/features/payfort/payfort_repo.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:network_info_plus/network_info_plus.dart';
class PayfortViewModel extends ChangeNotifier {
@ -99,6 +100,7 @@ class PayfortViewModel extends ChangeNotifier {
String? applePayShaType,
String? applePayShaRequestPhrase,
}) async {
var sdkTokenResponse;
try {
String? deviceId = await _payfort.getDeviceId();
@ -125,6 +127,7 @@ class PayfortViewModel extends ChangeNotifier {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
// payfortProjectDetailsRespModel = apiResponse.data!;
sdkTokenResponse = apiResponse.data;
isApplePayConfigurationLoading = false;
notifyListeners();
}
@ -133,7 +136,7 @@ class PayfortViewModel extends ChangeNotifier {
} catch (e) {
print("Error here: ${e.toString()}");
}
return null;
return sdkTokenResponse;
}
Future<void> paymentWithApplePay({
@ -181,6 +184,7 @@ class PayfortViewModel extends ChangeNotifier {
isApplePayConfigurationLoading = false;
notifyListeners();
LoaderBottomSheet.hideLoader();
_payfort.callPayFortForApplePay(
request: request,

@ -1,3 +1,5 @@
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
class PatientRadiologyResponseModel {
String? setupID;
int? projectID;
@ -6,7 +8,7 @@ class PatientRadiologyResponseModel {
int? invoiceNo;
int? doctorID;
int? clinicID;
String? orderDate;
DateTime? orderDate;
String? reportData;
String? imageURL;
String? procedureID;
@ -120,7 +122,7 @@ class PatientRadiologyResponseModel {
invoiceNo = json['InvoiceNo'];
doctorID = json['DoctorID'];
clinicID = json['ClinicID'];
orderDate = json['OrderDate'];
orderDate = DateUtil.convertStringToDate(json['OrderDate']);
reportData = json['ReportData'];
imageURL = json['ImageURL'];
procedureID = json['ProcedureID'];

@ -3,11 +3,17 @@ 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/core/utils/utils.dart';
import 'package:hmg_patient_app_new/features/authentication/models/resp_models/authenticated_user_resp_model.dart';
import 'package:hmg_patient_app_new/features/radiology/models/resp_models/patient_radiology_response_model.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
abstract class RadiologyRepo {
Future<Either<Failure, GenericApiModel<List<PatientRadiologyResponseModel>>>> getPatientRadiologyOrders({required String patientId});
Future<Either<Failure, GenericApiModel<String>>> getRadiologyImage({required PatientRadiologyResponseModel patientRadiologyResponseModel});
Future<Either<Failure, GenericApiModel<String>>> getRadiologyReportPDF({required PatientRadiologyResponseModel patientRadiologyResponseModel, required AuthenticatedUser authenticatedUser});
}
class RadiologyRepoImp implements RadiologyRepo {
@ -58,4 +64,99 @@ class RadiologyRepoImp implements RadiologyRepo {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<String>>> getRadiologyImage({required PatientRadiologyResponseModel patientRadiologyResponseModel}) async {
Map<String, dynamic> mapDevice = {
"InvoiceNo": Utils.isVidaPlusProject(patientRadiologyResponseModel.projectID!) ? "0" : patientRadiologyResponseModel.invoiceNo,
"InvoiceNo_VP": Utils.isVidaPlusProject(patientRadiologyResponseModel.projectID!) ? patientRadiologyResponseModel.invoiceNo : "0",
"LineItemNo": patientRadiologyResponseModel.invoiceLineItemNo,
"ProjectID": patientRadiologyResponseModel.projectID!,
"InvoiceType": patientRadiologyResponseModel.invoiceType!,
"ExamId": patientRadiologyResponseModel.examId ?? "",
};
try {
GenericApiModel<String>? apiResponse;
Failure? failure;
await apiClient.post(
GET_RAD_IMAGE_URL,
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["Data"],
);
} 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<String>>> getRadiologyReportPDF({required PatientRadiologyResponseModel patientRadiologyResponseModel, required AuthenticatedUser authenticatedUser}) async {
Map<String, dynamic> mapDevice = {
"InvoiceNo": Utils.isVidaPlusProject(patientRadiologyResponseModel.projectID!) ? 0 : patientRadiologyResponseModel.invoiceNo,
"InvoiceNo_VP": Utils.isVidaPlusProject(patientRadiologyResponseModel.projectID!) ? patientRadiologyResponseModel.invoiceNo : 0,
"LineItemNo": patientRadiologyResponseModel.invoiceLineItemNo,
"InvoiceLineItemNo": patientRadiologyResponseModel.invoiceLineItemNo,
"ProjectID": patientRadiologyResponseModel.projectID!,
"InvoiceType": patientRadiologyResponseModel.invoiceType!,
"SetupID": patientRadiologyResponseModel.setupID!,
// "ExamId": patientRadiologyResponseModel.examId ?? "",
"IsDownload": true,
'ClinicName': patientRadiologyResponseModel.clinicDescription,
'DateofBirth': authenticatedUser.dateofBirth,
'DoctorName': patientRadiologyResponseModel.doctorName,
'OrderDate': '${patientRadiologyResponseModel.orderDate!.year}-${patientRadiologyResponseModel.orderDate!.month}-${patientRadiologyResponseModel.orderDate!.day}',
'PatientIditificationNum': authenticatedUser.patientIdentificationNo,
'PatientMobileNumber': authenticatedUser.mobileNumber,
'PatientName': "${authenticatedUser.firstName!} ${authenticatedUser.lastName!}",
'ProjectName': patientRadiologyResponseModel.projectName,
'RadResult': patientRadiologyResponseModel.reportData,
"To": authenticatedUser.emailAddress
};
try {
GenericApiModel<String>? apiResponse;
Failure? failure;
await apiClient.post(
SEND_RAD_REPORT_EMAIL,
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["Base64Data"],
);
} 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()));
}
}
}

@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/features/authentication/models/resp_models/authenticated_user_resp_model.dart';
import 'package:hmg_patient_app_new/features/radiology/radiology_repo.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
@ -6,17 +7,23 @@ import 'models/resp_models/patient_radiology_response_model.dart';
class RadiologyViewModel extends ChangeNotifier {
bool isRadiologyOrdersLoading = false;
bool isRadiologyPDFReportLoading = false;
RadiologyRepo radiologyRepo;
ErrorHandlerService errorHandlerService;
List<PatientRadiologyResponseModel> patientRadiologyOrders = [];
String radiologyImageURL = "";
String patientRadiologyReportPDFBase64 = "";
RadiologyViewModel({required this.radiologyRepo, required this.errorHandlerService});
initRadiologyProvider() {
patientRadiologyOrders.clear();
isRadiologyOrdersLoading = true;
isRadiologyPDFReportLoading = true;
radiologyImageURL = "";
getPatientRadiologyOrders();
notifyListeners();
}
@ -40,4 +47,50 @@ class RadiologyViewModel extends ChangeNotifier {
},
);
}
Future<void> getRadiologyImage({required PatientRadiologyResponseModel patientRadiologyResponseModel, Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await radiologyRepo.getRadiologyImage(patientRadiologyResponseModel: patientRadiologyResponseModel);
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) {
radiologyImageURL = apiResponse.data!;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
Future<void> getRadiologyPDF(
{required PatientRadiologyResponseModel patientRadiologyResponseModel, required AuthenticatedUser authenticatedUser, Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await radiologyRepo.getRadiologyReportPDF(patientRadiologyResponseModel: patientRadiologyResponseModel, authenticatedUser: authenticatedUser);
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) {
patientRadiologyReportPDFBase64 = apiResponse.data!;
isRadiologyPDFReportLoading = false;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
}

@ -846,5 +846,7 @@ abstract class LocaleKeys {
static const pleaseSelectACountry = 'pleaseSelectACountry';
static const pleaseEnterEmail = 'pleaseEnterEmail';
static const pleaseEnterAValidEmailFormat = 'pleaseEnterAValidEmailFormat';
static const selectCountry = 'selectCountry';
static const forLoginVerification = 'forLoginVerification';
}

@ -132,6 +132,7 @@ void main() async {
errorHandlerService: getIt(),
navigationService: getIt(),
myAppointmentsViewModel: getIt(),
locationUtils: getIt(),
),
),
ChangeNotifierProvider<AuthenticationViewModel>(

@ -5,6 +5,7 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
@ -28,11 +29,13 @@ 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/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
import 'package:maps_launcher/maps_launcher.dart';
import 'package:provider/provider.dart';
import '../../core/dependencies.dart';
import '../medical_file/widgets/medical_file_card.dart';
class AppointmentDetailsPage extends StatefulWidget {
@ -62,6 +65,7 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
@override
Widget build(BuildContext context) {
AppState appState = getIt.get<AppState>();
myAppointmentsViewModel = Provider.of<MyAppointmentsViewModel>(context, listen: false);
prescriptionsViewModel = Provider.of<PrescriptionsViewModel>(context, listen: false);
bookAppointmentsViewModel = Provider.of<BookAppointmentsViewModel>(context, listen: false);
@ -72,7 +76,7 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
Expanded(
child: CollapsingListView(
title: "Appointment Details".needTranslation,
report: () {},
report: AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) ? () {} : null,
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -312,13 +316,16 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
],
),
SizedBox(width: 68.h),
Utils.buildSvgWithAssets(
Transform.flip(
flipX: appState.isArabic() ? true : false,
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon,
iconColor: AppColors.blackColor,
width: 18.h,
height: 13.h,
fit: BoxFit.contain,
),
),
],
),
],
@ -331,7 +338,7 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
).onPress(() {
prescriptionVM.setPrescriptionsDetailsLoading();
Navigator.of(context).push(
FadePage(
CustomPageRoute(
page: PrescriptionDetailPage(prescriptionsResponseModel: getPrescriptionRequestModel()),
),
);
@ -364,7 +371,7 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
onPressed: () {
Navigator.of(context)
.push(
FadePage(
CustomPageRoute(
page: PrescriptionsListPage(),
),
)
@ -425,8 +432,8 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
LocaleKeys.upcomingPaymentNow.tr(context: context).toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor),
"VAT 15%(${widget.patientAppointmentHistoryResponseModel.patientTaxAmount})".needTranslation.toText14(isBold: true, color: AppColors.greyTextColor),
Expanded(child: LocaleKeys.upcomingPaymentNow.tr(context: context).toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor)),
"VAT 15%(${widget.patientAppointmentHistoryResponseModel.patientTaxAmount})".needTranslation.toText14(isBold: true, color: AppColors.greyTextColor, letterSpacing: -2),
],
),
SizedBox(height: 18.h),
@ -560,7 +567,7 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
case 20:
myAppointmentsViewModel.setIsPatientAppointmentShareLoading(true);
Navigator.of(context).push(
FadePage(
CustomPageRoute(
page: AppointmentPaymentPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel),
),
);

@ -27,6 +27,7 @@ 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/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/in_app_browser/InAppBrowser.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
import 'package:provider/provider.dart';
@ -79,9 +80,7 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
child: CollapsingListView(
title: "Appointment Payment".needTranslation,
child: SingleChildScrollView(
child: myAppointmentsVM.isAppointmentPatientShareLoading
? const MoviesShimmerWidget().paddingAll(24.h)
: Column(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 24.h),
@ -97,19 +96,22 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset(AppAssets.mada, width: 72.h, height: 25.h),
Image.asset(AppAssets.mada, width: 72.h, height: 25.h).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading),
SizedBox(height: 16.h),
"Mada".needTranslation.toText16(isBold: true),
"Mada".needTranslation.toText16(isBold: true).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading),
],
),
SizedBox(width: 8.h),
const Spacer(),
Utils.buildSvgWithAssets(
Transform.flip(
flipX: appState.isArabic() ? true : false,
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon,
iconColor: AppColors.blackColor,
width: 18.h,
height: 13.h,
fit: BoxFit.contain,
).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading),
),
],
).paddingSymmetrical(16.h, 16.h),
@ -136,19 +138,22 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
SizedBox(width: 8.h),
Image.asset(AppAssets.Mastercard, width: 40.h, height: 40.h),
],
),
).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading),
SizedBox(height: 16.h),
"Visa or Mastercard".needTranslation.toText16(isBold: true),
"Visa or Mastercard".needTranslation.toText16(isBold: true).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading),
],
),
SizedBox(width: 8.h),
const Spacer(),
Utils.buildSvgWithAssets(
Transform.flip(
flipX: appState.isArabic() ? true : false,
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon,
iconColor: AppColors.blackColor,
width: 18.h,
height: 13.h,
fit: BoxFit.contain,
).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading),
),
],
).paddingSymmetrical(16.h, 16.h),
@ -169,19 +174,22 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset(AppAssets.tamara_en, width: 72.h, height: 25.h),
Image.asset(AppAssets.tamara_en, width: 72.h, height: 25.h).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading),
SizedBox(height: 16.h),
"Tamara".needTranslation.toText16(isBold: true),
"Tamara".needTranslation.toText16(isBold: true).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading),
],
),
SizedBox(width: 8.h),
const Spacer(),
Utils.buildSvgWithAssets(
Transform.flip(
flipX: appState.isArabic() ? true : false,
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon,
iconColor: AppColors.blackColor,
width: 18.h,
height: 13.h,
fit: BoxFit.contain,
).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading),
),
],
).paddingSymmetrical(16.h, 16.h),
@ -204,9 +212,7 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
),
child: Consumer<PayfortViewModel>(builder: (context, payfortVM, child) {
//TODO: Need to add loading state & animation for Apple Pay Configuration
return payfortVM.isApplePayConfigurationLoading
? const MoviesShimmerWidget().paddingAll(16.h)
: Column(
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
(myAppointmentsVM.patientAppointmentShareResponseModel!.isCash ?? true)
@ -227,7 +233,7 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
text: LocaleKeys.updateInsurance.tr(context: context),
onPressed: () {
Navigator.of(context).push(
FadePage(
CustomPageRoute(
page: InsuranceHomePage(),
),
);
@ -272,21 +278,25 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"".needTranslation.toText14(isBold: true),
Utils.getPaymentAmountWithSymbol(
myAppointmentsVM.patientAppointmentShareResponseModel!.patientShareWithTax!.toString().toText24(isBold: true), AppColors.blackColor, 17,
Utils.getPaymentAmountWithSymbol(myAppointmentsVM.patientAppointmentShareResponseModel!.patientShareWithTax!.toString().toText24(isBold: true), AppColors.blackColor, 17,
isSaudiCurrency: true),
],
).paddingSymmetrical(24.h, 0.h),
//TODO: Add Apple Pay Privileges
Utils.buildSvgWithAssets(
Platform.isIOS
? Utils.buildSvgWithAssets(
icon: AppAssets.apple_pay_button,
width: 200.h,
height: 80.h,
fit: BoxFit.contain,
).paddingSymmetrical(24.h, 0.h).onPress(() {
// payfortVM.setIsApplePayConfigurationLoading(true);
if (Utils.havePrivilege(103)) {
startApplePay();
}),
} else {
openPaymentURL(selectedPaymentMethod);
}
})
: SizedBox(height: 12.h),
SizedBox(height: 12.h),
],
);
@ -365,8 +375,8 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(),
payedAmount: payfortViewModel.payfortCheckPaymentStatusResponseModel!.amount!,
paymentReference: payfortViewModel.payfortCheckPaymentStatusResponseModel!.fortId!,
patientID: "4767477",
patientType: 1,
patientID: appState.getAuthenticatedUser()!.patientId.toString(),
patientType: appState.getAuthenticatedUser()!.patientType!,
onSuccess: (value) async {
print(value);
await myAppointmentsViewModel.addAdvanceNumberRequest(
@ -389,12 +399,12 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
Navigator.of(context).pop();
Navigator.pushAndRemoveUntil(
context,
FadePage(
CustomPageRoute(
page: LandingNavigation(),
),
(r) => false);
Navigator.of(context).push(
FadePage(page: MyAppointmentsPage()),
CustomPageRoute(page: MyAppointmentsPage()),
);
});
});
@ -528,6 +538,7 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
onSucceeded: (successResult) async {
Navigator.of(context).pop();
log("successResult: ${successResult.responseMessage.toString()}");
selectedPaymentMethod = successResult.paymentOption ?? "VISA";
checkPaymentStatus();
},
// projectId: appo.projectID,

@ -6,6 +6,7 @@ import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_card.dart';
import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart';
@ -53,7 +54,6 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
CustomTabBarModel(null, "Completed".needTranslation),
],
onTabChange: (index) {
print(index);
myAppointmentsViewModel.onTabChange(index);
},
).paddingSymmetrical(24.h, 0.h),
@ -86,7 +86,15 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
: 1,
itemBuilder: (context, index) {
return myAppointmentsVM.isMyAppointmentsLoading
? const MoviesShimmerWidget().paddingSymmetrical(24.h, 0.h)
? Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
child: AppointmentCard(
patientAppointmentHistoryResponseModel: PatientAppointmentHistoryResponseModel(),
myAppointmentsViewModel: myAppointmentsViewModel,
isLoading: true,
isFromHomePage: false,
),
).paddingSymmetrical(24.h, 0.h)
: myAppointmentsVM.patientAppointmentsHistoryList.isNotEmpty
? AnimationConfiguration.staggeredList(
position: index,
@ -101,6 +109,8 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
child: AppointmentCard(
patientAppointmentHistoryResponseModel: myAppointmentsVM.patientAppointmentsHistoryList[index],
myAppointmentsViewModel: myAppointmentsViewModel,
isLoading: false,
isFromHomePage: false,
),
).paddingSymmetrical(24.h, 0.h),
),

@ -2,6 +2,8 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
@ -21,6 +23,7 @@ class MyDoctorsPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
AppState appState = getIt.get<AppState>();
myAppointmentsViewModel = Provider.of<MyAppointmentsViewModel>(context, listen: false);
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
@ -136,7 +139,9 @@ class MyDoctorsPage extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"".toText16(),
Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, width: 15.h, height: 15.h, fit: BoxFit.contain, iconColor: AppColors.textColor),
Transform.flip(
flipX: appState.isArabic() ? true : false,
child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, width: 15.h, height: 15.h, fit: BoxFit.contain, iconColor: AppColors.textColor)),
],
),
],

@ -16,14 +16,16 @@ import 'package:hmg_patient_app_new/presentation/appointments/appointment_detail
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/routes/custom_page_route.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
import 'package:smooth_corner/smooth_corner.dart';
class AppointmentCard extends StatefulWidget {
AppointmentCard({super.key, required this.patientAppointmentHistoryResponseModel, required this.myAppointmentsViewModel});
AppointmentCard({super.key, required this.patientAppointmentHistoryResponseModel, required this.myAppointmentsViewModel, this.isLoading = false, this.isFromHomePage = false});
PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel;
MyAppointmentsViewModel myAppointmentsViewModel;
bool isLoading;
bool isFromHomePage;
@override
State<AppointmentCard> createState() => _AppointmentCardState();
@ -37,7 +39,7 @@ class _AppointmentCardState extends State<AppointmentCard> {
onTap: () {
Navigator.of(context)
.push(
FadePage(
CustomPageRoute(
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel),
),
)
@ -65,7 +67,9 @@ class _AppointmentCardState extends State<AppointmentCard> {
mainAxisSize: MainAxisSize.min,
children: [
CustomButton(
text: appState.isArabic()
text: widget.isLoading
? "OutPatient"
: appState.isArabic()
? widget.patientAppointmentHistoryResponseModel.isInOutPatientDescriptionN!
: widget.patientAppointmentHistoryResponseModel.isInOutPatientDescription!,
onPressed: () {},
@ -84,7 +88,7 @@ class _AppointmentCardState extends State<AppointmentCard> {
mainAxisSize: MainAxisSize.min,
children: [
CustomButton(
text: AppointmentType.getAppointmentStatusType(widget.patientAppointmentHistoryResponseModel.patientStatusType!),
text: widget.isLoading ? "Booked" : AppointmentType.getAppointmentStatusType(widget.patientAppointmentHistoryResponseModel.patientStatusType!),
onPressed: () {},
backgroundColor: AppColors.successColor.withOpacity(0.1),
borderColor: AppColors.successColor.withOpacity(0.0),
@ -98,11 +102,11 @@ 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()
? SizedBox().toShimmer2(isShow: widget.isLoading)
: Switch(
activeColor: AppColors.successColor,
activeTrackColor: AppColors.successColor.withValues(alpha: .15),
@ -114,13 +118,13 @@ class _AppointmentCardState extends State<AppointmentCard> {
return const Icon(Icons.close); // Icon when switch is OFF
},
),
value: widget.patientAppointmentHistoryResponseModel.hasReminder!,
value: widget.isLoading ? false : widget.patientAppointmentHistoryResponseModel.hasReminder!,
onChanged: (newValue) {
setState(() {
widget.myAppointmentsViewModel.setAppointmentReminder(newValue, widget.patientAppointmentHistoryResponseModel);
});
},
),
).toShimmer2(isShow: widget.isLoading),
],
),
SizedBox(height: 16.h),
@ -128,30 +132,42 @@ class _AppointmentCardState extends State<AppointmentCard> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.network(
widget.patientAppointmentHistoryResponseModel.doctorImageURL!,
widget.isLoading ? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png" : widget.patientAppointmentHistoryResponseModel.doctorImageURL!,
width: 63.h,
height: 63.h,
fit: BoxFit.fill,
).circle(100),
).circle(100).toShimmer2(isShow: widget.isLoading),
SizedBox(width: 16.h),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
widget.patientAppointmentHistoryResponseModel.doctorNameObj!.toText16(isBold: true),
(widget.isLoading ? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png" : widget.patientAppointmentHistoryResponseModel.doctorNameObj!)
.toText16(isBold: true)
.toShimmer2(isShow: widget.isLoading),
SizedBox(height: 8.h),
Wrap(
direction: Axis.horizontal,
spacing: 3.h,
runSpacing: 4.h,
children: [
AppCustomChipWidget(labelText: widget.patientAppointmentHistoryResponseModel.clinicName!),
AppCustomChipWidget(labelText: widget.patientAppointmentHistoryResponseModel.projectName!),
widget.isFromHomePage
? SizedBox.shrink()
: AppCustomChipWidget(labelText: widget.isLoading ? "Cardiology" : widget.patientAppointmentHistoryResponseModel.clinicName!).toShimmer2(isShow: widget.isLoading),
widget.isFromHomePage
? SizedBox.shrink()
: AppCustomChipWidget(labelText: widget.isLoading ? "Olaya" : widget.patientAppointmentHistoryResponseModel.projectName!).toShimmer2(isShow: widget.isLoading),
AppCustomChipWidget(
icon: AppAssets.appointment_calendar_icon,
labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate), false)),
labelText:
widget.isLoading ? "Cardiology" : DateUtil.formatDateToDate(DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate), false))
.toShimmer2(isShow: widget.isLoading),
AppCustomChipWidget(
icon: AppAssets.appointment_time_icon,
labelText: DateUtil.formatDateToTimeLang(DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate), false)),
labelText: widget.isLoading
? "Cardiology"
: DateUtil.formatDateToTimeLang(DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate), false))
.toShimmer2(isShow: widget.isLoading),
],
),
],
@ -165,12 +181,12 @@ class _AppointmentCardState extends State<AppointmentCard> {
Expanded(
flex: 6,
child: AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)
? getArrivedAppointmentButton()
? getArrivedAppointmentButton().toShimmer2(isShow: widget.isLoading)
: CustomButton(
text: AppointmentType.getNextActionText(widget.patientAppointmentHistoryResponseModel.nextAction),
onPressed: () {
Navigator.of(context)
.push(FadePage(
.push(CustomPageRoute(
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel),
))
.then((val) {
@ -188,8 +204,8 @@ class _AppointmentCardState extends State<AppointmentCard> {
height: 40.h,
icon: AppointmentType.getNextActionIcon(widget.patientAppointmentHistoryResponseModel.nextAction),
iconColor: AppointmentType.getNextActionTextColor(widget.patientAppointmentHistoryResponseModel.nextAction),
iconSize: 14.h,
),
iconSize: 15.h,
).toShimmer2(isShow: widget.isLoading),
),
SizedBox(width: 8.h),
Expanded(
@ -203,17 +219,21 @@ class _AppointmentCardState extends State<AppointmentCard> {
),
child: Padding(
padding: EdgeInsets.all(10.h),
child: Transform.flip(
flipX: appState.isArabic() ? true : false,
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon,
iconColor: AppColors.whiteColor,
width: 10.h,
height: 10.h,
fit: BoxFit.contain,
),
),
).onPress(() {
),
).toShimmer2(isShow: widget.isLoading).onPress(() {
Navigator.of(context)
.push(
FadePage(
CustomPageRoute(
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel),
),
)

@ -19,6 +19,7 @@ import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:barcode_scan2/barcode_scan2.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/nfc/nfc_reader_sheet.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
class AppointmentCheckinBottomSheet extends StatelessWidget {
@ -30,11 +31,12 @@ class AppointmentCheckinBottomSheet extends StatelessWidget {
bool _supportsNFC = false;
late LocationUtils locationUtils;
late AppState appState;
ProjectDetailListModel projectDetailListModel = ProjectDetailListModel();
@override
Widget build(BuildContext context) {
AppState _appState = getIt.get<AppState>();
appState = getIt.get<AppState>();
FlutterNfcKit.nfcAvailability.then((value) {
_supportsNFC = (value == NFCAvailability.available);
});
@ -51,8 +53,8 @@ class AppointmentCheckinBottomSheet extends StatelessWidget {
// navigationService: myAppointmentsViewModel.navigationService,
// appState: myAppointmentsViewModel.appState,
// );
locationUtils.getCurrentLocation(callBack: (value) {
projectDetailListModel = Utils.getProjectDetailObj(_appState, patientAppointmentHistoryResponseModel.projectID);
locationUtils.getCurrentLocation(onSuccess: (value) {
projectDetailListModel = Utils.getProjectDetailObj(appState, patientAppointmentHistoryResponseModel.projectID);
double dist = Utils.distance(value.latitude, value.longitude, double.parse(projectDetailListModel.latitude!), double.parse(projectDetailListModel.longitude!)).ceilToDouble() * 1000;
print(dist);
if (dist <= projectDetailListModel.geofenceRadius!) {
@ -110,20 +112,25 @@ class AppointmentCheckinBottomSheet extends StatelessWidget {
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
title.toText16(isBold: true, color: AppColors.textColor),
subTitle.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor),
],
),
Utils.buildSvgWithAssets(
),
Transform.flip(
flipX: appState.isArabic() ? true : false,
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon,
iconColor: AppColors.blackColor,
width: 18.h,
height: 13.h,
fit: BoxFit.contain,
),
),
],
),
],
@ -144,12 +151,12 @@ class AppointmentCheckinBottomSheet extends StatelessWidget {
Navigator.of(context).pop();
Navigator.pushAndRemoveUntil(
context,
FadePage(
CustomPageRoute(
page: LandingNavigation(),
),
(r) => false);
Navigator.of(context).push(
FadePage(page: MyAppointmentsPage()),
CustomPageRoute(page: MyAppointmentsPage()),
);
}, isFullScreen: false);
},

@ -1,5 +1,7 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
@ -21,6 +23,7 @@ class FacilitySelectionItem extends StatelessWidget {
@override
Widget build(BuildContext context) {
AppState appState = getIt.get<AppState>();
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
@ -42,13 +45,16 @@ class FacilitySelectionItem extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
info,
Utils.buildSvgWithAssets(
Transform.flip(
flipX: appState.isArabic() ? true : false,
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon,
iconColor: AppColors.blackColor,
width: 18,
height: 13,
fit: BoxFit.contain,
),
),
],
)
],

@ -93,7 +93,7 @@ class HospitalBottomSheetBody extends StatelessWidget {
?.hmcDoctorList?[index];
return HospitalListItem(
hospitalData: hospital,
isLocationEnabled: appointmentsViewModel.getLocationStatus(),
isLocationEnabled: appointmentsViewModel.isLocationEnabled(),
).onPress(() {
regionalViewModel.setHospitalModel(hospital);
regionalViewModel.setBottomSheetState(AppointmentViaRegionState.CLINIC_SELECTION);

@ -17,8 +17,7 @@ class HospitalListItem extends StatelessWidget {
late AppState appState;
HospitalListItem(
{super.key, required this.hospitalData, required this.isLocationEnabled});
HospitalListItem({super.key, required this.hospitalData, required this.isLocationEnabled});
@override
Widget build(BuildContext context) {
@ -39,13 +38,16 @@ class HospitalListItem extends StatelessWidget {
children: [hospitalName, distanceInfo],
),
),
Utils.buildSvgWithAssets(
Transform.flip(
flipX: appState.isArabic() ? true : false,
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon,
iconColor: AppColors.blackColor,
width: 18,
height: 13,
fit: BoxFit.contain,
),
),
],
).paddingSymmetrical(16.h, 16.h),
);
@ -70,23 +72,16 @@ class HospitalListItem extends StatelessWidget {
);
Widget get distanceInfo => Row(
children: [
Visibility(
visible: (hospitalData?.distanceInKMs != "0"),
child:
AppCustomChipWidget(
labelText:
"${hospitalData?.distanceInKMs ?? ""} km".needTranslation,
child: AppCustomChipWidget(
labelText: "${hospitalData?.distanceInKMs ?? ""} km".needTranslation,
deleteIcon: AppAssets.location_red,
deleteIconSize: Size(9, 12),
backgroundColor: AppColors.secondaryLightRedColor,
textColor: AppColors.errorColor,
),
),
Visibility(
visible: (hospitalData?.distanceInKMs == "0"),
@ -96,8 +91,9 @@ class HospitalListItem extends StatelessWidget {
labelText: "Distance not available".needTranslation,
textColor: AppColors.blackColor,
),
SizedBox(width: 8.h,)
SizedBox(
width: 8.h,
)
],
)),
Visibility(

@ -1,6 +1,8 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
@ -15,15 +17,11 @@ class RegionListItem extends StatelessWidget {
final String hmgCount;
final String subTitle;
const RegionListItem(
{super.key,
required this.title,
required this.subTitle,
required this.hmcCount,
required this.hmgCount});
const RegionListItem({super.key, required this.title, required this.subTitle, required this.hmcCount, required this.hmgCount});
@override
Widget build(BuildContext context) {
AppState appState = getIt.get<AppState>();
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
@ -40,19 +38,20 @@ class RegionListItem extends StatelessWidget {
Row(
spacing: 8.h,
children: [
placesCountItem(
AppAssets.hmg, hmgCount, " ${LocaleKeys.hospital.tr()}"),
placesCountItem(AppAssets.hmc, hmcCount,
" ${LocaleKeys.medicalCenters.tr()}"),
placesCountItem(AppAssets.hmg, hmgCount, " ${LocaleKeys.hospital.tr()}"),
placesCountItem(AppAssets.hmc, hmcCount, " ${LocaleKeys.medicalCenters.tr()}"),
],
),
Utils.buildSvgWithAssets(
Transform.flip(
flipX: appState.isArabic() ? true : false,
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon,
iconColor: AppColors.blackColor,
width: 18,
height: 13,
fit: BoxFit.contain,
),
),
],
)
],
@ -68,18 +67,8 @@ class RegionListItem extends StatelessWidget {
richText: RichText(
text: TextSpan(
text: count,
style: TextStyle(
fontSize: 12.h,
fontWeight: FontWeight.w700,
color: AppColors.blackColor),
children: [
TextSpan(
text: title,
style: TextStyle(
fontSize: 12.h,
fontWeight: FontWeight.w500,
color: AppColors.blackColor))
])),
style: TextStyle(fontSize: 12.h, fontWeight: FontWeight.w700, color: AppColors.blackColor),
children: [TextSpan(text: title, style: TextStyle(fontSize: 12.h, fontWeight: FontWeight.w500, color: AppColors.blackColor))])),
);
}

@ -43,7 +43,9 @@ class _RegisterNew extends State<RegisterNewStep2> {
@override
Widget build(BuildContext context) {
AppState appState = getIt.get<AppState>();
var name = appState.getLanguageCode() == "en"
var name = authVM!.isUserFromUAE()
? ""
: appState.getLanguageCode() == "en"
? ("${appState.getNHICUserData.firstNameEn!.toUpperCase()} ${appState.getNHICUserData.lastNameEn!.toUpperCase()}")
: ("${appState.getNHICUserData.firstNameAr!.toUpperCase()} ${appState.getNHICUserData.lastNameAr!.toUpperCase()}");
return Scaffold(
@ -57,7 +59,15 @@ class _RegisterNew extends State<RegisterNewStep2> {
onLanguageChanged: (lang) {},
hideLogoAndLang: true,
),
body: SingleChildScrollView(
body: GestureDetector(
onTap: () {
FocusScope.of(context).unfocus();
},
behavior: HitTestBehavior.translucent, // Ensures taps on empty space are detected
child: SizedBox(
width: double.infinity,
height: double.infinity,
child: SingleChildScrollView(
reverse: false,
padding: EdgeInsets.only(left: 24.h, right: 24.h, top: 0.h),
child: Column(
@ -65,9 +75,7 @@ class _RegisterNew extends State<RegisterNewStep2> {
children: <Widget>[
LocaleKeys.personalDetailsVerification.tr().toText26(color: AppColors.textColor, weight: FontWeight.w600, letterSpacing: -2),
SizedBox(height: 24.h),
Directionality(
textDirection: Directionality.of(context),
child: Container(
Container(
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(24)),
padding: EdgeInsets.only(left: 16.h, right: 16.h),
child: Column(
@ -81,6 +89,10 @@ class _RegisterNew extends State<RegisterNewStep2> {
isAllowRadius: false,
isBorderAllowed: false,
keyboardType: TextInputType.text,
// textInputAction: TextInputAction.done,
onSubmitted: (value) {
FocusScope.of(context).unfocus();
},
isAllowLeadingIcon: true,
isReadOnly: authVM!.isUserFromUAE() ? false : true,
leadingIcon: AppAssets.user_circle,
@ -263,7 +275,6 @@ class _RegisterNew extends State<RegisterNewStep2> {
],
),
),
),
SizedBox(height: 50.h),
Row(
children: [
@ -315,6 +326,8 @@ class _RegisterNew extends State<RegisterNewStep2> {
],
),
),
),
),
);
}

@ -65,14 +65,16 @@ class _SavedLogin extends State<SavedLogin> {
body: SafeArea(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 24.h),
child: Column(
child: appState.getSelectDeviceByImeiRespModelElement != null ? Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Spacer(flex: 2),
// Welcome back text
LocaleKeys.welcomeBack.tr().toText16(color: AppColors.inputLabelTextColor),
SizedBox(height: 16.h),
appState.getSelectDeviceByImeiRespModelElement!.name!.toCamelCase.toText26(isBold: true, height: 26 / 36, color: AppColors.textColor),
appState.getSelectDeviceByImeiRespModelElement != null
? appState.getSelectDeviceByImeiRespModelElement!.name!.toCamelCase.toText26(isBold: true, height: 26 / 36, color: AppColors.textColor)
: SizedBox(),
SizedBox(height: 24.h),
Container(
padding: EdgeInsets.all(16.h),
@ -89,15 +91,22 @@ class _SavedLogin extends State<SavedLogin> {
// Last login info
("${LocaleKeys.lastLoginBy.tr()} ${loginType.displayName}").toText14(isBold: true, color: AppColors.greyTextColor, letterSpacing: -1),
(appState.getSelectDeviceByImeiRespModelElement!.createdOn != null
appState.getSelectDeviceByImeiRespModelElement != null
? (appState.getSelectDeviceByImeiRespModelElement!.createdOn != null
? DateUtil.getFormattedDate(DateUtil.convertStringToDate(appState.getSelectDeviceByImeiRespModelElement!.createdOn!), "d MMMM, y 'at' HH:mm")
: '--')
.toText16(isBold: true, color: AppColors.textColor),
.toText16(isBold: true, color: AppColors.textColor)
: SizedBox(),
Container(
appState.getSelectDeviceByImeiRespModelElement != null
? Container(
margin: EdgeInsets.all(16.h),
child: Utils.buildSvgWithAssets(
icon: getTypeIcons(appState.getSelectDeviceByImeiRespModelElement!.logInType!), height: 54, width: 54, iconColor: loginType.toInt == 4 ? null : AppColors.primaryRedColor)),
icon: getTypeIcons(appState.getSelectDeviceByImeiRespModelElement!.logInType!),
height: 54,
width: 54,
iconColor: loginType.toInt == 4 ? null : AppColors.primaryRedColor))
: SizedBox(),
// Face ID login button
SizedBox(
height: 45,
@ -110,8 +119,8 @@ class _SavedLogin extends State<SavedLogin> {
authVm.checkUserAuthentication(otpTypeEnum: loginType == LoginTypeEnum.sms ? OTPTypeEnum.sms : OTPTypeEnum.whatsapp);
}
},
backgroundColor: Color(0xffED1C2B),
borderColor: Color(0xffFEE9EA),
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: Colors.white,
fontSize: 12,
fontWeight: FontWeight.w500,
@ -297,7 +306,7 @@ class _SavedLogin extends State<SavedLogin> {
),
const SizedBox(height: 20),
],
),
) : SizedBox.shrink(),
),
),
);

@ -14,15 +14,14 @@ import 'package:hmg_patient_app_new/features/my_appointments/appointment_via_reg
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/doctor_list_api_response.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/faculity_selection/facility_type_selection_widget.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/region_bottomsheet/region_list_widget.dart'
show RegionBottomSheetBody;
import 'package:hmg_patient_app_new/presentation/appointments/widgets/region_bottomsheet/region_list_widget.dart' show RegionBottomSheetBody;
import 'package:hmg_patient_app_new/presentation/book_appointment/search_doctor_by_name.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/select_clinic_page.dart';
import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'
show showCommonBottomSheetWithoutHeight;
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart' show showCommonBottomSheetWithoutHeight;
import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
import 'package:provider/provider.dart';
@ -44,6 +43,7 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
void initState() {
scheduleMicrotask(() {
bookAppointmentsViewModel.initBookAppointmentViewModel();
bookAppointmentsViewModel.getLocation();
});
super.initState();
}
@ -52,13 +52,12 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
Widget build(BuildContext context) {
bookAppointmentsViewModel = Provider.of<BookAppointmentsViewModel>(context, listen: false);
appState = getIt.get<AppState>();
regionalViewModel =
Provider.of<AppointmentViaRegionViewmodel>(context, listen: true);
regionalViewModel = Provider.of<AppointmentViaRegionViewmodel>(context, listen: true);
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView(
title: LocaleKeys.bookAppo.tr(context: context),
isLeading: false,
isLeading: Navigator.canPop(context),
child: SingleChildScrollView(
child: Consumer<BookAppointmentsViewModel>(builder: (context, bookAppointmentsVM, child) {
return Column(
@ -118,14 +117,15 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
),
],
),
Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 15.h, height: 15.h),
Transform.flip(
flipX: appState.isArabic() ? true : false, child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 15.h, height: 15.h)),
],
).onPress(() {
bookAppointmentsViewModel.setIsClinicsListLoading(true);
bookAppointmentsViewModel.setLoadSpecificClinic(false);
bookAppointmentsViewModel.setProjectID(null);
Navigator.of(context).push(
FadePage(
CustomPageRoute(
page: SelectClinicPage(),
),
);
@ -149,12 +149,13 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
),
],
),
Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 15.h, height: 15.h),
Transform.flip(
flipX: appState.isArabic() ? true : false, child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 15.h, height: 15.h)),
],
).onPress(() {
bookAppointmentsViewModel.setIsDoctorSearchByNameStarted(false);
Navigator.of(context).push(
FadePage(
CustomPageRoute(
page: SearchDoctorByName(),
),
);
@ -178,7 +179,8 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
),
],
),
Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 15.h, height: 15.h),
Transform.flip(
flipX: appState.isArabic() ? true : false, child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 15.h, height: 15.h)),
],
).onPress(() {
openRegionListBottomSheet(context);
@ -198,15 +200,10 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
void openRegionListBottomSheet(BuildContext context) {
regionalViewModel.flush();
// AppointmentViaRegionViewmodel? viewmodel = null;
showCommonBottomSheetWithoutHeight(context,
title: "",
titleWidget: Consumer<AppointmentViaRegionViewmodel>(
builder: (_, data, __) => getTitle(data)),
isDismissible: false,
showCommonBottomSheetWithoutHeight(context, title: "", titleWidget: Consumer<AppointmentViaRegionViewmodel>(builder: (_, data, __) => getTitle(data)), isDismissible: false,
child: Consumer<AppointmentViaRegionViewmodel>(builder: (_, data, __) {
return getRegionalSelectionWidget(data);
}), callBackFunc: () {
});
}), callBackFunc: () {});
}
Widget getRegionalSelectionWidget(AppointmentViaRegionViewmodel data) {
@ -215,7 +212,9 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
}
if (data.bottomSheetState == AppointmentViaRegionState.TYPE_SELECTION) {
bookAppointmentsViewModel.resetFilterList();
return FacilityTypeSelectionWidget(selectedRegion: data.selectedRegionId??"",);
return FacilityTypeSelectionWidget(
selectedRegion: data.selectedRegionId ?? "",
);
}
if (data.bottomSheetState == AppointmentViaRegionState.HOSPITAL_SELECTION) {
return HospitalBottomSheetBody();
@ -225,9 +224,7 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
bookAppointmentsViewModel.setIsClinicsListLoading(true);
bookAppointmentsViewModel.setLoadSpecificClinic(true);
bookAppointmentsViewModel.setProjectID(regionalViewModel.selectedHospital?.hospitalList.first?.mainProjectID.toString());
}
else {
} else {
SizedBox.shrink();
}
return SizedBox.shrink();
@ -237,9 +234,7 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
if (data.selectedRegionId == null) {
return LocaleKeys.selectRegion.tr().toText20(weight: FontWeight.w600);
} else {
return Utils.buildSvgWithAssets(
icon: AppAssets.arrow_back, iconColor: Color(0xff2B353E))
.onPress(() {
return Utils.buildSvgWithAssets(icon: AppAssets.arrow_back, iconColor: Color(0xff2B353E)).onPress(() {
data.handleBackPress();
});
}

@ -16,6 +16,7 @@ import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
import 'package:provider/provider.dart';
@ -214,7 +215,7 @@ class _ReviewAppointmentPageState extends State<ReviewAppointmentPage> {
LoadingUtils.hideFullScreenLoader();
Navigator.pushAndRemoveUntil(
context,
FadePage(
CustomPageRoute(
page: LandingNavigation(),
),
(r) => false);

@ -18,6 +18,7 @@ import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/input_widget.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
import 'package:provider/provider.dart';
@ -122,7 +123,7 @@ class _SearchDoctorByNameState extends State<SearchDoctorByName> {
await bookAppointmentsVM.getDoctorProfile(onSuccess: (dynamic respData) {
LoaderBottomSheet.hideLoader();
Navigator.of(context).push(
FadePage(
CustomPageRoute(
page: DoctorProfilePage(),
),
);

@ -19,6 +19,7 @@ import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/clinic
import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/input_widget.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
import 'package:provider/provider.dart';
@ -141,13 +142,13 @@ class _SelectClinicPageState extends State<SelectClinicPage> {
bookAppointmentsViewModel.setIsDoctorsListLoading(true);
if (clinic.isLiveCareClinicAndOnline ?? false) {
Navigator.of(context).push(
FadePage(
CustomPageRoute(
page: SelectLivecareClinicPage(),
),
);
} else {
Navigator.of(context).push(
FadePage(
CustomPageRoute(
page: SelectDoctorPage(),
),
);

@ -20,6 +20,7 @@ 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/input_widget.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
import 'package:provider/provider.dart';
@ -124,7 +125,7 @@ class _SelectDoctorPageState extends State<SelectDoctorPage> {
await bookAppointmentsVM.getDoctorProfile(onSuccess: (dynamic respData) {
LoaderBottomSheet.hideLoader();
Navigator.of(context).push(
FadePage(
CustomPageRoute(
page: DoctorProfilePage(),
),
);

@ -10,6 +10,7 @@ import 'package:hmg_patient_app_new/presentation/book_appointment/select_doctor_
import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
class SelectLivecareClinicPage extends StatelessWidget {
@ -122,7 +123,7 @@ class SelectLivecareClinicPage extends StatelessWidget {
onPressed: () {
Navigator.of(context).pop();
Navigator.of(context).push(
FadePage(
CustomPageRoute(
page: SelectDoctorPage(),
),
);

@ -19,6 +19,7 @@ import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/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/transitions/fade_page.dart';
import 'package:lottie/lottie.dart';
import 'package:provider/provider.dart';
@ -106,7 +107,7 @@ class _AppointmentCalendarState extends State<AppointmentCalendar> {
),
view: CalendarView.month,
todayHighlightColor: Colors.transparent,
todayTextStyle: TextStyle(color: AppColors.textColor),
todayTextStyle: TextStyle(color: AppColors.textColor, fontWeight: FontWeight.bold),
selectionDecoration: ShapeDecoration(
color: AppColors.transparent,
shape: SmoothRectangleBorder(
@ -151,8 +152,8 @@ class _AppointmentCalendarState extends State<AppointmentCalendar> {
child: Wrap(
direction: Axis.horizontal,
alignment: WrapAlignment.start,
spacing: 8.h,
runSpacing: 8.h,
spacing: 6.h,
runSpacing: 6.h,
children: List.generate(
dayEvents.length, // Generate a large number of items to ensure scrolling
(index) => TimeSlotChip(
@ -177,7 +178,7 @@ class _AppointmentCalendarState extends State<AppointmentCalendar> {
bookAppointmentsViewModel.setSelectedAppointmentDateTime(selectedDate, selectedTime);
Navigator.of(context).pop();
Navigator.of(context).push(
FadePage(
CustomPageRoute(
page: ReviewAppointmentPage(),
),
);
@ -216,7 +217,7 @@ class _AppointmentCalendarState extends State<AppointmentCalendar> {
Navigator.of(context).pop();
Navigator.pushAndRemoveUntil(
context,
FadePage(
CustomPageRoute(
page: LandingNavigation(),
),
(r) => false);
@ -320,7 +321,7 @@ class TimeSlotChip extends StatelessWidget {
return GestureDetector(
onTap: onTap,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 18.h, vertical: 8.h),
padding: EdgeInsets.symmetric(horizontal: 14.h, vertical: 8.h),
decoration: ShapeDecoration(
color: AppColors.whiteColor,
shape: SmoothRectangleBorder(

@ -1,5 +1,7 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
@ -15,6 +17,7 @@ class ClinicCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
AppState appState = getIt.get<AppState>();
return Container(
padding: EdgeInsets.all(16.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
@ -33,7 +36,9 @@ class ClinicCard extends StatelessWidget {
SizedBox(height: 16.h),
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
Expanded(child: (isLoading ? "Cardiology" : clinicsListResponseModel.clinicDescription!).toText16(isBold: true).toShimmer2(isShow: isLoading)),
Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, width: 15.h, height: 15.h, fit: BoxFit.contain, iconColor: AppColors.textColor).toShimmer2(isShow: isLoading),
Transform.flip(
flipX: appState.isArabic() ? true : false,
child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, width: 15.h, height: 15.h, fit: BoxFit.contain, iconColor: AppColors.textColor).toShimmer2(isShow: isLoading)),
]),
],
),

@ -13,7 +13,7 @@ import 'package:hmg_patient_app_new/presentation/habib_wallet/recharge_wallet_pa
import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:provider/provider.dart';
class HabibWalletPage extends StatefulWidget {
@ -24,8 +24,11 @@ class HabibWalletPage extends StatefulWidget {
}
class _HabibWalletState extends State<HabibWalletPage> {
late HabibWalletViewModel habibWalletVM;
@override
Widget build(BuildContext context) {
habibWalletVM = Provider.of<HabibWalletViewModel>(context, listen: false);
AppState _appState = getIt.get<AppState>();
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
@ -65,10 +68,10 @@ class _HabibWalletState extends State<HabibWalletPage> {
),
Spacer(),
LocaleKeys.balanceAmount.tr(context: context).toText14(weight: FontWeight.w500, color: AppColors.whiteColor),
SizedBox(height: 4.h),
Consumer<HabibWalletViewModel>(builder: (context, habibWalletVM, child) {
return Utils.getPaymentAmountWithSymbol(habibWalletVM.habibWalletAmount.toString().toText32(isBold: true, color: AppColors.whiteColor), AppColors.whiteColor, 13.h,
isExpanded: false)
.toShimmer2(isShow: habibWalletVM.isWalletAmountLoading, radius: 12.h, width: 80.h, height: 40.h);
return Utils.getPaymentAmountWithSymbol2(habibWalletVM.habibWalletAmount, textColor: AppColors.whiteColor, iconColor: AppColors.whiteColor, iconSize: 13, isExpanded: false)
.toShimmer2(isShow: habibWalletVM.isWalletAmountLoading, radius: 12.h, width: 80.h, height: 24.h);
}),
],
),
@ -83,11 +86,15 @@ class _HabibWalletState extends State<HabibWalletPage> {
iconSize: 21.h,
text: "Recharge".needTranslation,
onPressed: () {
Navigator.of(context).push(
FadePage(
Navigator.of(context)
.push(
CustomPageRoute(
page: RechargeWalletPage(),
),
);
)
.then((val) {
habibWalletVM.getPatientBalanceAmount();
});
},
backgroundColor: AppColors.infoColor,
borderColor: AppColors.infoColor,
@ -95,7 +102,7 @@ class _HabibWalletState extends State<HabibWalletPage> {
fontSize: 14,
fontWeight: FontWeight.bold,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
// padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
),
],

@ -1,5 +1,6 @@
import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:family_bottom_sheet/family_bottom_sheet.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
@ -8,12 +9,17 @@ 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/habib_wallet/habib_wallet_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/habib_wallet/wallet_payment_confirm_page.dart';
import 'package:hmg_patient_app_new/presentation/habib_wallet/widgets/select_hospital_bottom_sheet.dart';
import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/input_widget.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:provider/provider.dart';
import 'widgets/select-medical_file.dart';
@ -27,9 +33,25 @@ class RechargeWalletPage extends StatefulWidget {
class _RechargeWalletPageState extends State<RechargeWalletPage> {
FocusNode textFocusNode = FocusNode();
late HabibWalletViewModel habibWalletVM;
late AppState appState;
final TextEditingController amountTextController = TextEditingController();
@override
void initState() {
scheduleMicrotask(() {
habibWalletVM.setDepositorDetails(appState.getAuthenticatedUser()!.patientId.toString(), "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}",
appState.getAuthenticatedUser()!.mobileNumber!);
habibWalletVM.setSelectedRechargeType(0);
habibWalletVM.getProjectsList();
});
super.initState();
}
@override
Widget build(BuildContext context) {
AppState appState = getIt.get<AppState>();
habibWalletVM = Provider.of<HabibWalletViewModel>(context, listen: false);
appState = getIt.get<AppState>();
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: Column(
@ -57,6 +79,7 @@ class _RechargeWalletPageState extends State<RechargeWalletPage> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
//TODO: Check with hussain to show AED or SAR
"Enter an amount".needTranslation.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500),
Spacer(),
Row(
@ -66,6 +89,7 @@ class _RechargeWalletPageState extends State<RechargeWalletPage> {
SizedBox(
width: 150.h,
child: TextInputWidget(
controller: amountTextController,
labelText: "",
hintText: "",
isEnable: true,
@ -78,6 +102,7 @@ class _RechargeWalletPageState extends State<RechargeWalletPage> {
padding: EdgeInsets.symmetric(horizontal: 8.h, vertical: 0.h),
focusNode: textFocusNode,
isWalletAmountInput: true,
keyboardType: TextInputType.numberWithOptions(signed: false, decimal: true),
// leadingIcon: AppAssets.student_card,
),
),
@ -93,7 +118,8 @@ class _RechargeWalletPageState extends State<RechargeWalletPage> {
),
),
SizedBox(height: 24.h),
Container(
Consumer<HabibWalletViewModel>(builder: (context, habibWalletVM, child) {
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
@ -114,8 +140,8 @@ class _RechargeWalletPageState extends State<RechargeWalletPage> {
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.myAccount.tr(context: context).toText16(color: AppColors.textColor, weight: FontWeight.w500),
"${LocaleKeys.medicalFile.tr(context: context)}: ${appState.getAuthenticatedUser()!.patientId}"
(habibWalletVM.getSelectedRechargeTypeValue()).toText16(color: AppColors.textColor, weight: FontWeight.w500),
"${LocaleKeys.medicalFile.tr(context: context)}: ${habibWalletVM.fileNumber}"
.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500),
],
),
@ -124,26 +150,20 @@ class _RechargeWalletPageState extends State<RechargeWalletPage> {
Utils.buildSvgWithAssets(icon: AppAssets.arrow_down, width: 25.h, height: 25.h),
],
).onPress(() async {
// showCommonBottomSheetWithoutHeight(context, title: "Select Medical File".needTranslation, child: SelectMedicalFile(), callBackFunc: () {}, isFullScreen: false);
// showCommonBottomSheetWithoutHeight(context, title: "Select Medical File".needTranslation, child: const MultiPageBottomSheet(), callBackFunc: () {}, isFullScreen: false);
await FamilyModalSheet.show<void>(
context: context,
contentBackgroundColor: AppColors.scaffoldBgColor,
backgroundColor: AppColors.bottomSheetBgColor,
mainContentPadding: EdgeInsets.all(24.h),
isScrollControlled: true,
safeAreaMinimum: EdgeInsets.zero,
useSafeArea: false,
sheetAnimationStyle: AnimationStyle(
duration: Duration(milliseconds: 500), // Custom animation duration
reverseDuration: Duration(milliseconds: 300), // Custom reverse animation duration
),
builder: (ctx) {
return const MultiPageBottomSheet();
},
// Optional configurations
);
habibWalletVM.setCurrentIndex(0);
showCommonBottomSheetWithoutHeight(context, title: "Select Medical File".needTranslation,
titleWidget: Consumer<HabibWalletViewModel>(builder: (context, habibWalletVM, child) {
return habibWalletVM.currentIndex != 0
? IconButton(
icon: Utils.buildSvgWithAssets(icon: AppAssets.arrow_back, width: 24.h, height: 24.h),
padding: EdgeInsets.zero,
onPressed: () => habibWalletVM.setCurrentIndex(0),
highlightColor: Colors.transparent,
)
: "Select Medical File".needTranslation.toText20(weight: FontWeight.w600);
}), child: Consumer<HabibWalletViewModel>(builder: (context, habibWalletVM, child) {
return MultiPageBottomSheet();
}), callBackFunc: () {}, isFullScreen: false, isCloseButtonVisible: true);
}),
SizedBox(height: 16.h),
Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h),
@ -159,17 +179,27 @@ class _RechargeWalletPageState extends State<RechargeWalletPage> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.hospital.tr(context: context).toText16(color: AppColors.textColor, weight: FontWeight.w500),
"Olaya".toText14(color: AppColors.greyTextColor, weight: FontWeight.w500),
],
SizedBox(
width: MediaQuery.of(context).size.width * 0.55,
child: (habibWalletVM.selectedHospital != null ? habibWalletVM.selectedHospital!.name : LocaleKeys.selectHospital.tr(context: context))!
.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500),
),
],
),
],
).onPress(() {
showCommonBottomSheetWithoutHeight(context,
title: LocaleKeys.selectHospital.tr(context: context), isDismissible: false, child: SelectHospitalBottomSheet(), callBackFunc: () {});
}),
Utils.buildSvgWithAssets(icon: AppAssets.arrow_down, width: 25.h, height: 25.h),
],
),
SizedBox(height: 16.h),
Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h),
SizedBox(height: 16.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Utils.buildSvgWithAssets(icon: AppAssets.email_icon, width: 40.h, height: 40.h),
@ -183,6 +213,8 @@ class _RechargeWalletPageState extends State<RechargeWalletPage> {
),
],
),
],
),
SizedBox(height: 16.h),
Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h),
SizedBox(height: 16.h),
@ -208,12 +240,15 @@ class _RechargeWalletPageState extends State<RechargeWalletPage> {
],
),
),
),
);
}),
],
),
),
),
)),
),
),
//TODO: Handle Family member selection & Other Account Selection
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
@ -222,7 +257,38 @@ class _RechargeWalletPageState extends State<RechargeWalletPage> {
),
child: CustomButton(
text: LocaleKeys.next.tr(context: context),
onPressed: () {},
onPressed: () {
if (amountTextController.text.isEmpty) {
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: "Please enter amount to continue.".needTranslation),
callBackFunc: () {
textFocusNode.requestFocus();
},
isFullScreen: false,
isCloseButtonVisible: true,
);
} else if (habibWalletVM.selectedHospital == null) {
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: "Please select hospital to continue.".needTranslation),
callBackFunc: () {
textFocusNode.requestFocus();
},
isFullScreen: false,
isCloseButtonVisible: true,
);
} else {
habibWalletVM.setWalletRechargeAmount(num.parse(amountTextController.text));
// habibWalletVM.setDepositorDetails(appState.getAuthenticatedUser()!.patientId.toString(), "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}",
// appState.getAuthenticatedUser()!.mobileNumber!);
Navigator.of(context).push(
CustomPageRoute(
page: WalletPaymentConfirmPage(),
),
);
}
},
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor,

@ -0,0 +1,430 @@
import 'dart:async';
import 'dart:developer';
import 'dart:io';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/cache_consts.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/utils/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/habib_wallet/habib_wallet_view_model.dart';
import 'package:hmg_patient_app_new/features/payfort/models/apple_pay_request_insert_model.dart';
import 'package:hmg_patient_app_new/features/payfort/payfort_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/theme/colors.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/in_app_browser/InAppBrowser.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:provider/provider.dart';
class WalletPaymentConfirmPage extends StatefulWidget {
const WalletPaymentConfirmPage({super.key});
@override
State<WalletPaymentConfirmPage> createState() => _WalletPaymentConfirmPageState();
}
class _WalletPaymentConfirmPageState extends State<WalletPaymentConfirmPage> {
late PayfortViewModel payfortViewModel;
late AppState appState;
late HabibWalletViewModel habibWalletVM;
MyInAppBrowser? browser;
String selectedPaymentMethod = "";
String transID = "";
@override
void initState() {
scheduleMicrotask(() {
payfortViewModel.initPayfortViewModel();
});
super.initState();
}
@override
Widget build(BuildContext context) {
appState = getIt.get<AppState>();
habibWalletVM = Provider.of<HabibWalletViewModel>(context, listen: false);
payfortViewModel = Provider.of<PayfortViewModel>(context, listen: false);
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: CollapsingListView(
title: "Select Payment Method",
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 24.h),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset(AppAssets.mada, width: 72.h, height: 25.h).toShimmer2(isShow: false),
SizedBox(height: 16.h),
"Mada".needTranslation.toText16(isBold: true).toShimmer2(isShow: false),
],
),
SizedBox(width: 8.h),
const Spacer(),
Transform.flip(
flipX: appState.isArabic() ? true : false,
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon,
iconColor: AppColors.blackColor,
width: 18.h,
height: 13.h,
fit: BoxFit.contain,
).toShimmer2(isShow: false),
),
],
).paddingSymmetrical(16.h, 16.h),
).paddingSymmetrical(24.h, 0.h).onPress(() {
selectedPaymentMethod = "MADA";
openPaymentURL("mada");
}),
SizedBox(height: 16.h),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Image.asset(AppAssets.visa, width: 40.h, height: 40.h),
SizedBox(width: 8.h),
Image.asset(AppAssets.Mastercard, width: 40.h, height: 40.h),
],
).toShimmer2(isShow: false),
SizedBox(height: 16.h),
"Visa or Mastercard".needTranslation.toText16(isBold: true).toShimmer2(isShow: false),
],
),
SizedBox(width: 8.h),
const Spacer(),
Transform.flip(
flipX: appState.isArabic() ? true : false,
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon,
iconColor: AppColors.blackColor,
width: 18.h,
height: 13.h,
fit: BoxFit.contain,
).toShimmer2(isShow: false),
),
],
).paddingSymmetrical(16.h, 16.h),
).paddingSymmetrical(24.h, 0.h).onPress(() {
selectedPaymentMethod = "VISA";
openPaymentURL("visa");
}),
],
),
),
),
),
Container(
// height: 200.h,
width: MediaQuery.of(context).size.width,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: true,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 24.h),
habibWalletVM.depositorName.toText18(isBold: true).paddingSymmetrical(24.h, 0.h),
SizedBox(height: 12.h),
Wrap(
direction: Axis.horizontal,
spacing: 4.h,
runSpacing: 4.h,
children: [
AppCustomChipWidget(labelText: "${LocaleKeys.fileno.tr(context: context)}.: ${habibWalletVM.fileNumber}"),
AppCustomChipWidget(labelText: "${LocaleKeys.mobileNumber.tr(context: context)}: ${habibWalletVM.mobileNumber}"),
AppCustomChipWidget(labelText: "${habibWalletVM.selectedHospital!.name}"),
],
).paddingSymmetrical(24.h, 0.h),
SizedBox(height: 16.h),
Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h).paddingSymmetrical(24.h, 0.h),
SizedBox(height: 16.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"Total amount to pay".needTranslation.toText16(isBold: true),
Utils.getPaymentAmountWithSymbol(habibWalletVM.walletRechargeAmount.toString().toText24(isBold: true), AppColors.blackColor, 15.h, isSaudiCurrency: true),
],
).paddingSymmetrical(24.h, 0.h),
SizedBox(height: 12.h),
Platform.isIOS
? Utils.buildSvgWithAssets(
icon: AppAssets.apple_pay_button,
width: 200.h,
height: 56.h,
fit: BoxFit.contain,
).paddingSymmetrical(24.h, 0.h).onPress(() {
if (Utils.havePrivilege(103)) {
startApplePay();
} else {
openPaymentURL("ApplePay");
}
})
: SizedBox(height: 12.h),
SizedBox(height: 32.h),
],
),
),
],
),
);
}
startApplePay() async {
LoaderBottomSheet.showLoader();
ApplePayInsertRequest applePayInsertRequest = ApplePayInsertRequest();
transID = Utils.getAdvancePaymentTransID(habibWalletVM.selectedHospital!.iD!, int.parse(habibWalletVM.fileNumber));
await payfortViewModel.getPayfortConfigurations(serviceId: ServiceTypeEnum.advancePayment.getIdFromServiceEnum(), projectId: habibWalletVM.selectedHospital!.iD!, integrationId: 2);
applePayInsertRequest.clientRequestID = transID;
applePayInsertRequest.clinicID = 0;
applePayInsertRequest.currency = appState.getAuthenticatedUser()!.outSa! == 0 ? "SAR" : "AED";
applePayInsertRequest.customerEmail = "CustID_${habibWalletVM.fileNumber.toString()}@HMG.com";
applePayInsertRequest.customerID = habibWalletVM.fileNumber.toString();
applePayInsertRequest.customerName = "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}";
applePayInsertRequest.deviceToken = await Utils.getStringFromPrefs(CacheConst.pushToken);
applePayInsertRequest.voipToken = await Utils.getStringFromPrefs(CacheConst.voipToken);
applePayInsertRequest.doctorID = 0;
applePayInsertRequest.projectID = habibWalletVM.selectedHospital!.iD!.toString();
applePayInsertRequest.serviceID = ServiceTypeEnum.appointmentPayment.getIdFromServiceEnum().toString();
applePayInsertRequest.channelID = 3;
applePayInsertRequest.patientID = habibWalletVM.fileNumber.toString();
applePayInsertRequest.patientTypeID = appState.getAuthenticatedUser()!.patientType;
applePayInsertRequest.patientOutSA = appState.getAuthenticatedUser()!.outSa;
applePayInsertRequest.appointmentDate = null;
applePayInsertRequest.appointmentNo = 0;
applePayInsertRequest.orderDescription = "Advance Payment";
applePayInsertRequest.liveServiceID = "0";
applePayInsertRequest.latitude = "0.0";
applePayInsertRequest.longitude = "0.0";
applePayInsertRequest.amount = habibWalletVM.walletRechargeAmount.toString();
applePayInsertRequest.isSchedule = "0";
applePayInsertRequest.language = appState.isArabic() ? 'ar' : 'en';
applePayInsertRequest.languageID = appState.isArabic() ? 1 : 2;
applePayInsertRequest.userName = int.parse(habibWalletVM.fileNumber);
applePayInsertRequest.responseContinueURL = "http://hmg.com/Documents/success.html";
applePayInsertRequest.backClickUrl = "http://hmg.com/Documents/success.html";
applePayInsertRequest.paymentOption = "ApplePay";
applePayInsertRequest.isMobSDK = true;
applePayInsertRequest.merchantReference = transID;
applePayInsertRequest.merchantIdentifier = payfortViewModel.payfortProjectDetailsRespModel!.merchantIdentifier;
applePayInsertRequest.commandType = "PURCHASE";
applePayInsertRequest.signature = payfortViewModel.payfortProjectDetailsRespModel!.signature;
applePayInsertRequest.accessCode = payfortViewModel.payfortProjectDetailsRespModel!.accessCode;
applePayInsertRequest.shaRequestPhrase = payfortViewModel.payfortProjectDetailsRespModel!.shaRequest;
applePayInsertRequest.shaResponsePhrase = payfortViewModel.payfortProjectDetailsRespModel!.shaResponse;
applePayInsertRequest.returnURL = "";
//TODO: Need to pass dynamic params to the Apple Pay instead of static values
await payfortViewModel.applePayRequestInsert(applePayInsertRequest: applePayInsertRequest).then((value) {
payfortViewModel.paymentWithApplePay(
customerName: "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}",
// customerEmail: projectViewModel.authenticatedUserObject.user.emailAddress,
customerEmail: "CustID_${appState.getAuthenticatedUser()!.patientId.toString()}@HMG.com",
orderDescription: "Appointment Payment",
orderAmount: double.parse(habibWalletVM.walletRechargeAmount.toString()),
merchantReference: transID,
merchantIdentifier: payfortViewModel.payfortProjectDetailsRespModel!.merchantIdentifier,
applePayAccessCode: payfortViewModel.payfortProjectDetailsRespModel!.accessCode,
applePayShaRequestPhrase: payfortViewModel.payfortProjectDetailsRespModel!.shaRequest,
currency: appState.getAuthenticatedUser()!.outSa! == 0 ? "SAR" : "AED",
onFailed: (failureResult) async {
log("failureResult: ${failureResult.message.toString()}");
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: failureResult.message.toString()),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
},
onSucceeded: (successResult) async {
log("successResult: ${successResult.responseMessage.toString()}");
selectedPaymentMethod = successResult.paymentOption ?? "VISA";
checkPaymentStatus();
},
);
});
}
void checkPaymentStatus() async {
LoaderBottomSheet.showLoader();
await payfortViewModel.checkPaymentStatus(
transactionID: transID,
onSuccess: (apiResponse) async {
print(apiResponse.data);
if (payfortViewModel.payfortCheckPaymentStatusResponseModel!.responseMessage!.toLowerCase() == "success") {
await habibWalletVM.HISCreateAdvancePayment(
paymentMethodName: selectedPaymentMethod,
paidAmount: habibWalletVM.walletRechargeAmount,
paymentReference: payfortViewModel.payfortCheckPaymentStatusResponseModel!.fortId!,
patientID: habibWalletVM.fileNumber,
projectID: habibWalletVM.selectedHospital!.iD!,
depositorName: habibWalletVM.depositorName,
onSuccess: (value) async {
await habibWalletVM.addAdvanceNumberRequest(
advanceNumber: Utils.isVidaPlusProject(habibWalletVM.selectedHospital!.iD)
? value.data['OnlineCheckInAppointments'][0]['AdvanceNumber_VP'].toString()
: value.data['OnlineCheckInAppointments'][0]['AdvanceNumber'].toString(),
paymentReference: payfortViewModel.payfortCheckPaymentStatusResponseModel!.fortId!,
onSuccess: (value) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getSuccessWidget(loadingText: "Payment Successful!".needTranslation),
callBackFunc: () {
Navigator.of(context).pop();
Navigator.of(context).pop();
},
isFullScreen: false,
isCloseButtonVisible: true,
);
},
onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: "Payment Failed - ${err}".needTranslation),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
});
},
onError: (err) {});
} else {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: "Payment Failed! Please try again.".needTranslation),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}
},
);
}
onBrowserLoadStart(String url) {
print("onBrowserLoadStart");
print(url);
if (selectedPaymentMethod == "tamara") {
if (Platform.isAndroid) {
Uri uri = new Uri.dataFromString(url);
// tamaraPaymentStatus = uri.queryParameters['status']!;
// tamaraOrderID = uri.queryParameters['AuthorizePaymentId']!;
} else {
Uri uri = new Uri.dataFromString(url);
// tamaraPaymentStatus = uri.queryParameters['paymentStatus']!;
// tamaraOrderID = uri.queryParameters['orderId']!;
}
}
// if(selectedPaymentMethod != "TAMARA") {
MyInAppBrowser.successURLS.forEach((element) {
if (url.contains(element)) {
browser?.close();
MyInAppBrowser.isPaymentDone = true;
return;
}
});
// }
// if(selectedPaymentMethod != "TAMARA") {
MyInAppBrowser.errorURLS.forEach((element) {
if (url.contains(element)) {
browser?.close();
MyInAppBrowser.isPaymentDone = false;
return;
}
});
// }
}
onBrowserExit(bool isPaymentMade) async {
print("onBrowserExit Called!!!!");
if (selectedPaymentMethod == "TAMARA") {
// checkTamaraPaymentStatus(transID!, appo);
// if (tamaraPaymentStatus != null && tamaraPaymentStatus.toLowerCase() == "approved") {
// updateTamaraRequestStatus("success", "14", Utils.getAppointmentTransID(appo.projectID, appo.clinicID, appo.appointmentNo), tamaraOrderID, num.parse(selectedInstallments), appo);
// } else {
// updateTamaraRequestStatus("Failed", "00", Utils.getAppointmentTransID(appo.projectID, appo.clinicID, appo.appointmentNo), tamaraOrderID, num.parse(selectedInstallments), appo);
// }
} else {
checkPaymentStatus();
// checkPaymentStatus(appo);
}
}
openPaymentURL(String paymentMethod) {
browser = MyInAppBrowser(onExitCallback: onBrowserExit, onLoadStartCallback: onBrowserLoadStart, context: context);
transID = Utils.getAdvancePaymentTransID(habibWalletVM.selectedHospital!.iD!, int.parse(habibWalletVM.fileNumber));
browser?.openPaymentBrowser(
habibWalletVM.walletRechargeAmount,
"Advance Payment",
transID,
habibWalletVM.selectedHospital!.iD!.toString(),
"CustID_${habibWalletVM.fileNumber.toString()}@HMG.com",
selectedPaymentMethod,
appState.getAuthenticatedUser()!.patientType.toString(),
habibWalletVM.depositorName,
habibWalletVM.fileNumber.toString(),
appState.getAuthenticatedUser()!,
browser!,
false,
"3",
"0",
context,
"",
"",
"",
"",
"3");
}
}

@ -0,0 +1,106 @@
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/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
class HospitalListItemAdvancePayment extends StatelessWidget {
final HospitalsModel hospitalModel;
final bool isLocationEnabled;
late AppState appState;
HospitalListItemAdvancePayment({super.key, required this.hospitalModel, required this.isLocationEnabled});
@override
Widget build(BuildContext context) {
appState = getIt.get<AppState>();
return DecoratedBox(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 8.h,
children: [hospitalName],
),
),
Transform.flip(
flipX: appState.isArabic() ? true : false,
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon,
iconColor: AppColors.blackColor,
width: 18,
height: 13,
fit: BoxFit.contain,
),
),
],
).paddingSymmetrical(16.h, 16.h),
);
}
Widget get hospitalName => Row(
children: [
Utils.buildSvgWithAssets(
icon: (hospitalModel.isHMC == true) ? AppAssets.hmc : AppAssets.hmg,
).paddingOnly(right: 10),
Expanded(
child: Text(
hospitalModel.name ?? "",
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 16,
color: AppColors.blackColor,
),
),
)
],
);
// Widget get distanceInfo => Row(
// children: [
// Visibility(
// visible: (hospitalModel.distanceInKMs != "0"),
// child: AppCustomChipWidget(
// labelText: "${hospitalData?.distanceInKMs ?? ""} km".needTranslation,
// deleteIcon: AppAssets.location_red,
// deleteIconSize: Size(9, 12),
// backgroundColor: AppColors.secondaryLightRedColor,
// textColor: AppColors.errorColor,
// ),
// ),
// Visibility(
// visible: (hospitalData?.distanceInKMs == "0"),
// child: Row(
// children: [
// AppCustomChipWidget(
// labelText: "Distance not available".needTranslation,
// textColor: AppColors.blackColor,
// ),
// SizedBox(
// width: 8.h,
// )
// ],
// )),
// Visibility(
// visible: !isLocationEnabled,
// child: AppCustomChipWidget(
// labelText: "Location turned off".needTranslation,
// deleteIcon: AppAssets.location_unavailable,
// deleteIconSize: Size(9, 12),
// textColor: AppColors.blackColor,
// )),
// ],
// );
}

@ -27,8 +27,13 @@ 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/habib_wallet/habib_wallet_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/services/dialog_service.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/input_widget.dart';
import 'package:provider/provider.dart';
class MultiPageBottomSheet extends StatefulWidget {
const MultiPageBottomSheet({Key? key}) : super(key: key);
@ -39,24 +44,87 @@ class MultiPageBottomSheet extends StatefulWidget {
class _MultiPageBottomSheetState extends State<MultiPageBottomSheet> {
late AppState appState;
static final DialogService _dialogService = getIt.get<DialogService>();
TextEditingController fileNumberEditingController = TextEditingController();
@override
Widget build(BuildContext context) {
appState = getIt.get<AppState>();
return SizedBox(
height: MediaQuery.of(context).size.height * 0.38,
child: Column(
return Consumer<HabibWalletViewModel>(builder: (context, habibWalletVM, child) {
return Padding(
padding: MediaQuery.of(context).viewInsets,
child: habibWalletVM.isBottomSheetContentLoading ? Utils.getLoadingWidget() : getCurrentIndexWidget(habibWalletVM),
);
});
}
Widget getCurrentIndexWidget(HabibWalletViewModel habibWalletVM) {
switch (habibWalletVM.currentIndex) {
case 0:
return getSelectMedicalFileContent(habibWalletVM);
case 1:
return getOtherAccountContent(habibWalletVM);
case 2:
return getOtherAccountContent(habibWalletVM);
default:
return getSelectMedicalFileContent(habibWalletVM);
}
}
Widget getOtherAccountContent(HabibWalletViewModel habibWalletVM) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
"Select Medical File".toText20(weight: FontWeight.w600).expanded,
Utils.buildSvgWithAssets(icon: AppAssets.close_bottom_sheet_icon, iconColor: Color(0xff2B353E)).onPress(() {
"Enter File Number".needTranslation.toText20(weight: FontWeight.w600),
SizedBox(height: 12.h),
TextInputWidget(
labelText: LocaleKeys.fileNumber.tr(),
hintText: "xxxxxxxxx",
controller: fileNumberEditingController,
isEnable: true,
prefix: null,
isAllowRadius: true,
isBorderAllowed: false,
isAllowLeadingIcon: true,
autoFocus: false,
padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 8.h),
leadingIcon: AppAssets.requests,
).withVerticalPadding(8),
SizedBox(height: 12.h),
CustomButton(
text: LocaleKeys.submit.tr(),
onPressed: () async {
await habibWalletVM.getPatientInfoByPatientID(
patientID: fileNumberEditingController.text,
onSuccess: (response) async {
print(response.data["GetPatientInfoByPatientIDList"][0]["FullName"]);
await _dialogService.showCommonBottomSheetWithoutH(
message: "A file was found with name: ${response.data["GetPatientInfoByPatientIDList"][0]["FullName"]}, Would you like to recharge wallet for this file number?".needTranslation,
label: LocaleKeys.notice.tr(),
onOkPressed: () {
habibWalletVM.setSelectedRechargeType(3);
habibWalletVM.setDepositorDetails(response.data["GetPatientInfoByPatientIDList"][0]["PatientID"].toString(), response.data["GetPatientInfoByPatientIDList"][0]["FullName"],
response.data["GetPatientInfoByPatientIDList"][0]["MobileNumber"]);
Navigator.of(context).pop();
}),
],
Navigator.of(context).pop();
},
onCancelPressed: () {});
},
onError: (error) {});
},
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedBorderColor,
textColor: AppColors.whiteColor,
),
],
);
}
Widget getSelectMedicalFileContent(HabibWalletViewModel habibWalletVM) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
@ -120,9 +188,10 @@ class _MultiPageBottomSheetState extends State<MultiPageBottomSheet> {
Utils.buildSvgWithAssets(icon: AppAssets.forward_chevron_icon, iconColor: AppColors.textColor, width: 15.h, height: 15.h),
],
).paddingAll(16.h),
).onPress(() {}),
).onPress(() {
habibWalletVM.setCurrentIndex(2);
}),
],
),
);
}
}

@ -0,0 +1,96 @@
import 'package:easy_localization/easy_localization.dart' show tr, StringTranslateExtension;
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/appointment_via_region_viewmodel.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/facility_selection.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/hospital_bottom_sheet/hospital_list_items.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/hospital_bottom_sheet/type_selection_widget.dart';
import 'package:hmg_patient_app_new/presentation/habib_wallet/widgets/hospital_list_item.dart';
import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/theme/colors.dart' show AppColors;
import 'package:hmg_patient_app_new/widgets/input_widget.dart';
import 'package:provider/provider.dart';
class SelectHospitalBottomSheet extends StatelessWidget {
late HabibWalletViewModel habibWalletVM;
final TextEditingController searchText = TextEditingController();
SelectHospitalBottomSheet({super.key});
@override
Widget build(BuildContext context) {
habibWalletVM = Provider.of<HabibWalletViewModel>(context, listen: false);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Text(
// LocaleKeys.selectHospital.tr(),
// style: TextStyle(
// fontSize: 21,
// fontWeight: FontWeight.w600,
// color: AppColors.blackColor,
// ),
// ),
Text(
"Please select the hospital you want to make an advance payment for.".needTranslation,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: AppColors.greyTextColor,
),
),
SizedBox(height: 16.h),
// TextInputWidget(
// labelText: LocaleKeys.search.tr(),
// hintText: "Search Hospital".tr(),
// controller: searchText,
// onChange: (value) {
// // appointmentsViewModel.filterHospitalListByString(value, regionalViewModel.selectedRegionId , regionalViewModel.selectedFacilityType ==
// // FacilitySelection.HMG.name);
// },
// isEnable: true,
// prefix: null,
// autoFocus: false,
// isBorderAllowed: false,
// keyboardType: TextInputType.text,
// isAllowLeadingIcon: true,
// selectionType: SelectionTypeEnum.search,
// padding: EdgeInsets.symmetric(
// vertical: ResponsiveExtension(10).h,
// horizontal: ResponsiveExtension(15).h,
// ),
// ),
// SizedBox(height: 24.h),
// TypeSelectionWidget(
// hmcCount: "0",
// hmgCount: "0",
// ),
// SizedBox(height: 21.h),
SizedBox(
height: MediaQuery.sizeOf(context).height * .4,
child: ListView.separated(
itemBuilder: (_, index) {
return HospitalListItemAdvancePayment(
hospitalModel: habibWalletVM.advancePaymentHospitals[index],
isLocationEnabled: false,
).onPress(() {
habibWalletVM.setSelectedHospital(habibWalletVM.advancePaymentHospitals[index]);
Navigator.of(context).pop();
});
},
separatorBuilder: (_, __) => SizedBox(
height: 16.h,
),
itemCount: habibWalletVM.advancePaymentHospitals.length),
)
],
);
}
}

@ -80,7 +80,7 @@ class LandingPageData {
),
ServiceCardData(
serviceName: "lab_results",
icon: AppAssets.lab_result_icon,
icon: AppAssets.home_lab_result_icon,
title: "My Lab",
subtitle: "Results",
backgroundColor: AppColors.whiteColor,
@ -90,7 +90,7 @@ class LandingPageData {
),
ServiceCardData(
serviceName: "radiology_results",
icon: AppAssets.lab_result_icon,
icon: AppAssets.home_lab_result_icon,
title: "My Radiology",
subtitle: "Results",
backgroundColor: AppColors.whiteColor,

@ -3,6 +3,7 @@ import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:flutter_swiper_view/flutter_swiper_view.dart';
import 'package:get_it/get_it.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
@ -15,9 +16,12 @@ 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/authentication/authentication_view_model.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/my_appointments_page.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_card.dart';
import 'package:hmg_patient_app_new/presentation/authentication/quick_login.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/book_appointment_page.dart';
import 'package:hmg_patient_app_new/presentation/home/data/landing_page_data.dart';
@ -34,6 +38,7 @@ import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart' show CustomTabBar;
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:hmg_patient_app_new/widgets/routes/spring_page_route_builder.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
import 'package:provider/provider.dart';
@ -55,6 +60,8 @@ class _LandingPageState extends State<LandingPage> {
late PrescriptionsViewModel prescriptionsViewModel;
final CacheService cacheService = GetIt.instance<CacheService>();
final SwiperController _controller = SwiperController();
@override
void initState() {
authVM = context.read<AuthenticationViewModel>();
@ -123,7 +130,7 @@ class _LandingPageState extends State<LandingPage> {
children: [
Utils.buildSvgWithAssets(icon: AppAssets.bell, height: 20, width: 20).onPress(() {
Navigator.of(context).push(
FadePage(
CustomPageRoute(
page: MedicalFilePage(),
// page: LoginScreen(),
),
@ -131,7 +138,7 @@ class _LandingPageState extends State<LandingPage> {
}),
Utils.buildSvgWithAssets(icon: AppAssets.search_icon, height: 20, width: 20).onPress(() {
Navigator.of(context).push(
FadePage(
CustomPageRoute(
page: MedicalFilePage(),
// page: LoginScreen(),
),
@ -139,7 +146,7 @@ class _LandingPageState extends State<LandingPage> {
}),
Utils.buildSvgWithAssets(icon: AppAssets.contact_icon, height: 20, width: 20).onPress(() {
Navigator.of(context).push(
FadePage(
CustomPageRoute(
page: MedicalFilePage(),
// page: LoginScreen(),
),
@ -152,25 +159,95 @@ class _LandingPageState extends State<LandingPage> {
appState.isAuthenticated
? Column(
children: [
Container(
width: double.infinity,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24,
SizedBox(height: 12.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"Appointments & Visits".toText16(isBold: true),
Row(
children: [
LocaleKeys.viewAll.tr(context: context).toText12(color: AppColors.primaryRedColor),
SizedBox(width: 2.h),
Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 10.h),
],
),
],
).paddingSymmetrical(24.h, 0.h).onPress(() {
Navigator.of(context).push(
CustomPageRoute(
page: MyAppointmentsPage(),
),
);
}),
SizedBox(height: 12.h),
Consumer<MyAppointmentsViewModel>(builder: (context, myAppointmentsVM, child) {
return myAppointmentsVM.isMyAppointmentsLoading
? Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
child: AppointmentCard(
patientAppointmentHistoryResponseModel: PatientAppointmentHistoryResponseModel(),
myAppointmentsViewModel: myAppointmentsViewModel,
isLoading: true,
isFromHomePage: true,
),
).paddingSymmetrical(24.h, 0.h)
: myAppointmentsVM.patientAppointmentsHistoryList.isNotEmpty
? myAppointmentsVM.patientAppointmentsHistoryList.length == 1
? Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
child: AppointmentCard(
patientAppointmentHistoryResponseModel: myAppointmentsVM.patientAppointmentsHistoryList.first,
myAppointmentsViewModel: myAppointmentsViewModel,
isLoading: false,
isFromHomePage: true,
),
).paddingSymmetrical(24.h, 0.h)
: Swiper(
itemCount: myAppointmentsVM.isMyAppointmentsLoading
? 3
: myAppointmentsVM.patientAppointmentsHistoryList.length < 3
? myAppointmentsVM.patientAppointmentsHistoryList.length
: 3,
layout: SwiperLayout.STACK,
loop: true,
itemWidth: MediaQuery.of(context).size.width - 72,
indicatorLayout: PageIndicatorLayout.COLOR,
axisDirection: AxisDirection.right,
controller: _controller,
itemHeight: 210 + 25,
pagination: const SwiperPagination(
alignment: Alignment.bottomCenter,
margin: EdgeInsets.only(top: 210 + 8 + 24),
builder: DotSwiperPaginationBuilder(color: Color(0xffD9D9D9), activeColor: AppColors.blackBgColor),
),
itemBuilder: (BuildContext context, int index) {
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
child: AppointmentCard(
patientAppointmentHistoryResponseModel: myAppointmentsVM.patientAppointmentsHistoryList[index],
myAppointmentsViewModel: myAppointmentsViewModel,
isLoading: false,
isFromHomePage: true,
),
);
},
)
: Container(
width: double.infinity,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24, hasShadow: true),
child: Padding(
padding: EdgeInsets.all(12.h),
child: Column(
children: [
Utils.buildSvgWithAssets(icon: AppAssets.home_calendar_icon, width: 32.h, height: 32.h),
SizedBox(height: 12.h),
"You do not have any upcoming appointment. Please book an appointment".toText12(isCenter: true),
"You do not have any upcoming appointment. Please book an appointment".needTranslation.toText12(isCenter: true),
SizedBox(height: 12.h),
CustomButton(
text: LocaleKeys.bookAppo.tr(context: context),
onPressed: () {
Navigator.of(context).push(
FadePage(
CustomPageRoute(
page: BookAppointmentPage(),
),
);
@ -189,7 +266,8 @@ class _LandingPageState extends State<LandingPage> {
],
),
),
).paddingSymmetrical(24.h, 0.h),
).paddingSymmetrical(24.h, 0.h);
}),
SizedBox(height: 12.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@ -205,7 +283,7 @@ class _LandingPageState extends State<LandingPage> {
],
).paddingSymmetrical(24.h, 0.h).onPress(() {
Navigator.of(context).push(
FadePage(
CustomPageRoute(
page: MedicalFilePage(),
),
);

@ -8,6 +8,7 @@ import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_mode
import 'package:hmg_patient_app_new/presentation/habib_wallet/habib_wallet_page.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
import 'package:provider/provider.dart';
@ -98,7 +99,7 @@ class HabibWalletCard extends StatelessWidget {
],
).onPress(() {
Navigator.of(context).push(
FadePage(
CustomPageRoute(
page: HabibWalletPage(),
),
);

@ -7,6 +7,7 @@ import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.d
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';
import 'package:hmg_patient_app_new/presentation/prescriptions/prescriptions_list_page.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
import '../../../core/utils/utils.dart';
@ -61,28 +62,28 @@ class SmallServiceCard extends StatelessWidget {
switch (serviceName) {
case "lab_results":
Navigator.of(context).push(
FadePage(
CustomPageRoute(
page: LabOrdersPage(),
),
);
break;
case "radiology_results":
Navigator.of(context).push(
FadePage(
CustomPageRoute(
page: RadiologyOrdersPage(),
),
);
break;
case "prescriptions":
Navigator.of(context).push(
FadePage(
CustomPageRoute(
page: PrescriptionsListPage(),
),
);
break;
case "insurance_update":
Navigator.of(context).push(
FadePage(
CustomPageRoute(
page: InsuranceHomePage(),
),
);
@ -90,7 +91,7 @@ class SmallServiceCard extends StatelessWidget {
case "my_doctors":
Navigator.of(context).push(
FadePage(
CustomPageRoute(
page: MyDoctorsPage(),
),
);
@ -98,7 +99,7 @@ class SmallServiceCard extends StatelessWidget {
case "sick_leaves":
Navigator.of(context).push(
FadePage(
CustomPageRoute(
page: PatientSickleavesListPage(),
),
);

@ -10,6 +10,7 @@ import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package: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/shimmer/movies_shimmer_widget.dart';
import 'package:provider/provider.dart';
@ -73,7 +74,7 @@ class InsuranceHistory extends StatelessWidget {
],
),
SizedBox(height: 8.h),
"Haroon Amjad".toText16(weight: FontWeight.w600),
// "Haroon Amjad".toText16(weight: FontWeight.w600),
SizedBox(height: 8.h),
Row(
children: [
@ -82,38 +83,11 @@ class InsuranceHistory extends StatelessWidget {
spacing: 4.h,
runSpacing: 4.h,
children: [
Row(
children: [
CustomButton(
text: "File No.: 3628599",
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
),
],
AppCustomChipWidget(
labelText: "File No.: ${insuranceVM.patientInsuranceCardHistoryList[index].patientID}",
),
Row(
children: [
CustomButton(
text: insuranceVM.patientInsuranceCardHistoryList[index].createdOn!,
// text: "test",
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
),
],
AppCustomChipWidget(
labelText: insuranceVM.patientInsuranceCardHistoryList[index].createdOn!,
),
],
),

@ -5,12 +5,14 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.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/app_state.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/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import '../../core/dependencies.dart';
class CollapsingListView extends StatelessWidget {
final String title;
@ -27,6 +29,7 @@ class CollapsingListView extends StatelessWidget {
@override
Widget build(BuildContext context) {
AppState appState = getIt.get<AppState>();
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: Column(
@ -42,11 +45,14 @@ class CollapsingListView extends StatelessWidget {
surfaceTintColor: Colors.transparent,
backgroundColor: AppColors.bgScaffoldColor,
leading: isLeading
? IconButton(
? Transform.flip(
flipX: appState.isArabic() ? true : false,
child: IconButton(
icon: Utils.buildSvgWithAssets(icon: isClose ? AppAssets.closeBottomNav : AppAssets.arrow_back, width: 32.h, height: 32.h),
padding: EdgeInsets.only(left: 12),
onPressed: () => Navigator.pop(context),
highlightColor: Colors.transparent,
),
)
: SizedBox.shrink(),
flexibleSpace: LayoutBuilder(
@ -71,7 +77,7 @@ class CollapsingListView extends StatelessWidget {
t,
)!,
child: Padding(
padding: EdgeInsets.only(left: leftPadding, bottom: bottomPadding),
padding: EdgeInsets.only(left: appState.isArabic() ? 0 : leftPadding, right: appState.isArabic() ? leftPadding : 0, bottom: bottomPadding),
child: Row(
spacing: 4.h,
children: [

@ -6,6 +6,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/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/features/lab/lab_view_model.dart';
@ -15,7 +17,7 @@ import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/chip/custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:provider/provider.dart';
import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart';
import 'collapsing_list_view.dart';
class LabOrdersPage extends StatefulWidget {
@ -72,6 +74,20 @@ class _LabOrdersPageState extends State<LabOrdersPage> {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 16.h),
CustomTabBar(
activeTextColor: Color(0xffED1C2B),
activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1),
tabs: [
CustomTabBarModel(null, "By Visit".needTranslation),
CustomTabBarModel(null, "By Test".needTranslation),
// CustomTabBarModel(null, "Completed".needTranslation),
],
onTabChange: (index) {
// myAppointmentsViewModel.onTabChange(index);
},
),
SizedBox(height: 16.h),
selectedFilterText!.isNotEmpty
? CustomChipWidget(
chipText: selectedFilterText!,

@ -22,6 +22,7 @@ import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_mo
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/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/widgets/appointment_calendar.dart';
import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.dart';
import 'package:hmg_patient_app_new/presentation/insurance/widgets/patient_insurance_card.dart';
@ -40,6 +41,7 @@ import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart';
import 'package:hmg_patient_app_new/widgets/input_widget.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
import 'package:provider/provider.dart';
@ -116,6 +118,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
child: Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
@ -131,35 +134,19 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
children: [
"${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}".toText18(isBold: true),
SizedBox(height: 4.h),
Row(
Wrap(
direction: Axis.horizontal,
spacing: 4.h,
runSpacing: 4.h,
children: [
CustomButton(
AppCustomChipWidget(
icon: AppAssets.file_icon,
iconColor: AppColors.blackColor,
iconSize: 12.h,
text: "File no: ${appState.getAuthenticatedUser()!.patientId}",
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.normal,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
labelText: "${LocaleKeys.fileNo.tr(context: context)}: ${appState.getAuthenticatedUser()!.patientId}",
),
SizedBox(width: 4.h),
CustomButton(
text: LocaleKeys.verified.tr(context: context),
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.normal,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
AppCustomChipWidget(
icon: AppAssets.checkmark_icon,
labelText: LocaleKeys.verified.tr(context: context),
iconColor: AppColors.successColor,
),
],
),
@ -170,55 +157,21 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
SizedBox(height: 16.h),
Divider(color: AppColors.dividerColor, height: 1.h),
SizedBox(height: 16.h),
Row(
Wrap(
direction: Axis.horizontal,
spacing: 4.h,
runSpacing: 4.h,
children: [
CustomButton(
text: "${appState.getAuthenticatedUser()!.age} Years Old",
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.normal,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
AppCustomChipWidget(
labelText: "${appState.getAuthenticatedUser()!.age} Years Old",
),
SizedBox(width: 4.h),
CustomButton(
AppCustomChipWidget(
icon: AppAssets.blood_icon,
labelText: "${LocaleKeys.bloodType.tr(context: context)}: ${appState.getUserBloodGroup}",
iconColor: AppColors.primaryRedColor,
iconSize: 13.h,
text: "Blood: ${appState.getUserBloodGroup}",
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.normal,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
),
// SizedBox(width: 4.h),
// CustomButton(
// icon: AppAssets.insurance_active_icon,
// iconColor: AppColors.successColor,
// iconSize: 13.h,
// text: "Insurance Active",
// onPressed: () {},
// backgroundColor: AppColors.bgGreenColor.withOpacity(0.20),
// borderColor: AppColors.bgGreenColor.withOpacity(0.0),
// textColor: AppColors.blackColor,
// fontSize: 10,
// fontWeight: FontWeight.normal,
// borderRadius: 12,
// padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
// height: 30.h,
// ),
),
],
),
SizedBox(height: 8.h),
],
),
),
@ -314,7 +267,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
],
).paddingSymmetrical(24.h, 0.h).onPress(() {
Navigator.of(context).push(
FadePage(
CustomPageRoute(
page: MyAppointmentsPage(),
),
);
@ -326,7 +279,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
scrollDirection: Axis.horizontal,
padding: EdgeInsets.only(top: 16.h, left: 24.h, right: 24.h, bottom: 0.h),
shrinkWrap: true,
itemCount: myAppointmentsVM.isMyAppointmentsLoading ? 5 : myAppointmentsVM.patientAppointmentsHistoryList.length,
itemCount: myAppointmentsVM.isMyAppointmentsLoading ? 5 : (myAppointmentsVM.patientAppointmentsHistoryList.isNotEmpty ? myAppointmentsVM.patientAppointmentsHistoryList.length : 1),
itemBuilder: (context, index) {
return AnimationConfiguration.staggeredList(
position: index,
@ -344,13 +297,49 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
onRescheduleTap: () {},
onAskDoctorTap: () {},
)
: MedicalFileAppointmentCard(
: myAppointmentsVM.patientAppointmentsHistoryList.isNotEmpty
? MedicalFileAppointmentCard(
patientAppointmentHistoryResponseModel: myAppointmentsVM.patientAppointmentsHistoryList[index],
myAppointmentsViewModel: myAppointmentsViewModel,
onRescheduleTap: () {
openDoctorScheduleCalendar(myAppointmentsVM.patientAppointmentsHistoryList[index]);
},
onAskDoctorTap: () {},
)
: Container(
width: MediaQuery.of(context).size.width - 48.h,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24, hasShadow: true),
child: Padding(
padding: EdgeInsets.all(12.h),
child: Column(
children: [
Utils.buildSvgWithAssets(icon: AppAssets.home_calendar_icon, width: 32.h, height: 32.h),
SizedBox(height: 12.h),
"You do not have any appointments. Please book an appointment".needTranslation.toText12(isCenter: true),
SizedBox(height: 12.h),
CustomButton(
text: LocaleKeys.bookAppo.tr(context: context),
onPressed: () {
Navigator.of(context).push(
CustomPageRoute(
page: BookAppointmentPage(),
),
);
},
backgroundColor: Color(0xffFEE9EA),
borderColor: Color(0xffFEE9EA),
textColor: Color(0xffED1C2B),
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40,
icon: AppAssets.add_icon,
iconColor: AppColors.primaryRedColor,
),
],
),
),
),
),
),
@ -415,8 +404,8 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
children: [
Image.network(
prescriptionVM.patientPrescriptionOrders[index].doctorImageURL!,
width: 63.h,
height: 63.h,
width: 40.h,
height: 40.h,
fit: BoxFit.fill,
).circle(100),
SizedBox(width: 16.h),
@ -441,13 +430,15 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
],
),
),
SizedBox(width: 40.h),
Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, width: 15.h, height: 15.h, fit: BoxFit.contain, iconColor: AppColors.textColor),
// SizedBox(width: 40.h),
Transform.flip(
flipX: appState.isArabic() ? true : false,
child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, width: 15.h, height: 15.h, fit: BoxFit.contain, iconColor: AppColors.textColor)),
],
).onPress(() {
prescriptionVM.setPrescriptionsDetailsLoading();
Navigator.of(context).push(
FadePage(
CustomPageRoute(
page: PrescriptionDetailPage(prescriptionsResponseModel: prescriptionVM.patientPrescriptionOrders[index]),
),
);
@ -468,7 +459,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
text: "All Prescriptions".needTranslation,
onPressed: () {
Navigator.of(context).push(
FadePage(
CustomPageRoute(
page: PrescriptionsListPage(),
),
);
@ -526,7 +517,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
myAppointmentsViewModel.setIsPatientMyDoctorsLoading(true);
myAppointmentsViewModel.getPatientMyDoctors();
Navigator.of(context).push(
FadePage(
CustomPageRoute(
page: MyDoctorsPage(),
),
);
@ -631,7 +622,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
iconSize: 40.h,
).onPress(() {
Navigator.of(context).push(
FadePage(
CustomPageRoute(
page: VaccineListPage(),
),
);
@ -673,7 +664,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
iconSize: 36.h)
.onPress(() {
Navigator.of(context).push(
FadePage(
CustomPageRoute(
page: InsuranceHomePage(),
),
);
@ -747,7 +738,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
medicalFileViewModel.setIsPatientMedicalReportsLoading(true);
medicalFileViewModel.getPatientMedicalReportList();
Navigator.of(context).push(
FadePage(
CustomPageRoute(
page: MedicalReportsPage(),
),
);
@ -761,7 +752,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
iconSize: 40.h,
).onPress(() {
Navigator.of(context).push(
FadePage(
CustomPageRoute(
page: PatientSickleavesListPage(),
),
);

@ -1,5 +1,7 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
@ -15,6 +17,7 @@ class LabRadCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
AppState appState = getIt.get<AppState>();
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: false),
child: Column(
@ -49,13 +52,11 @@ class LabRadCard extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox.shrink(),
Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon,
width: 15.h,
height: 15.h,
fit: BoxFit.contain,
iconColor: AppColors.textColor
).toShimmer2(isShow: false, radius: 12.h),
Transform.flip(
flipX: appState.isArabic() ? true : false,
child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, width: 15.h, height: 15.h, fit: BoxFit.contain, iconColor: AppColors.textColor)
.toShimmer2(isShow: false, radius: 12.h),
),
],
)
],

@ -1,6 +1,8 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
@ -13,6 +15,8 @@ import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/appointment_details_page.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
class MedicalFileAppointmentCard extends StatelessWidget {
@ -26,23 +30,20 @@ class MedicalFileAppointmentCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
AppState appState = getIt.get<AppState>();
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CustomButton(
text: DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false),
onPressed: () {},
backgroundColor: AppointmentType.isArrived(patientAppointmentHistoryResponseModel) ? AppColors.greyColor : AppColors.secondaryLightRedColor,
borderColor: AppointmentType.isArrived(patientAppointmentHistoryResponseModel) ? AppColors.greyLightColor : AppColors.secondaryLightRedColor,
textColor: AppointmentType.isArrived(patientAppointmentHistoryResponseModel) ? AppColors.textColor : AppColors.primaryRedColor,
fontSize: 12,
fontWeight: FontWeight.w500,
borderRadius: 12.h,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
AppCustomChipWidget(
richText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)
.toText12(color: AppointmentType.isArrived(patientAppointmentHistoryResponseModel) ? AppColors.textColor : AppColors.primaryRedColor, fontWeight: FontWeight.w500)
.paddingOnly(left: 8.h),
icon: AppointmentType.isArrived(patientAppointmentHistoryResponseModel) ? AppAssets.appointment_calendar_icon : AppAssets.alarm_clock_icon,
iconColor: AppointmentType.isArrived(patientAppointmentHistoryResponseModel) ? AppColors.textColor : AppColors.primaryRedColor,
iconSize: 16.h,
backgroundColor: AppointmentType.isArrived(patientAppointmentHistoryResponseModel) ? AppColors.greyColor : AppColors.secondaryLightRedColor,
textColor: AppointmentType.isArrived(patientAppointmentHistoryResponseModel) ? AppColors.textColor : AppColors.primaryRedColor,
padding: EdgeInsets.only(top: 12.h, bottom: 12.h, left: 8.h, right: 8.h),
).toShimmer2(isShow: myAppointmentsViewModel.isMyAppointmentsLoading),
SizedBox(height: 16.h),
Container(
@ -86,7 +87,7 @@ class MedicalFileAppointmentCard extends StatelessWidget {
text: AppointmentType.getNextActionText(patientAppointmentHistoryResponseModel.nextAction),
onPressed: () {
Navigator.of(context)
.push(FadePage(
.push(CustomPageRoute(
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel),
))
.then((val) {
@ -119,6 +120,8 @@ class MedicalFileAppointmentCard extends StatelessWidget {
),
child: Padding(
padding: EdgeInsets.all(10.h),
child: Transform.flip(
flipX: appState.isArabic() ? true : false,
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon,
width: 10.h,
@ -126,10 +129,11 @@ class MedicalFileAppointmentCard extends StatelessWidget {
fit: BoxFit.contain,
),
),
),
).toShimmer2(isShow: myAppointmentsViewModel.isMyAppointmentsLoading).onPress(() {
Navigator.of(context)
.push(
FadePage(
CustomPageRoute(
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel),
),
)

@ -17,6 +17,7 @@ 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/routes/custom_page_route.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
import 'package:open_filex/open_filex.dart';
import 'package:provider/provider.dart';
@ -138,6 +139,8 @@ class PatientSickLeaveCard extends StatelessWidget {
),
child: Padding(
padding: EdgeInsets.all(10.h),
child: Transform.flip(
flipX: _appState.isArabic() ? true : false,
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon,
width: 10.h,
@ -145,9 +148,10 @@ class PatientSickLeaveCard extends StatelessWidget {
fit: BoxFit.contain,
),
),
),
).toShimmer2(isShow: isLoading).onPress(() {
Navigator.of(context).push(
FadePage(
CustomPageRoute(
page: PatientSickleavesListPage(),
),
);

@ -4,6 +4,8 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
@ -15,6 +17,7 @@ import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/presentation/prescriptions/prescription_detail_page.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
import 'package:provider/provider.dart';
@ -41,6 +44,7 @@ class _PrescriptionsListPageState extends State<PrescriptionsListPage> {
@override
Widget build(BuildContext context) {
AppState appState = getIt.get<AppState>();
prescriptionsViewModel = Provider.of<PrescriptionsViewModel>(context, listen: false);
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
@ -248,6 +252,8 @@ class _PrescriptionsListPageState extends State<PrescriptionsListPage> {
),
child: Padding(
padding: EdgeInsets.all(8.h),
child: Transform.flip(
flipX: appState.isArabic() ? true : false,
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon,
width: 10.h,
@ -255,10 +261,11 @@ class _PrescriptionsListPageState extends State<PrescriptionsListPage> {
fit: BoxFit.contain,
),
),
),
).onPress(() {
model.setPrescriptionsDetailsLoading();
Navigator.of(context).push(
FadePage(
CustomPageRoute(
page: PrescriptionDetailPage(prescriptionsResponseModel: prescription),
),
);

@ -100,13 +100,13 @@ class _ProfileSettingsState extends State<ProfileSettings> {
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Utils.buildSvgWithAssets(icon: AppAssets.wallet, width: 40.h, height: 40.h),
"Al Habib Wallet".needTranslation.toText14(weight: FontWeight.w600, maxlines: 2).expanded,
"Habib Wallet".needTranslation.toText14(weight: FontWeight.w600, maxlines: 2).expanded,
Utils.buildSvgWithAssets(icon: AppAssets.arrow_forward),
],
),
Spacer(),
Consumer<HabibWalletViewModel>(builder: (context, habibWalletVM, child) {
return Utils.getPaymentAmountWithSymbol2(habibWalletVM.habibWalletAmount, AppColors.whiteColor, 13.h, isExpanded: false)
return Utils.getPaymentAmountWithSymbol2(habibWalletVM.habibWalletAmount, isExpanded: false)
.toShimmer2(isShow: habibWalletVM.isWalletAmountLoading, radius: 12.h, width: 80.h, height: 24.h);
}),
CustomButton(
@ -115,7 +115,7 @@ class _ProfileSettingsState extends State<ProfileSettings> {
iconSize: 24.h,
iconColor: AppColors.infoColor,
textColor: AppColors.infoColor,
text: "Recharge",
text: "Recharge".needTranslation,
borderWidth: 0.h,
fontWeight: FontWeight.w500,
borderColor: Colors.transparent,

@ -12,8 +12,11 @@ import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart';
import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/presentation/radiology/radiology_result_page.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart';
import 'package:provider/provider.dart';
@ -49,10 +52,11 @@ class _RadiologyOrdersPageState extends State<RadiologyOrdersPage> {
child: SingleChildScrollView(
child: Consumer<RadiologyViewModel>(
builder: (context, model, child) {
return Column(
return Padding(
padding: EdgeInsets.symmetric(horizontal: 24.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 16.h),
// Expandable list
ListView.builder(
shrinkWrap: true,
@ -60,9 +64,7 @@ class _RadiologyOrdersPageState extends State<RadiologyOrdersPage> {
itemCount: model.isRadiologyOrdersLoading ? 5 : model.patientRadiologyOrders.length,
itemBuilder: (context, index) {
final isExpanded = expandedIndex == index;
return model.isRadiologyOrdersLoading
? const MoviesShimmerWidget()
: AnimationConfiguration.staggeredList(
return AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
@ -87,71 +89,52 @@ class _RadiologyOrdersPageState extends State<RadiologyOrdersPage> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
CustomButton(
text: LocaleKeys.resultsAvailable.tr(context: context),
onPressed: () {},
AppCustomChipWidget(
labelText: LocaleKeys.resultsAvailable.tr(context: context),
backgroundColor: AppColors.successColor.withOpacity(0.15),
borderColor: AppColors.successColor.withOpacity(0.01),
textColor: AppColors.successColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
),
Icon(isExpanded ? Icons.expand_less : Icons.expand_more),
],
),
).toShimmer2(isShow: model.isRadiologyOrdersLoading, width: 100),
SizedBox(height: 8.h),
Row(
children: [
Image.network(
model.patientRadiologyOrders[index].doctorImageURL!,
model.isRadiologyOrdersLoading
? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png"
: model.patientRadiologyOrders[index].doctorImageURL!,
width: 24.h,
height: 24.h,
fit: BoxFit.fill,
).circle(100),
).circle(100).toShimmer2(isShow: model.isRadiologyOrdersLoading),
SizedBox(width: 4.h),
model.patientRadiologyOrders[index].doctorName!.toText16(isBold: true)
(model.isRadiologyOrdersLoading ? "Dr John Smith" : model.patientRadiologyOrders[index].doctorName!)
.toText16(isBold: true)
.toShimmer2(isShow: model.isRadiologyOrdersLoading)
],
),
SizedBox(height: 8.h),
Row(
Wrap(
direction: Axis.horizontal,
spacing: 3.h,
runSpacing: 4.h,
children: [
CustomButton(
text: DateUtil.formatDateToDate(DateUtil.convertStringToDate(model.patientRadiologyOrders[index].orderDate), false),
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 12,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 24.h,
),
SizedBox(width: 8.h),
CustomButton(
text: model.patientRadiologyOrders[index].clinicDescription!,
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 12,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 24.h,
),
AppCustomChipWidget(
icon: AppAssets.doctor_calendar_icon,
labelText: model.isRadiologyOrdersLoading ? "01 Jan 2025" : DateUtil.formatDateToDate(model.patientRadiologyOrders[index].orderDate!, false),
).toShimmer2(isShow: model.isRadiologyOrdersLoading),
AppCustomChipWidget(
labelText: model.isRadiologyOrdersLoading ? "01 Jan 2025" : model.patientRadiologyOrders[index].clinicDescription!,
).toShimmer2(isShow: model.isRadiologyOrdersLoading),
// AppCustomChipWidget(labelText: "").toShimmer2(isShow: model.isRadiologyOrdersLoading, width: 16.h),
// AppCustomChipWidget(labelText: "").toShimmer2(isShow: model.isRadiologyOrdersLoading, width: 16.h),
],
),
],
),
),
AnimatedCrossFade(
model.isRadiologyOrdersLoading
? SizedBox.shrink()
: AnimatedCrossFade(
firstChild: SizedBox.shrink(),
secondChild: Padding(
padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 8.h),
@ -171,7 +154,13 @@ class _RadiologyOrdersPageState extends State<RadiologyOrdersPage> {
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
text: LocaleKeys.viewReport.tr(context: context),
onPressed: () {},
onPressed: () {
Navigator.of(context).push(
CustomPageRoute(
page: RadiologyResultPage(patientRadiologyResponseModel: model.patientRadiologyOrders[index]),
),
);
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
@ -199,6 +188,7 @@ class _RadiologyOrdersPageState extends State<RadiologyOrdersPage> {
},
),
],
),
);
},
),

@ -0,0 +1,162 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/radiology/models/resp_models/patient_radiology_response_model.dart';
import 'package:hmg_patient_app_new/features/radiology/radiology_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/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';
import 'package:url_launcher/url_launcher.dart';
class RadiologyResultPage extends StatefulWidget {
RadiologyResultPage({super.key, required this.patientRadiologyResponseModel});
PatientRadiologyResponseModel patientRadiologyResponseModel;
@override
State<RadiologyResultPage> createState() => _RadiologyResultPageState();
}
class _RadiologyResultPageState extends State<RadiologyResultPage> {
late RadiologyViewModel radiologyViewModel;
@override
void initState() {
scheduleMicrotask(() {
radiologyViewModel.getRadiologyImage(patientRadiologyResponseModel: widget.patientRadiologyResponseModel);
});
super.initState();
}
@override
Widget build(BuildContext context) {
radiologyViewModel = Provider.of<RadiologyViewModel>(context);
AppState _appState = getIt.get<AppState>();
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: Column(
children: [
Expanded(
child: CollapsingListView(
title: "Radiology Result".needTranslation,
child: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 24.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 24.h),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: true,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 16.h),
widget.patientRadiologyResponseModel.description!.toText16(isBold: true),
SizedBox(height: 8.h),
widget.patientRadiologyResponseModel.reportData!.trim().toText12(isBold: true, color: AppColors.textColorLight),
SizedBox(height: 16.h),
CustomButton(
text: "View Radiology Image".needTranslation,
onPressed: () async {
if (radiologyViewModel.radiologyImageURL.isNotEmpty) {
Uri uri = Uri.parse(radiologyViewModel.radiologyImageURL);
launchUrl(uri, mode: LaunchMode.platformDefault, webOnlyWindowName: "");
} else {
Utils.showToast("Radiology image not available".needTranslation);
}
},
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor,
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
icon: AppAssets.download,
iconColor: AppColors.whiteColor,
iconSize: 20.h,
),
SizedBox(height: 16.h),
],
).paddingSymmetrical(16.h, 0.h),
),
SizedBox(height: 24.h),
],
),
),
),
),
),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: true,
),
child: CustomButton(
text: "Download report".needTranslation,
onPressed: () async {
LoaderBottomSheet.showLoader();
await radiologyViewModel.getRadiologyPDF(patientRadiologyResponseModel: widget.patientRadiologyResponseModel, authenticatedUser: _appState.getAuthenticatedUser()!, onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: err),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}).then((val) async {
LoaderBottomSheet.hideLoader();
if (radiologyViewModel.patientRadiologyReportPDFBase64.isNotEmpty) {
String path = await Utils.createFileFromString(radiologyViewModel.patientRadiologyReportPDFBase64, "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,
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),
),
],
),
);
}
}

@ -25,6 +25,7 @@ import 'package:provider/provider.dart';
import 'core/cache_consts.dart';
import 'core/utils/local_notifications.dart';
import 'core/utils/push_notification_handler.dart';
import 'widgets/routes/custom_page_route.dart';
class SplashPage extends StatefulWidget {
@override
@ -52,7 +53,7 @@ class _SplashScreenState extends State<SplashPage> {
Navigator.of(context).pushReplacement(FadePage(page: SplashAnimationScreen()));
} else {
Navigator.of(context).pushReplacement(
FadePage(
CustomPageRoute(
page: LandingNavigation(),
// page: LoginScreen(),
),

@ -85,7 +85,8 @@ class _GenericBottomSheetState extends State<GenericBottomSheet> {
textDirection: Directionality.of(context),
child: Container(
padding: EdgeInsets.all(24.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.bgScaffoldColor, borderRadius: 16),
decoration: RoundedRectangleBorder()
.toSmoothCornerDecoration(color: AppColors.bgScaffoldColor, borderRadius: 16, customBorder: BorderRadius.only(topLeft: Radius.circular(16), topRight: Radius.circular(16))),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
@ -151,7 +152,7 @@ class _GenericBottomSheetState extends State<GenericBottomSheet> {
prefix: widget.isForEmail ? null : widget.countryCode,
isBorderAllowed: false,
isAllowLeadingIcon: true,
fontSize: 12.h,
fontSize: 13.h,
isCountryDropDown: widget.isEnableCountryDropdown,
leadingIcon: widget.isForEmail ? AppAssets.email : AppAssets.smart_phone,
)

@ -53,7 +53,7 @@ class CustomButton extends StatelessWidget {
color: isDisabled ? Colors.transparent : backgroundColor,
borderRadius: borderRadius,
side: BorderSide(
width: borderWidth,
width: borderWidth.h,
color: isDisabled ? borderColor.withOpacity(0.5) : borderColor,
)),
child: Row(
@ -62,10 +62,12 @@ class CustomButton extends StatelessWidget {
children: [
if (icon != null)
Padding(
padding: const EdgeInsets.only(right: 8.0),
padding: const EdgeInsets.only(right: 8.0, left: 8.0),
child: Utils.buildSvgWithAssets(icon: icon!, iconColor: iconColor, isDisabled: isDisabled, width: iconSize, height: iconSize),
),
Text(
Padding(
padding: EdgeInsets.only(top: 2.5),
child: Text(
text,
style: context.dynamicTextStyle(
fontSize: fontSize.fSize,
@ -74,6 +76,7 @@ class CustomButton extends StatelessWidget {
fontWeight: fontWeight,
),
),
),
],
),
)

@ -23,6 +23,7 @@ class AppCustomChipWidget extends StatelessWidget {
this.deleteIconSize = const Size(12, 12),
this.deleteIconColor = AppColors.textColor,
this.deleteIconHasColor = false,
this.padding = EdgeInsets.zero,
});
final String? labelText;
@ -38,6 +39,7 @@ class AppCustomChipWidget extends StatelessWidget {
final bool iconHasColor;
final bool deleteIconHasColor;
final OutlinedBorder? shape;
final EdgeInsets? padding;
@override
Widget build(BuildContext context) {
@ -50,7 +52,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(10.0), // Apply a border radius of 16.0
),
),
child: icon.isNotEmpty
@ -67,7 +69,8 @@ class AppCustomChipWidget extends StatelessWidget {
weight: FontWeight.w500,
letterSpacing: -0.64,
color: textColor),
padding: EdgeInsets.all(0.0),
// padding: EdgeInsets.all(0.0),
padding: padding,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
labelPadding: EdgeInsets.only(
left: -4.h,

@ -98,30 +98,33 @@ class _CustomCountryDropdownState extends State<CustomCountryDropdown> {
children: [
Text(
LocaleKeys.phoneNumber.tr(),
style: TextStyle(fontSize: 12.fSize, height: 21 / 12, fontWeight: FontWeight.w500, letterSpacing: -1),
style: TextStyle(fontSize: 12.fSize, height: 1.2.h, fontWeight: FontWeight.w500, letterSpacing: -1),
),
],
),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
selectedCountry!.countryCode,
style: TextStyle(fontSize: 12.fSize, height: 23 / 18, fontWeight: FontWeight.w600, letterSpacing: -1),
style: TextStyle(fontSize: 12.fSize, fontWeight: FontWeight.w600, letterSpacing: -0.4, height: 1.5),
),
SizedBox(width: 4.h),
if (widget.isEnableTextField)
SizedBox(
height: 20,
width: 200,
// color: Colors.red,
height: 20.h,
width: 200.h,
child: Align(
alignment: Alignment.centerLeft,
child: TextField(
focusNode: textFocusNode,
style: TextStyle(fontSize: 12.fSize, height: 23 / 18, fontWeight: FontWeight.w600, letterSpacing: -1),
decoration: InputDecoration(hintText: "", isDense: false, border: InputBorder.none),
style: TextStyle(fontSize: 12.fSize, fontWeight: FontWeight.w600, letterSpacing: -0.4, height: 1.5),
decoration: InputDecoration(hintText: "", isDense: true, border: InputBorder.none, contentPadding: EdgeInsets.zero),
keyboardType: TextInputType.phone,
onChanged: widget.onPhoneNumberChanged),
onChanged: widget.onPhoneNumberChanged,
),
),
),
],
)
@ -134,7 +137,7 @@ class _CustomCountryDropdownState extends State<CustomCountryDropdown> {
? appState.getLanguageCode() == "ar"
? selectedCountry!.nameArabic
: selectedCountry!.displayName
: "Select Country",
: LocaleKeys.selectCountry.tr(),
style: TextStyle(fontSize: 14.fSize, height: 21 / 14, fontWeight: FontWeight.w500, letterSpacing: -0.2),
),
],

@ -122,7 +122,9 @@ class DropdownWidget extends StatelessWidget {
.toList(),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
color: AppColors.scaffoldBgColor
);
if (selected != null && onChange != null) {

@ -42,6 +42,7 @@ class TextInputWidget extends StatelessWidget {
final bool? isWalletAmountInput;
final Widget? suffix;
final Color? labelColor;
final Function(String)? onSubmitted;
// final List<Country> countryList;
// final Function(Country)? onCountryChange;
@ -72,7 +73,8 @@ class TextInputWidget extends StatelessWidget {
this.fontSize = 14,
this.isWalletAmountInput = false,
this.suffix,
this.labelColor
this.labelColor,
this.onSubmitted
// this.countryList = const [],
// this.onCountryChange,
});
@ -239,7 +241,8 @@ class TextInputWidget extends StatelessWidget {
onTapOutside: (event) {
FocusManager.instance.primaryFocus?.unfocus();
},
style: TextStyle(fontSize: fontSize!.fSize, height: isWalletAmountInput! ? 1 / 4 : 21 / 14, fontWeight: FontWeight.w500, color: AppColors.textColor, letterSpacing: -0.2),
onSubmitted: onSubmitted,
style: TextStyle(fontSize: fontSize!.fSize, height: isWalletAmountInput! ? 1 / 4 : 0, fontWeight: FontWeight.w500, color: AppColors.textColor, letterSpacing: -1),
decoration: InputDecoration(
isDense: true,
hintText: hintText,

@ -80,9 +80,10 @@ dependencies:
path_provider: ^2.0.8
open_filex: ^4.7.0
flutter_swiper_view: ^1.1.8
family_bottom_sheet: ^0.1.0
location: ^8.0.1
gms_check: ^1.0.4
huawei_location: ^6.14.2+301
dev_dependencies:
flutter_test:

Loading…
Cancel
Save