Merge branch 'refs/heads/master' into dev_sikander

pull/57/head
Sikander Saleem 1 month ago
commit c553876c8a

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 161 KiB

@ -0,0 +1,3 @@
<svg width="14" height="15" viewBox="0 0 14 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.5525 2.46209C9.79631 1.38751 8.20843 1.81413 7.24747 2.53374L7.24696 2.53412L6.99967 2.71856L6.75249 2.53412C5.79154 1.81445 4.20322 1.38747 2.44697 2.46209C1.21445 3.21625 0.537796 4.77917 0.775779 6.54921C1.01526 8.33035 2.16771 10.3232 4.57364 12.0996L4.64687 12.1537C5.46216 12.7562 6.04566 13.1875 6.99985 13.1875C7.95405 13.1875 8.53754 12.7562 9.35283 12.1537L9.42606 12.0996C10.3581 11.4114 11.1012 10.6918 11.6765 9.96419C11.6797 9.96013 11.6828 9.95603 11.6859 9.95191C14.0531 6.94717 13.5653 3.69371 11.5525 2.46209ZM7.74698 6.53198L10.4778 9.62245C10.0168 10.1444 9.44544 10.6701 8.74767 11.1853C7.83876 11.8564 7.54669 12.05 6.99985 12.05C6.45302 12.05 6.16095 11.8564 5.25204 11.1853C3.02962 9.54441 2.09491 7.80338 1.90596 6.39801C1.71551 4.98153 2.26916 3.90531 3.0432 3.43169C4.33507 2.64121 5.40954 2.95773 6.05358 3.43314L4.70267 4.46071C3.88293 5.08424 3.8086 6.28812 4.54542 7.00729C4.86355 7.31779 5.27961 7.48142 5.70118 7.48816C5.71901 7.48845 5.73685 7.48845 5.75469 7.48818C6.05478 7.48357 6.3557 7.39938 6.62418 7.232L7.74698 6.53198Z" fill="#2E3039"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

@ -126,7 +126,7 @@
"gregorianDate": "التاريخ الميلادي", "gregorianDate": "التاريخ الميلادي",
"verifyLoginWith": "يرجى اختيار واحدة من الخيارات التالية للتحقق", "verifyLoginWith": "يرجى اختيار واحدة من الخيارات التالية للتحقق",
"registerUser": "تسجيل", "registerUser": "تسجيل",
"verifyWithFingerprint": "بصمة الإصبع", "verifyWithFingerprint":"البيومترية",
"verifyWithFaceid": "معرف الوجه", "verifyWithFaceid": "معرف الوجه",
"verifyWithSms": "رسالة قصيرة", "verifyWithSms": "رسالة قصيرة",
"verifyWithWhatsapp": "واتساب", "verifyWithWhatsapp": "واتساب",
@ -849,6 +849,7 @@
"pleaseEnterEmail": "يرجى إدخال البريد الإلكتروني", "pleaseEnterEmail": "يرجى إدخال البريد الإلكتروني",
"pleaseEnterAValidEmailFormat": "يرجى إدخال تنسيق بريد إلكتروني صالح", "pleaseEnterAValidEmailFormat": "يرجى إدخال تنسيق بريد إلكتروني صالح",
"selectCountry": "اختر الدولة", "selectCountry": "اختر الدولة",
"forLoginVerification": "للتحقق من تسجيل الدخول" "forLoginVerification": "للتحقق من تسجيل الدخول",
"searchHospital": "بحث في المستشفى"
} }

@ -126,7 +126,7 @@
"gregorianDate": "Gregorian Date", "gregorianDate": "Gregorian Date",
"verifyLoginWith": "Please choose one of the following options to verify", "verifyLoginWith": "Please choose one of the following options to verify",
"registerUser": "Register", "registerUser": "Register",
"verifyWithFingerprint": "Fingerprint", "verifyWithFingerprint": "Biometric",
"verifyWithFaceid": "Face ID", "verifyWithFaceid": "Face ID",
"verifyWithSms": "SMS", "verifyWithSms": "SMS",
"verifyWithWhatsapp": "Whatsapp", "verifyWithWhatsapp": "Whatsapp",
@ -561,12 +561,12 @@
"selectTamaraPlan": "Select Tamara Payment Plan", "selectTamaraPlan": "Select Tamara Payment Plan",
"changeMethod": "Change Method", "changeMethod": "Change Method",
"reviewOrder": "Review Order", "reviewOrder": "Review Order",
"active": "ACTIVE", "active": "Active",
"inactive": "INACTIVE", "inactive": "InActive",
"balance": "BALANCE", "balance": "Balance",
"gained": "GAINED", "gained": "GAINED",
"consumed": "Consumed", "consumed": "Consumed",
"transferred": "TRANSFERRED", "transferred": "Transferred",
"riyal": "RIYAL", "riyal": "RIYAL",
"membersince": "MEMBER SINCE", "membersince": "MEMBER SINCE",
"identification": "رقم الهوية", "identification": "رقم الهوية",
@ -845,5 +845,6 @@
"pleaseEnterAValidEmailFormat": "Please enter a valid email format", "pleaseEnterAValidEmailFormat": "Please enter a valid email format",
"selectCountry": "Select Country", "selectCountry": "Select Country",
"forLoginVerification": "for login verification", "forLoginVerification": "for login verification",
"lastLoginBy": "Last login by" "lastLoginBy": "Last login by",
"searchHospital": "Search Hospital"
} }

@ -720,10 +720,14 @@ const SAVE_SETTING = 'Services/Patients.svc/REST/UpdatePateintInfo';
const DEACTIVATE_ACCOUNT = 'Services/Patients.svc/REST/PatientAppleActivation_InsertUpdate'; const DEACTIVATE_ACCOUNT = 'Services/Patients.svc/REST/PatientAppleActivation_InsertUpdate';
//family Files
const FAMILY_FILES= 'Services/Authentication.svc/REST/GetAllSharedRecordsByStatus';
class ApiConsts { class ApiConsts {
static const maxSmallScreen = 660; static const maxSmallScreen = 660;
static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.uat; static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.prod;
// static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT // static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT
@ -803,6 +807,8 @@ class ApiConsts {
static final String getPrivileges = 'Services/Patients.svc/REST/Service_Privilege'; static final String getPrivileges = 'Services/Patients.svc/REST/Service_Privilege';
static final String registerUser = 'Services/Authentication.svc/REST/PatientRegistration'; static final String registerUser = 'Services/Authentication.svc/REST/PatientRegistration';
static final String addFamilyFile = 'Services/Patients.svc/REST/ShareFamilyFileService';
// static values for Api // static values for Api
static final double appVersionID = 18.7; static final double appVersionID = 18.7;
static final int appChannelId = 3; static final int appChannelId = 3;

@ -143,12 +143,15 @@ class AppAssets {
static const String closeBottomNav = '$svgBasePath/close_bottom_nav.svg'; static const String closeBottomNav = '$svgBasePath/close_bottom_nav.svg';
static const String feedback = '$svgBasePath/feedback.svg'; static const String feedback = '$svgBasePath/feedback.svg';
static const String news = '$svgBasePath/news.svg'; static const String news = '$svgBasePath/news.svg';
static const String heart = '$svgBasePath/heart.svg';
// PNGS // // PNGS //
static const String hmg_logo = '$pngBasePath/hmg_logo.png'; static const String hmg_logo = '$pngBasePath/hmg_logo.png';
static const String livecare_service = '$pngBasePath/livecare_service.png'; static const String livecare_service = '$pngBasePath/livecare_service.png';
static const String male_img = '$pngBasePath/male_img.png'; static const String male_img = '$pngBasePath/male_img.png';
static const String femaleImg = '$pngBasePath/female_img.png'; static const String femaleImg = '$pngBasePath/female_img.png';
static const String babyGirlImg = '$pngBasePath/baby_girl_img.png';
static const String babyBoyImg = '$pngBasePath/baby_img.png';
static const String apple_pay = '$pngBasePath/Apple_Pay.png'; static const String apple_pay = '$pngBasePath/Apple_Pay.png';
static const String mada = '$pngBasePath/Mada.png'; static const String mada = '$pngBasePath/Mada.png';
static const String Mastercard = '$pngBasePath/Mastercard.png'; static const String Mastercard = '$pngBasePath/Mastercard.png';

@ -0,0 +1,14 @@
///class used to provide value for the [DynamicResultChart] to plot the values
class DataPoint {
///values that is displayed on the graph and dot is plotted on this
final double value;
///label shown on the bottom of the graph
String label;
DataPoint(
{required this.value,
required this.label,
});
}

@ -0,0 +1,21 @@
import 'dart:ui' show Color;
class ThresholdRange {
final String label;
final double value;
final Color color;
final Color lineColor;
final String? actualValue;
ThresholdRange(
{required this.label,
required this.value,
required this.color,
required this.lineColor,
this.actualValue});
@override
String toString() {
return 'ThresholdRange(label: $label, value: $value, color: ${color.value.toRadixString(16)}, lineColor: ${lineColor.value.toRadixString(16)})';
}
}

@ -8,22 +8,9 @@
import 'package:hmg_patient_app_new/core/app_state.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/dependencies.dart';
enum AuthMethodTypesEnum { enum AuthMethodTypesEnum { sms, whatsApp, fingerPrint, faceID, moreOptions }
sms,
whatsApp,
fingerPrint,
faceID,
moreOptions,
}
enum ViewStateEnum { enum ViewStateEnum { hide, idle, busy, error, busyLocal, errorLocal }
hide,
idle,
busy,
error,
busyLocal,
errorLocal,
}
enum CountryEnum { saudiArabia, unitedArabEmirates } enum CountryEnum { saudiArabia, unitedArabEmirates }
@ -35,7 +22,7 @@ enum GenderTypeEnum { male, female }
enum MaritalStatusTypeEnum { single, married, divorced, widowed } enum MaritalStatusTypeEnum { single, married, divorced, widowed }
enum ChipTypeEnum { success, error, alert, info, warning } enum ChipTypeEnum { success, error, alert, info, warning, lightBg, primaryRed }
enum OTPTypeEnum { sms, whatsapp, faceIDFingerprint } enum OTPTypeEnum { sms, whatsapp, faceIDFingerprint }

@ -8,16 +8,9 @@ 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/cache_consts.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:huawei_location/huawei_location.dart' as HmsLocation import 'package:huawei_location/huawei_location.dart' as HmsLocation show FusedLocationProviderClient, Location, LocationSettingsRequest, LocationRequest;
show import 'package:location/location.dart' show Location, PermissionStatus, LocationData;
FusedLocationProviderClient, import 'package:permission_handler/permission_handler.dart' show Permission, PermissionListActions, PermissionStatusGetters;
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 { class LocationUtils {
NavigationService navigationService; NavigationService navigationService;
@ -39,8 +32,7 @@ class LocationUtils {
isGMSDevice = GmsCheck().checkGmsAvailability(); isGMSDevice = GmsCheck().checkGmsAvailability();
} }
void getLocation( void getLocation({Function(LatLng)? onSuccess, VoidCallback? onFailure}) async {
{Function(LatLng)? onSuccess, VoidCallback? onFailure}) async {
if (Platform.isIOS) { if (Platform.isIOS) {
getCurrentLocation(onFailure: onFailure, onSuccess: onSuccess); getCurrentLocation(onFailure: onFailure, onSuccess: onSuccess);
return; return;
@ -54,8 +46,7 @@ class LocationUtils {
getHMSLocation(onFailure: onFailure, onSuccess: onSuccess); getHMSLocation(onFailure: onFailure, onSuccess: onSuccess);
} }
void getCurrentLocation( void getCurrentLocation({Function(LatLng)? onSuccess, VoidCallback? onFailure}) async {
{Function(LatLng)? onSuccess, VoidCallback? onFailure}) async {
var location = Location(); var location = Location();
bool isLocationEnabled = await location.serviceEnabled(); bool isLocationEnabled = await location.serviceEnabled();
@ -72,8 +63,7 @@ class LocationUtils {
LocationPermission permissionGranted = await Geolocator.checkPermission(); LocationPermission permissionGranted = await Geolocator.checkPermission();
if (permissionGranted == LocationPermission.denied) { if (permissionGranted == LocationPermission.denied) {
permissionGranted = await Geolocator.requestPermission(); permissionGranted = await Geolocator.requestPermission();
if (permissionGranted != LocationPermission.whileInUse && if (permissionGranted != LocationPermission.whileInUse && permissionGranted != LocationPermission.always) {
permissionGranted != LocationPermission.always) {
appState.resetLocation(); appState.resetLocation();
onFailure?.call(); onFailure?.call();
return; return;
@ -83,12 +73,10 @@ class LocationUtils {
Position? currentLocation = await Geolocator.getLastKnownPosition(); Position? currentLocation = await Geolocator.getLastKnownPosition();
if (currentLocation?.latitude == null || currentLocation?.longitude == null) { if (currentLocation?.latitude == null || currentLocation?.longitude == null) {
currentLocation = await Geolocator.getCurrentPosition( currentLocation = await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.low);
desiredAccuracy: LocationAccuracy.low);
} }
LatLng locationData = LatLng( LatLng locationData = LatLng(currentLocation?.latitude ?? 0.0, currentLocation?.longitude ?? 0.0);
currentLocation?.latitude ?? 0.0, currentLocation?.longitude ?? 0.0);
saveLatLngToAppState(locationData); saveLatLngToAppState(locationData);
onSuccess?.call(locationData); onSuccess?.call(locationData);
} }
@ -197,22 +185,17 @@ class LocationUtils {
appState.userLong = locationData.longitude; appState.userLong = locationData.longitude;
} }
void getHMSLocation( void getHMSLocation({VoidCallback? onFailure, Function(LatLng p1)? onSuccess}) async {
{VoidCallback? onFailure, Function(LatLng p1)? onSuccess}) async {
try { try {
var location = Location(); var location = Location();
HmsLocation.FusedLocationProviderClient locationService = HmsLocation.FusedLocationProviderClient locationService = HmsLocation.FusedLocationProviderClient()..initFusedLocationService();
HmsLocation.FusedLocationProviderClient()..initFusedLocationService();
bool isLocationEnabled = await Geolocator.isLocationServiceEnabled(); bool isLocationEnabled = await Geolocator.isLocationServiceEnabled();
//if the location service is not enabled, ask the user to enable it //if the location service is not enabled, ask the user to enable it
if (!isLocationEnabled) { if (!isLocationEnabled) {
HmsLocation.LocationRequest locationRequest = HmsLocation.LocationRequest locationRequest = HmsLocation.LocationRequest()..priority = HmsLocation.LocationRequest.PRIORITY_HIGH_ACCURACY;
HmsLocation.LocationRequest()
..priority = HmsLocation.LocationRequest.PRIORITY_HIGH_ACCURACY;
HmsLocation.LocationSettingsRequest request = HmsLocation.LocationSettingsRequest request = HmsLocation.LocationSettingsRequest(
HmsLocation.LocationSettingsRequest(
alwaysShow: true, alwaysShow: true,
needBle: false, needBle: false,
requests: [locationRequest], requests: [locationRequest],
@ -223,8 +206,7 @@ class LocationUtils {
LocationPermission permissionGranted = await Geolocator.checkPermission(); LocationPermission permissionGranted = await Geolocator.checkPermission();
if (permissionGranted == LocationPermission.denied) { if (permissionGranted == LocationPermission.denied) {
permissionGranted = await Geolocator.requestPermission(); permissionGranted = await Geolocator.requestPermission();
if (permissionGranted != LocationPermission.whileInUse && if (permissionGranted != LocationPermission.whileInUse && permissionGranted != LocationPermission.always) {
permissionGranted != LocationPermission.always) {
appState.resetLocation(); appState.resetLocation();
onFailure?.call(); onFailure?.call();
return; return;
@ -246,8 +228,7 @@ class LocationUtils {
onFailure?.call(); onFailure?.call();
return; return;
} }
var locationData = var locationData = LatLng(data.latitude ?? 0.0, data.longitude ?? 0.0);
LatLng(data.latitude ?? 0.0, data.longitude ?? 0.0);
saveLatLngToAppState(locationData); saveLatLngToAppState(locationData);
onSuccess?.call(locationData); onSuccess?.call(locationData);
}); });

@ -246,4 +246,20 @@ class RequestUtils {
if (!isDubai) "HealthId": appState.getNHICUserData.healthId, if (!isDubai) "HealthId": appState.getNHICUserData.healthId,
}; };
} }
static dynamic getAddFamilyRequest({required String nationalIDorFile, required String mobileNo, required String countryCode, required int loginType}) {
var request = <String, dynamic>{};
if (loginType == 1) {
request["sharedPatientID"] = 0;
request["sharedPatientIdentificationID"] = nationalIDorFile;
} else if (loginType == 2) {
request["sharedPatientID"] = int.parse(nationalIDorFile);
request["sharedPatientIdentificationID"] = '';
}
request["searchType"] = loginType;
request["sharedPatientMobileNumber"] = mobileNo;
request["zipCode"] = countryCode;
request["isRegister"] = false;
request["patientStatus"] = 2;
}
} }

@ -606,6 +606,27 @@ class Utils {
); );
} }
static Widget buildImgWithAssets({
required String icon,
Color? iconColor,
bool isDisabled = false,
double width = 24,
double height = 24,
BoxFit fit = BoxFit.cover,
}) {
return Image.asset(icon, width: width, height: height, fit: fit);
}
/// Widget to build an SVG from network
static Widget buildImgWithNetwork({required String url, required Color iconColor, bool isDisabled = false, double width = 24, double height = 24, BoxFit fit = BoxFit.cover}) {
return Image.network(
url,
width: width,
height: height,
fit: fit,
);
}
static Widget getPaymentMethods() { static Widget getPaymentMethods() {
return Row( return Row(
mainAxisSize: MainAxisSize.max, mainAxisSize: MainAxisSize.max,

@ -140,4 +140,34 @@ class ValidationUtils {
return true; return true;
} }
static bool isValidatedIdAndPhoneWithCountryValidation({String? nationalId, String? phoneNumber, required Function() onOkPress, CountryEnum? selectedCountry}) {
bool isCorrectID = true;
if (nationalId == null || nationalId.isEmpty) {
_dialogService.showExceptionBottomSheet(message: LocaleKeys.pleaseEnterAnationalID.tr(), onOkPressed: onOkPress);
isCorrectID = false;
}
if (nationalId != null && nationalId.isNotEmpty && selectedCountry != null) {
if (selectedCountry == CountryEnum.saudiArabia) {
if (!validateIqama(nationalId)) {
_dialogService.showExceptionBottomSheet(message: LocaleKeys.pleaseEnterAValidIqamaID.tr(), onOkPressed: onOkPress);
return false;
}
}
if (selectedCountry == CountryEnum.unitedArabEmirates) {
if (!validateUaeNationalId(nationalId)) {
_dialogService.showExceptionBottomSheet(message: LocaleKeys.pleaseEnterAValidNationalID.tr(), onOkPressed: onOkPress);
return false;
}
}
if (phoneNumber == null || phoneNumber.isEmpty) {
_dialogService.showExceptionBottomSheet(message: LocaleKeys.enterValidPhoneNumber.tr(), onOkPressed: onOkPress);
return false;
}
}
return isCorrectID;
}
} }

@ -22,7 +22,7 @@ extension WidgetExtensions on Widget {
Widget paddingSymmetrical(double horizontal, double vertical) => Padding(padding: EdgeInsets.symmetric(horizontal: horizontal, vertical: vertical), child: this); Widget paddingSymmetrical(double horizontal, double vertical) => Padding(padding: EdgeInsets.symmetric(horizontal: horizontal, vertical: vertical), child: this);
Widget paddingOnly({double left = 0.0, double right = 0.0, double top = 0.0, double bottom = 0.0}) => Widget paddingOnly({double left = 0.0, double right = 0.0, double top = 0.0, double bottom = 0.0}) =>
Padding(padding: EdgeInsets.only(left: left, right: right, top: top, bottom: bottom), child: this); Padding(padding: EdgeInsetsDirectional.only(start: left, end: right, top: top, bottom: bottom), child: this);
Widget toExpanded({int flex = 1}) => Expanded(flex: flex, child: this); Widget toExpanded({int flex = 1}) => Expanded(flex: flex, child: this);
@ -233,6 +233,10 @@ extension ChipTypeEnumExtension on ChipTypeEnum {
return AppColors.infoColor; // Replace with your actual color return AppColors.infoColor; // Replace with your actual color
case ChipTypeEnum.warning: case ChipTypeEnum.warning:
return AppColors.warningColor; // Replace with your actual color return AppColors.warningColor; // Replace with your actual color
case ChipTypeEnum.lightBg:
return AppColors.chipPrimaryRedBorderColor; // Replace with your actual color
case ChipTypeEnum.primaryRed:
return AppColors.chipSecondaryLightRedColor; // Replace with your actual color
} }
} }
@ -248,6 +252,10 @@ extension ChipTypeEnumExtension on ChipTypeEnum {
return AppColors.infoLightColor; // Replace with your actual color return AppColors.infoLightColor; // Replace with your actual color
case ChipTypeEnum.warning: case ChipTypeEnum.warning:
return AppColors.warningLightColor; // Replace with your actual color return AppColors.warningLightColor; // Replace with your actual color
case ChipTypeEnum.lightBg:
return AppColors.chipSecondaryLightRedColor; // Replace with your actual color
case ChipTypeEnum.primaryRed:
return AppColors.chipPrimaryRedBorderColor;
} }
} }
} }

@ -414,11 +414,6 @@ class BookAppointmentsViewModel extends ChangeNotifier {
} }
Future<void> getRegionMappedProjectList() async { 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;
// }
isRegionListLoading = true; isRegionListLoading = true;
notifyListeners(); notifyListeners();
final result = await bookAppointmentsRepo.getProjectList(); final result = await bookAppointmentsRepo.getProjectList();
@ -432,7 +427,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
} else if (apiResponse.messageStatus == 1) { } else if (apiResponse.messageStatus == 1) {
var projectList = apiResponse.data!; var projectList = apiResponse.data!;
hospitalList = await DoctorMapper.getMappedHospitals(projectList, hospitalList = await DoctorMapper.getMappedHospitals(projectList,
isArabic: false, isArabic: _appState.isArabic(),
lat: _appState.userLat, lat: _appState.userLat,
lng: _appState.userLong, lng: _appState.userLong,
); );

@ -17,6 +17,8 @@ class LabViewModel extends ChangeNotifier {
List<String> get labSuggestions => _labSuggestionsList; List<String> get labSuggestions => _labSuggestionsList;
Set<TestDetails> uniqueTests = {};
LabViewModel({required this.labRepo, required this.errorHandlerService}); LabViewModel({required this.labRepo, required this.errorHandlerService});
initLabProvider() { initLabProvider() {
@ -43,6 +45,7 @@ class LabViewModel extends ChangeNotifier {
isLabOrdersLoading = false; isLabOrdersLoading = false;
isLabResultsLoading = false; isLabResultsLoading = false;
filterSuggestions(); filterSuggestions();
getUniqueTestDescription();
notifyListeners(); notifyListeners();
if (onSuccess != null) { if (onSuccess != null) {
onSuccess(apiResponse); onSuccess(apiResponse);
@ -75,4 +78,15 @@ class LabViewModel extends ChangeNotifier {
} }
notifyListeners(); notifyListeners();
} }
getUniqueTestDescription() {
uniqueTests = {
for (var item in patientLabOrders)
if (item.testDetails != null)
...?item.testDetails?.map<TestDetails>((test) =>
TestDetails(description: test.description.toString(), testCode: test.testCode.toString(), testID: test.testID, createdOn: item.createdOn))
};
uniqueTests.forEach(print);
}
} }

@ -226,13 +226,14 @@ class TestDetails {
String? description; String? description;
String? testCode; String? testCode;
String? testID; String? testID;
String? createdOn;
TestDetails({this.description, this.testCode, this.testID}); TestDetails({this.description, this.testCode, this.testID, this.createdOn});
TestDetails.fromJson(Map<String, dynamic> json) { TestDetails.fromJson(Map<String, dynamic> json) {
description = json['Description']; description = json['Description'];
testCode = json['TestCode']; testCode = json['TestCode'];
testID = json['TestID']; testID = json['TestID'];
createdOn = json['CreatedOn'];
} }
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
@ -240,6 +241,7 @@ class TestDetails {
data['Description'] = this.description; data['Description'] = this.description;
data['TestCode'] = this.testCode; data['TestCode'] = this.testCode;
data['TestID'] = this.testID; data['TestID'] = this.testID;
data['CreatedOn'] = this.createdOn;
return data; return data;
} }
} }

@ -11,6 +11,7 @@ import 'package:hmg_patient_app_new/features/medical_file/models/patient_vaccine
import 'package:hmg_patient_app_new/services/logger_service.dart'; import 'package:hmg_patient_app_new/services/logger_service.dart';
import '../authentication/models/resp_models/authenticated_user_resp_model.dart'; import '../authentication/models/resp_models/authenticated_user_resp_model.dart';
import 'models/family_file_response_model.dart';
abstract class MedicalFileRepo { abstract class MedicalFileRepo {
Future<Either<Failure, GenericApiModel<List<PatientVaccineResponseModel>>>> getPatientVaccinesList(); Future<Either<Failure, GenericApiModel<List<PatientVaccineResponseModel>>>> getPatientVaccinesList();
@ -22,6 +23,10 @@ abstract class MedicalFileRepo {
Future<Either<Failure, GenericApiModel<List<PatientMedicalReportResponseModel>>>> getPatientMedicalReportsList(); Future<Either<Failure, GenericApiModel<List<PatientMedicalReportResponseModel>>>> getPatientMedicalReportsList();
Future<Either<Failure, GenericApiModel<dynamic>>> getPatientMedicalReportPDF(PatientMedicalReportResponseModel patientMedicalReportResponseModel, AuthenticatedUser authenticatedUser); Future<Either<Failure, GenericApiModel<dynamic>>> getPatientMedicalReportPDF(PatientMedicalReportResponseModel patientMedicalReportResponseModel, AuthenticatedUser authenticatedUser);
Future<Either<Failure, GenericApiModel<List<FamilyFileResponseModelLists>>>> getPatientFamilyFiles();
Future<Either<Failure, GenericApiModel<List<dynamic>>>> addFamilyFile({required dynamic request});
} }
class MedicalFileRepoImp implements MedicalFileRepo { class MedicalFileRepoImp implements MedicalFileRepo {
@ -267,4 +272,80 @@ class MedicalFileRepoImp implements MedicalFileRepo {
return Left(UnknownFailure(e.toString())); return Left(UnknownFailure(e.toString()));
} }
} }
@override
Future<Either<Failure, GenericApiModel<List<FamilyFileResponseModelLists>>>> getPatientFamilyFiles() async {
try {
GenericApiModel<List<FamilyFileResponseModelLists>>? apiResponse;
Failure? failure;
await apiClient.post(
FAMILY_FILES,
body: {"Status": 3},
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response['GetAllSharedRecordsByStatusList'];
// if (list == null || list.isEmpty) {
// throw Exception("lab list is empty");
// }
final familyLists = list.map((item) => FamilyFileResponseModelLists.fromJson(item as Map<String, dynamic>)).toList().cast<FamilyFileResponseModelLists>();
apiResponse = GenericApiModel<List<FamilyFileResponseModelLists>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: familyLists,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<List<dynamic>>>> addFamilyFile({dynamic request}) async {
try {
GenericApiModel<List<dynamic>>? apiResponse;
Failure? failure;
await apiClient.post(
ApiConsts.addFamilyFile,
body: request,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
print(response);
// final list = response['GetAllSharedRecordsByStatusList'];
//
// final familyLists = list.map((item) => FamilyFileResponseModelLists.fromJson(item as Map<String, dynamic>)).toList().cast<FamilyFileResponseModelLists>();
//
// apiResponse = GenericApiModel<List<FamilyFileResponseModelLists>>(
// messageStatus: messageStatus,
// statusCode: statusCode,
// errorMessage: null,
// data: familyLists,
// );
} 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,9 +1,14 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/request_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/authentication/models/resp_models/authenticated_user_resp_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/medical_file_repo.dart'; import 'package:hmg_patient_app_new/features/medical_file/medical_file_repo.dart';
import 'package:hmg_patient_app_new/features/medical_file/models/family_file_response_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/models/patient_medical_response_model.dart'; import 'package:hmg_patient_app_new/features/medical_file/models/patient_medical_response_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/models/patient_sickleave_response_model.dart'; import 'package:hmg_patient_app_new/features/medical_file/models/patient_sickleave_response_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/models/patient_vaccine_response_model.dart'; import 'package:hmg_patient_app_new/features/medical_file/models/patient_vaccine_response_model.dart';
import 'package:hmg_patient_app_new/services/dialog_service.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart'; import 'package:hmg_patient_app_new/services/error_handler_service.dart';
class MedicalFileViewModel extends ChangeNotifier { class MedicalFileViewModel extends ChangeNotifier {
@ -25,10 +30,14 @@ class MedicalFileViewModel extends ChangeNotifier {
List<PatientMedicalReportResponseModel> patientMedicalReportReadyList = []; List<PatientMedicalReportResponseModel> patientMedicalReportReadyList = [];
List<PatientMedicalReportResponseModel> patientMedicalReportCancelledList = []; List<PatientMedicalReportResponseModel> patientMedicalReportCancelledList = [];
List<FamilyFileResponseModelLists> patientFamilyFiles = [];
String patientSickLeavePDFBase64 = ""; String patientSickLeavePDFBase64 = "";
String patientMedicalReportPDFBase64 = ""; String patientMedicalReportPDFBase64 = "";
int selectedMedicalReportsTabIndex = 0; int selectedMedicalReportsTabIndex = 0;
static final DialogService _dialogService = getIt.get<DialogService>();
AppState _appState = getIt<AppState>();
MedicalFileViewModel({required this.medicalFileRepo, required this.errorHandlerService}); MedicalFileViewModel({required this.medicalFileRepo, required this.errorHandlerService});
@ -217,4 +226,76 @@ class MedicalFileViewModel extends ChangeNotifier {
}, },
); );
} }
Future<void> getFamilyFiles({Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await medicalFileRepo.getPatientFamilyFiles();
result.fold(
(failure) async => await errorHandlerService.handleError(
failure: failure,
onOkPressed: () {
onError!(failure.message);
},
),
(apiResponse) {
if (apiResponse.messageStatus == 2) {
_dialogService.showErrorBottomSheet(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
patientFamilyFiles = apiResponse.data!;
patientFamilyFiles.insert(
0,
FamilyFileResponseModelLists(
patientId: _appState.getAuthenticatedUser()!.patientId,
patientName: '${_appState.getAuthenticatedUser()!.firstName!} ${_appState.getAuthenticatedUser()!.lastName!}',
isActive: true,
gender: _appState.getAuthenticatedUser()!.gender!,
responseId: _appState.getAuthenticatedUser()!.patientId),
);
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
Future<void> switchFamilyFiles({Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await medicalFileRepo.getPatientFamilyFiles();
result.fold(
(failure) async => await errorHandlerService.handleError(
failure: failure,
onOkPressed: () {
onError!(failure.message);
},
),
(apiResponse) {
if (apiResponse.messageStatus == 2) {
_dialogService.showErrorBottomSheet(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
patientFamilyFiles = apiResponse.data!;
patientFamilyFiles.insert(
0,
FamilyFileResponseModelLists(
patientId: _appState.getAuthenticatedUser()!.patientId,
patientName: '${_appState.getAuthenticatedUser()!.firstName!} ${_appState.getAuthenticatedUser()!.lastName!}',
isActive: true,
gender: _appState.getAuthenticatedUser()!.gender!,
responseId: _appState.getAuthenticatedUser()!.patientId),
);
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
Future<void> addFamilyFile() async {
final resultEither = await medicalFileRepo.addFamilyFile(request: {});
resultEither.fold((failure) async => await errorHandlerService.handleError(failure: failure), (apiResponse) async {});
}
} }

@ -0,0 +1,105 @@
import 'dart:convert';
class FamilyFileResponseModelLists {
int? id;
int? patientId;
int? responseId;
dynamic relationshipId;
dynamic relationship;
dynamic relationshipN;
int? regionId;
int? familyRegionId;
int? status;
dynamic isActive;
String? editedOn;
String? createdOn;
int? age;
String? emaiLAddress;
int? gender;
String? genderDescription;
String? genderImage;
String? mobileNumber;
int? patientDataVerified;
String? patientIdenficationNumber;
String? patientName;
String? statusDescription;
FamilyFileResponseModelLists({
this.id,
this.patientId,
this.responseId,
this.relationshipId,
this.relationship,
this.relationshipN,
this.regionId,
this.familyRegionId,
this.status,
this.isActive,
this.editedOn,
this.createdOn,
this.age,
this.emaiLAddress,
this.gender,
this.genderDescription,
this.genderImage,
this.mobileNumber,
this.patientDataVerified,
this.patientIdenficationNumber,
this.patientName,
this.statusDescription,
});
factory FamilyFileResponseModelLists.fromRawJson(String str) => FamilyFileResponseModelLists.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory FamilyFileResponseModelLists.fromJson(Map<String, dynamic> json) => FamilyFileResponseModelLists(
id: json["ID"],
patientId: json["PatientID"],
responseId: json["ResponseID"],
relationshipId: json["RelationshipID"],
relationship: json["Relationship"],
relationshipN: json["RelationshipN"],
regionId: json["RegionID"],
familyRegionId: json["FamilyRegionID"],
status: json["Status"],
isActive: json["IsActive"],
editedOn: json["EditedOn"],
createdOn: json["CreatedOn"],
age: json["Age"],
emaiLAddress: json["EmaiLAddress"],
gender: json["Gender"],
genderDescription: json["GenderDescription"],
genderImage: json["GenderImage"],
mobileNumber: json["MobileNumber"],
patientDataVerified: json["PatientDataVerified"],
patientIdenficationNumber: json["PatientIdenficationNumber"],
patientName: json["PatientName"],
statusDescription: json["StatusDescription"],
);
Map<String, dynamic> toJson() => {
"ID": id,
"PatientID": patientId,
"ResponseID": responseId,
"RelationshipID": relationshipId,
"Relationship": relationship,
"RelationshipN": relationshipN,
"RegionID": regionId,
"FamilyRegionID": familyRegionId,
"Status": status,
"IsActive": isActive,
"EditedOn": editedOn,
"CreatedOn": createdOn,
"Age": age,
"EmaiLAddress": emaiLAddress,
"Gender": gender,
"GenderDescription": genderDescription,
"GenderImage": genderImage,
"MobileNumber": mobileNumber,
"PatientDataVerified": patientDataVerified,
"PatientIdenficationNumber": patientIdenficationNumber,
"PatientName": patientName,
"StatusDescription": statusDescription,
};
}

@ -1,4 +1,5 @@
import 'package:flutter/foundation.dart' show ChangeNotifier; import 'package:flutter/foundation.dart' show ChangeNotifier;
import 'package:hmg_patient_app_new/core/app_state.dart' show AppState;
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/doctor_list_api_response.dart'; 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/presentation/book_appointment/select_clinic_page.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart';
@ -20,8 +21,9 @@ class AppointmentViaRegionViewmodel extends ChangeNotifier {
final NavigationService navigationService; final NavigationService navigationService;
AppointmentViaRegionState bottomSheetState = AppointmentViaRegionState bottomSheetState =
AppointmentViaRegionState.REGION_SELECTION; AppointmentViaRegionState.REGION_SELECTION;
final AppState appState;
AppointmentViaRegionViewmodel({required this.navigationService}); AppointmentViaRegionViewmodel({required this.navigationService,required this.appState});
void setSelectedRegionId(String? regionId) { void setSelectedRegionId(String? regionId) {
selectedRegionId = regionId; selectedRegionId = regionId;
@ -69,4 +71,6 @@ class AppointmentViaRegionViewmodel extends ChangeNotifier {
void setHospitalModel(PatientDoctorAppointmentList? hospital) { void setHospitalModel(PatientDoctorAppointmentList? hospital) {
selectedHospital = hospital; selectedHospital = hospital;
} }
bool get isArabic => appState.isArabic();
} }

@ -848,5 +848,6 @@ abstract class LocaleKeys {
static const pleaseEnterAValidEmailFormat = 'pleaseEnterAValidEmailFormat'; static const pleaseEnterAValidEmailFormat = 'pleaseEnterAValidEmailFormat';
static const selectCountry = 'selectCountry'; static const selectCountry = 'selectCountry';
static const forLoginVerification = 'forLoginVerification'; static const forLoginVerification = 'forLoginVerification';
static const searchHospital = 'searchHospital';
} }

@ -147,8 +147,8 @@ void main() async {
), ),
), ),
ChangeNotifierProvider<AppointmentViaRegionViewmodel>( ChangeNotifierProvider<AppointmentViaRegionViewmodel>(
create: (_) => create: (_) => AppointmentViaRegionViewmodel(
AppointmentViaRegionViewmodel(navigationService: getIt())) navigationService: getIt(), appState: getIt()))
], child: MyApp()), ], child: MyApp()),
), ),
); );

@ -38,7 +38,7 @@ class FacilityTypeSelectionWidget extends StatelessWidget {
), ),
), ),
Text( Text(
LocaleKeys.selectFacilitiesSubTitle, LocaleKeys.selectFacilitiesSubTitle.tr(),
style: TextStyle( style: TextStyle(
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
@ -55,9 +55,11 @@ class FacilityTypeSelectionWidget extends StatelessWidget {
}), }),
).onPress( ).onPress(
() { () {
if(bookAppointmentViewModel.hospitalList?.registeredDoctorMap?[selectedRegion]?.hmgSize != 0) {
regionalViewModel.setFacility(FacilitySelection.HMG.name); regionalViewModel.setFacility(FacilitySelection.HMG.name);
regionalViewModel.setBottomSheetState( regionalViewModel.setBottomSheetState(
AppointmentViaRegionState.HOSPITAL_SELECTION); AppointmentViaRegionState.HOSPITAL_SELECTION);
}
}, },
), ),
SizedBox(height: 16.h), SizedBox(height: 16.h),
@ -69,9 +71,11 @@ class FacilityTypeSelectionWidget extends StatelessWidget {
"${bookAppointmentViewModel.hospitalList?.registeredDoctorMap?[selectedRegion]?.hmcSize ?? 0}" "${bookAppointmentViewModel.hospitalList?.registeredDoctorMap?[selectedRegion]?.hmcSize ?? 0}"
})).onPress( })).onPress(
() { () {
if(bookAppointmentViewModel.hospitalList?.registeredDoctorMap?[selectedRegion]?.hmcSize!= 0 ) {
regionalViewModel.setFacility(FacilitySelection.HMC.name); regionalViewModel.setFacility(FacilitySelection.HMC.name);
regionalViewModel.setBottomSheetState( regionalViewModel.setBottomSheetState(
AppointmentViaRegionState.HOSPITAL_SELECTION); AppointmentViaRegionState.HOSPITAL_SELECTION);
}
}, },
), ),
], ],

@ -50,7 +50,7 @@ class HospitalBottomSheetBody extends StatelessWidget {
SizedBox(height: 16.h), SizedBox(height: 16.h),
TextInputWidget( TextInputWidget(
labelText: LocaleKeys.search.tr(), labelText: LocaleKeys.search.tr(),
hintText: "Search Hospital".tr(), hintText: LocaleKeys.searchHospital.tr(),
controller: searchText, controller: searchText,
onChange: (value) { onChange: (value) {
appointmentsViewModel.filterHospitalListByString(value, regionalViewModel.selectedRegionId , regionalViewModel.selectedFacilityType == appointmentsViewModel.filterHospitalListByString(value, regionalViewModel.selectedRegionId , regionalViewModel.selectedFacilityType ==

@ -77,7 +77,7 @@ class RegionListItem extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Align( Align(
alignment: Alignment.centerLeft, alignment: AlignmentDirectional.centerStart,
child: Text( child: Text(
title, title,
style: TextStyle( style: TextStyle(

@ -200,7 +200,11 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
void openRegionListBottomSheet(BuildContext context) { void openRegionListBottomSheet(BuildContext context) {
regionalViewModel.flush(); regionalViewModel.flush();
// AppointmentViaRegionViewmodel? viewmodel = null; // 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, __) { child: Consumer<AppointmentViaRegionViewmodel>(builder: (_, data, __) {
return getRegionalSelectionWidget(data); return getRegionalSelectionWidget(data);
}), callBackFunc: () {}); }), callBackFunc: () {});
@ -234,9 +238,19 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
if (data.selectedRegionId == null) { if (data.selectedRegionId == null) {
return LocaleKeys.selectRegion.tr().toText20(weight: FontWeight.w600); return LocaleKeys.selectRegion.tr().toText20(weight: FontWeight.w600);
} else { } else {
return Utils.buildSvgWithAssets(icon: AppAssets.arrow_back, iconColor: Color(0xff2B353E)).onPress(() { return
Transform.flip(
flipX: data.isArabic ? true : false,
child: Utils.buildSvgWithAssets(
icon: AppAssets.arrow_back,
iconColor: Color(0xff2B353E),
fit: BoxFit.contain,
),
).onPress(() {
data.handleBackPress(); data.handleBackPress();
}); });
} }
} }
} }

@ -0,0 +1,84 @@
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_export.dart';
import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/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/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/chip/custom_chip_widget.dart';
class LabOrderByTest extends StatelessWidget {
final VoidCallback onTap;
final int index;
final TestDetails? tests;
final bool isLoading;
final bool isExpanded;
const LabOrderByTest({super.key, required this.onTap, this.tests, required this.index, this.isLoading = false, this.isExpanded = false});
@override
build(BuildContext context) {
return AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
margin: EdgeInsets.symmetric(vertical: 8.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 20.h, hasShadow: true),
child: InkWell(
onTap: () {
if (!isLoading) {
onTap();
}
},
child: Container(
key: ValueKey<int>(index),
padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 8.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// ...labOrder!.testDetails!.map((detail) {
Padding(
padding: EdgeInsets.only(bottom: 8.h),
child: '${tests!.description}'.toText14(weight: FontWeight.w500),
),
SizedBox(height: 12.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
AppCustomChipWidget(
richText: '${"Last Tested:".needTranslation} ${ DateUtil.formatDateToDate(DateUtil.convertStringToDate(tests!.createdOn), false)}'.toText12(isBold: true),
// chipType: ChipTypeEnum.lightBg,
backgroundColor: AppColors.greyLightColor,
textColor: AppColors.textColor,
// borderRadius: 5,
),
CustomButton(
icon: AppAssets.view_report_icon,
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
text: LocaleKeys.viewReport.tr(context: context),
onPressed: () {},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14,
fontWeight: FontWeight.bold,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
),
],
),
],
),
)));
}
}

@ -11,6 +11,7 @@ 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/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/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart'; import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_order_by_test.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart';
import 'package:hmg_patient_app_new/presentation/lab/search_lab_report.dart'; import 'package:hmg_patient_app_new/presentation/lab/search_lab_report.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/theme/colors.dart';
@ -32,7 +33,7 @@ class _LabOrdersPageState extends State<LabOrdersPage> {
List<List<TestDetails>?> labSuggestions = []; List<List<TestDetails>?> labSuggestions = [];
int? expandedIndex; int? expandedIndex;
String? selectedFilterText = ''; String? selectedFilterText = '';
int activeIndex = 0;
@override @override
void initState() { void initState() {
scheduleMicrotask(() { scheduleMicrotask(() {
@ -84,7 +85,10 @@ class _LabOrdersPageState extends State<LabOrdersPage> {
// CustomTabBarModel(null, "Completed".needTranslation), // CustomTabBarModel(null, "Completed".needTranslation),
], ],
onTabChange: (index) { onTabChange: (index) {
// myAppointmentsViewModel.onTabChange(index); activeIndex = index;
setState(() {
});
}, },
), ),
SizedBox(height: 16.h), SizedBox(height: 16.h),
@ -95,7 +99,8 @@ class _LabOrdersPageState extends State<LabOrdersPage> {
isSelected: true, isSelected: true,
) )
: SizedBox(), : SizedBox(),
ListView.builder( activeIndex == 0
? ListView.builder(
shrinkWrap: true, shrinkWrap: true,
physics: NeverScrollableScrollPhysics(), physics: NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
@ -127,7 +132,40 @@ class _LabOrdersPageState extends State<LabOrdersPage> {
), ),
); );
}, },
)
: ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero,
itemCount: model.isLabOrdersLoading ? 5 :model.uniqueTests.toList().length,
itemBuilder: (context, index) {
final isExpanded = expandedIndex == index;
return model.isLabOrdersLoading
? LabResultItemView(
onTap: () {},
labOrder: null,
index: index,
isLoading: true,
) : AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: LabOrderByTest(
onTap: () {
setState(() {
expandedIndex = isExpanded ? null : index;
});
},
tests: model.uniqueTests.toList()[index],
index: index,
isExpanded: isExpanded)),
), ),
);
},
)
], ],
); );
}, },

@ -4,9 +4,11 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.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_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/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.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/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/request_utils.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.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/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
@ -15,6 +17,7 @@ import 'package:hmg_patient_app_new/features/book_appointments/book_appointments
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_model.dart';
import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart'; import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart'; import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/models/family_file_response_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/models/patient_sickleave_response_model.dart'; import 'package:hmg_patient_app_new/features/medical_file/models/patient_sickleave_response_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/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/my_appointments/my_appointments_view_model.dart';
@ -33,7 +36,10 @@ import 'package:hmg_patient_app_new/presentation/medical_file/vaccine_list_page.
import 'package:hmg_patient_app_new/presentation/medical_file/widgets/lab_rad_card.dart'; import 'package:hmg_patient_app_new/presentation/medical_file/widgets/lab_rad_card.dart';
import 'package:hmg_patient_app_new/presentation/medical_file/widgets/medical_file_card.dart'; import 'package:hmg_patient_app_new/presentation/medical_file/widgets/medical_file_card.dart';
import 'package:hmg_patient_app_new/presentation/medical_file/widgets/patient_sick_leave_card.dart'; import 'package:hmg_patient_app_new/presentation/medical_file/widgets/patient_sick_leave_card.dart';
import 'package:hmg_patient_app_new/presentation/my_family/my_Family.dart';
import 'package:hmg_patient_app_new/presentation/my_family/widget/my_family_sheet.dart';
import 'package:hmg_patient_app_new/presentation/prescriptions/prescriptions_list_page.dart'; import 'package:hmg_patient_app_new/presentation/prescriptions/prescriptions_list_page.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.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/chip/app_custom_chip_widget.dart';
@ -72,6 +78,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
insuranceViewModel.initInsuranceProvider(); insuranceViewModel.initInsuranceProvider();
medicalFileViewModel.setIsPatientSickLeaveListLoading(true); medicalFileViewModel.setIsPatientSickLeaveListLoading(true);
medicalFileViewModel.getPatientSickLeaveList(); medicalFileViewModel.getPatientSickLeaveList();
medicalFileViewModel.getFamilyFiles();
medicalFileViewModel.onTabChanged(0); medicalFileViewModel.onTabChanged(0);
} }
}); });
@ -85,6 +92,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
medicalFileViewModel = Provider.of<MedicalFileViewModel>(context, listen: false); medicalFileViewModel = Provider.of<MedicalFileViewModel>(context, listen: false);
bookAppointmentsViewModel = Provider.of<BookAppointmentsViewModel>(context, listen: false); bookAppointmentsViewModel = Provider.of<BookAppointmentsViewModel>(context, listen: false);
appState = getIt.get<AppState>(); appState = getIt.get<AppState>();
NavigationService navigationService = getIt.get<NavigationService>();
return Scaffold( return Scaffold(
backgroundColor: AppColors.bgScaffoldColor, backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView( body: CollapsingListView(
@ -111,10 +119,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
SizedBox(height: 16.h), SizedBox(height: 16.h),
Container( Container(
width: double.infinity, width: double.infinity,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24),
color: AppColors.whiteColor,
borderRadius: 24,
),
child: Padding( child: Padding(
padding: EdgeInsets.all(16.h), padding: EdgeInsets.all(16.h),
child: Column( child: Column(
@ -123,11 +128,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
Row( Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Image.asset( Image.asset(appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg, width: 56.h, height: 56.h),
appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg,
width: 56.h,
height: 56.h,
),
SizedBox(width: 8.h), SizedBox(width: 8.h),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -768,4 +769,9 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
return Container(); return Container();
} }
} }
getMember() {
// AuthanticationViewModel authanticationViewModel = getIt.get<AuthanticationViewModel>();
// RequestUtils.getAddFamilyRequest(nationalIDorFile: nationalIDorFile, mobileNo: mobileNo, countryCode: countryCode, loginType: loginType);
}
} }

@ -0,0 +1,197 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.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/dependencies.dart';
import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/core/utils/validation_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/authentication/authentication_view_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/models/family_file_response_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/my_family/widget/family_cards.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/appbar/app_bar_widget.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/custom_tab_bar.dart';
import 'package:hmg_patient_app_new/widgets/dropdown/country_dropdown_widget.dart';
import 'package:hmg_patient_app_new/widgets/input_widget.dart';
class FamilyMedicalScreen extends StatefulWidget {
final List<FamilyFileResponseModelLists> profiles;
final Function(FamilyFileResponseModelLists) onSelect;
const FamilyMedicalScreen({
Key? key,
required this.profiles,
required this.onSelect,
}) : super(key: key);
@override
State<FamilyMedicalScreen> createState() => _FamilyMedicalScreenState();
}
class _FamilyMedicalScreenState extends State<FamilyMedicalScreen> {
List<CustomTabBarModel> tabs = [CustomTabBarModel("", LocaleKeys.medicalFile.tr()), CustomTabBarModel("", LocaleKeys.request.tr())];
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.scaffoldBgColor,
appBar: CustomAppBar(
onBackPressed: () {
Navigator.of(context).pop();
},
onLanguageChanged: (lang) {},
hideLogoAndLang: true,
),
body: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.myMedicalFile.tr().toText26(color: AppColors.textColor, weight: FontWeight.w600, letterSpacing: -2),
SizedBox(height: 25.h),
CustomTabBar(
tabs: tabs,
onTabChange: (int index) {},
),
SizedBox(height: 25.h),
FamilyCards(profiles: widget.profiles, onSelect: widget.onSelect, isShowDetails: true),
SizedBox(height: 20.h),
],
),
).paddingSymmetrical(20, 0),
bottomSheet: Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
customBorder: BorderRadius.only(topLeft: Radius.circular(24), topRight: Radius.circular(24)),
),
padding: EdgeInsets.symmetric(vertical: 10.h, horizontal: 20.h),
child: CustomButton(
text: "Add a new family member",
onPressed: () {
showModelSheet();
},
icon: AppAssets.add_icon,
height: 56.h,
fontWeight: FontWeight.w600,
)),
);
}
void showModelSheet() {
AuthenticationViewModel authVm = getIt.get<AuthenticationViewModel>();
return showCommonBottomSheetWithoutHeight(context,
title: "Add Family Member",
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
"Please fill the below field to add a new family member to your profile".toText16(color: AppColors.textColor, weight: FontWeight.w500),
SizedBox(height: 20.h),
Container(
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(24)),
padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 8.h),
child: Column(
children: [
CustomCountryDropdown(
countryList: CountryEnum.values,
onCountryChange: authVm.onCountryChange,
).paddingOnly(top: 8.h, bottom: 16.h),
Divider(
height: 1.h,
color: AppColors.spacerLineColor,
),
TextInputWidget(
labelText: LocaleKeys.nationalIdNumber.tr(),
hintText: "xxxxxxxxx",
controller: authVm.nationalIdController,
// focusNode: _nationalIdFocusNode,
isEnable: true,
prefix: null,
isAllowRadius: true,
isBorderAllowed: false,
isAllowLeadingIcon: true,
autoFocus: true,
keyboardType: TextInputType.number,
padding: EdgeInsets.symmetric(vertical: 8.h),
leadingIcon: AppAssets.student_card,
).paddingOnly(top: 8.h, bottom: 8.h),
Divider(
height: 1.h,
color: AppColors.spacerLineColor,
),
TextInputWidget(
labelText: LocaleKeys.phoneNumber.tr(),
hintText: "574345434",
controller: authVm.phoneNumberController,
isEnable: true,
prefix: authVm.selectedCountrySignup.countryCode,
isAllowRadius: true,
isBorderAllowed: false,
isAllowLeadingIcon: true,
autoFocus: true,
keyboardType: TextInputType.number,
padding: EdgeInsets.symmetric(vertical: 8.h),
leadingIcon: AppAssets.smart_phone,
).paddingOnly(top: 8.h, bottom: 4),
//TextInputWidget(
// labelText: widget.isForEmail ? LocaleKeys.email.tr() : LocaleKeys.phoneNumber.tr(),
// hintText: widget.isForEmail ? "demo@gmail.com" : "5xxxxxxxx",
// controller: widget.textController!,
// focusNode: _textFieldFocusNode,
// autoFocus: widget.autoFocus,
// padding: EdgeInsets.all(8.h),
// keyboardType: widget.isForEmail ? TextInputType.emailAddress : TextInputType.number,
// onChange: (value) {
// if (widget.onChange != null) {
// widget.onChange!(value);
// }
// },
// onCountryChange: (value) {
// if (widget.onCountryChange != null) {
// widget.onCountryChange!(value);
// }
// },
// isEnable: true,
// isReadOnly: widget.isFromSavedLogin,
// prefix: widget.isForEmail ? null : widget.countryCode,
// isBorderAllowed: false,
// isAllowLeadingIcon: true,
// fontSize: 13.h,
// isCountryDropDown: widget.isEnableCountryDropdown,
// leadingIcon: widget.isForEmail ? AppAssets.email : AppAssets.smart_phone,
// )
],
),
),
SizedBox(height: 20.h),
CustomButton(
text: "Verify the member",
onPressed: () {
FocusScope.of(context).unfocus();
if (ValidationUtils.isValidatedIdAndPhoneWithCountryValidation(
nationalId: authVm.nationalIdController.text,
selectedCountry: authVm.selectedCountrySignup,
phoneNumber: authVm.phoneNumberController.text,
onOkPress: () {
Navigator.of(context).pop();
},
)) {}
},
icon: AppAssets.add_icon,
height: 56.h,
fontWeight: FontWeight.w600),
SizedBox(height: 20.h),
],
),
callBackFunc: () {});
}
}

@ -0,0 +1,116 @@
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_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/enums.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/medical_file/models/family_file_response_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/custom_chip_widget.dart';
class FamilyCards extends StatefulWidget {
final List<FamilyFileResponseModelLists> profiles;
final Function(FamilyFileResponseModelLists) onSelect;
final bool isShowDetails;
final bool isBottomSheet;
const FamilyCards({super.key, required this.profiles, required this.onSelect, this.isShowDetails = false, this.isBottomSheet = false});
@override
State<FamilyCards> createState() => _FamilyCardsState();
}
class _FamilyCardsState extends State<FamilyCards> {
AppState appState = getIt<AppState>();
@override
Widget build(BuildContext context) {
return GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: widget.profiles.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 10.h,
mainAxisSpacing: 10.h,
childAspectRatio: widget.isShowDetails ? 0.56.h : 0.74.h,
),
itemBuilder: (context, index) {
final profile = widget.profiles[index];
final isActive = (profile.responseId == appState
.getAuthenticatedUser()
?.patientId);
return Container(
padding: EdgeInsets.symmetric(vertical: 15.h, horizontal: 15.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24),
child: Opacity(
opacity: isActive ? 0.4 : 1.0, // Fade all content if active
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: 5.h),
Utils.buildImgWithAssets(
icon: profile.gender == 1 ? ((profile.age ?? 0) < 7 ? AppAssets.babyBoyImg : AppAssets.male_img) : (profile.age! < 7 ? AppAssets.babyGirlImg : AppAssets.femaleImg),
width: 80.h,
height: 78.h),
SizedBox(height: 8.h),
(profile.patientName ?? "Unknown").toText16(isBold: false, isCenter: true, maxlines: 1, weight: FontWeight.w600),
SizedBox(height: 4.h),
CustomChipWidget(
chipType: ChipTypeEnum.alert,
backgroundColor: AppColors.lightGrayBGColor,
chipText: "Relation: ${profile.relationship ?? "N/A"}",
iconAsset: AppAssets.heart,
isShowBorder: false,
borderRadius: 8.h,
textColor: AppColors.textColor),
widget.isShowDetails ? SizedBox(height: 4.h) : SizedBox(),
widget.isShowDetails
? CustomChipWidget(
chipType: ChipTypeEnum.alert,
backgroundColor: AppColors.lightGrayBGColor,
chipText: "Age: ${profile.age ?? "N/A"} Years",
isShowBorder: false,
borderRadius: 8.h,
textColor: AppColors.textColor,
)
: SizedBox(),
widget.isShowDetails ? SizedBox(height: 8.h) : SizedBox(),
Spacer(),
if (isActive)
CustomButton(
height: 40.h,
onPressed: () {},
text: LocaleKeys.active.tr(),
backgroundColor: Colors.grey.shade200,
borderColor: Colors.grey.shade200,
textColor: AppColors.greyTextColor,
fontSize: 13.h,
).paddingOnly(top: 0, bottom: 0)
else
CustomButton(
height: 40.h,
onPressed: () => widget.onSelect(profile),
text: LocaleKeys.select.tr(),
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 13.h,
icon: widget.isBottomSheet ? null : AppAssets.heart,
iconColor: AppColors.primaryRedColor,
padding: EdgeInsets.symmetric(vertical: 0, horizontal: 0),
).paddingOnly(top: 0, bottom: 0),
],
),
),
);
},
);
}
}

@ -0,0 +1,31 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/features/medical_file/models/family_file_response_model.dart';
import 'package:hmg_patient_app_new/presentation/my_family/my_Family.dart';
import 'package:hmg_patient_app_new/presentation/my_family/widget/family_cards.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
class MyFamilySheet {
static void show(BuildContext context, List<FamilyFileResponseModelLists> familyLists, Function(FamilyFileResponseModelLists) onSelect) {
return showCommonBottomSheetWithoutHeight(
context,
titleWidget: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
'Please select a profile'.toText21(isBold: true),
'switch from the below list of medical file'.toText16(weight: FontWeight.w100, color: AppColors.greyTextColor),
],
),
child: FamilyCards(
profiles: familyLists,
onSelect: (profile) {
Navigator.of(context).pop(); // Close the bottom sheet
onSelect(profile); // Call the onSelect callback
},
isBottomSheet: true),
callBackFunc: () {},
);
}
}

@ -36,13 +36,15 @@ class AppColors {
static const Color warningColorYellow = Color(0xFFF4A308); static const Color warningColorYellow = Color(0xFFF4A308);
static const Color blackBgColor = Color(0xFF2E3039); static const Color blackBgColor = Color(0xFF2E3039);
static const blackColor = textColor; static const blackColor = textColor;
static const inputLabelTextColor = Color(0xff898A8D); static const Color inputLabelTextColor = Color(0xff898A8D);
static const greyTextColor = Color(0xFF8F9AA3); static const Color greyTextColor = Color(0xFF8F9AA3);
static const Color lightGrayBGColor = Color(0x142E3039);
static const lightGreenColor = Color(0xFF0ccedde); static const lightGreenColor = Color(0xFF0ccedde);
static const textGreenColor = Color(0xFF18C273); static const textGreenColor = Color(0xFF18C273);
static const Color ratingColorYellow = Color(0xFFFFAF15); static const Color ratingColorYellow = Color(0xFFFFAF15);
static const Color spacerLineColor = Color(0x2E30391A);
//Chips //Chips
static const Color successColor = Color(0xff18C273); static const Color successColor = Color(0xff18C273);
@ -51,6 +53,8 @@ class AppColors {
static const Color infoColor = Color(0xFF0B85F7); static const Color infoColor = Color(0xFF0B85F7);
static const Color warningColor = Color(0xFFFFCC00); static const Color warningColor = Color(0xFFFFCC00);
static const Color greyColor = Color(0xFFEFEFF0); static const Color greyColor = Color(0xFFEFEFF0);
static const Color chipPrimaryRedBorderColor = Color(0xFFED1C2B);
static const Color chipSecondaryLightRedColor = Color(0xFFFEE9EA);
static const Color successLightColor = Color(0xFF18C273); static const Color successLightColor = Color(0xFF18C273);
static const Color errorLightColor = Color(0xFFED1C2B); static const Color errorLightColor = Color(0xFFED1C2B);
@ -62,4 +66,7 @@ static const Color greyLightColor = Color(0xFFEFEFF0);
static const Color bottomNAVBorder = Color(0xFFEEEEEE); static const Color bottomNAVBorder = Color(0xFFEEEEEE);
static const Color quickLoginColor = Color(0xFF666666); static const Color quickLoginColor = Color(0xFF666666);
static const Color tooltipTextColor = Color(0xFF414D55);
static const Color graphGridColor = Color(0x4D18C273);
} }

@ -72,9 +72,9 @@ class AppCustomChipWidget extends StatelessWidget {
// padding: EdgeInsets.all(0.0), // padding: EdgeInsets.all(0.0),
padding: padding, padding: padding,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
labelPadding: EdgeInsets.only( labelPadding: EdgeInsetsDirectional.only(
left: -4.h, start: -4.h,
right: deleteIcon?.isNotEmpty == true ? 2.h : 8.h), end: deleteIcon?.isNotEmpty == true ? 2.h : 8.h),
backgroundColor: backgroundColor, backgroundColor: backgroundColor,
shape: shape, shape: shape,
deleteIcon: deleteIcon?.isNotEmpty == true deleteIcon: deleteIcon?.isNotEmpty == true
@ -96,9 +96,9 @@ class AppCustomChipWidget extends StatelessWidget {
padding: EdgeInsets.all(0.0), padding: EdgeInsets.all(0.0),
backgroundColor: backgroundColor, backgroundColor: backgroundColor,
shape: shape, shape: shape,
labelPadding: EdgeInsets.only( labelPadding: EdgeInsetsDirectional.only(
left: 8.h, start: 8.h,
right: deleteIcon?.isNotEmpty == true ? -2.h : 8.h), end: deleteIcon?.isNotEmpty == true ? -2.h : 8.h),
deleteIcon: deleteIcon?.isNotEmpty == true deleteIcon: deleteIcon?.isNotEmpty == true
? Utils.buildSvgWithAssets( ? Utils.buildSvgWithAssets(
icon: deleteIcon!, icon: deleteIcon!,

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/enums.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/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
@ -12,6 +13,10 @@ class CustomChipWidget extends StatelessWidget {
final bool isSelected; final bool isSelected;
final double borderRadius; final double borderRadius;
final EdgeInsetsGeometry padding; final EdgeInsetsGeometry padding;
final Color? backgroundColor;
final Color? textColor;
final Color? borderColor;
final bool isShowBorder;
const CustomChipWidget({ const CustomChipWidget({
super.key, super.key,
@ -22,6 +27,10 @@ class CustomChipWidget extends StatelessWidget {
this.isSelected = false, this.isSelected = false,
this.borderRadius = 12, this.borderRadius = 12,
this.padding = const EdgeInsets.all(8), this.padding = const EdgeInsets.all(8),
this.backgroundColor,
this.textColor,
this.borderColor,
this.isShowBorder = false,
}); });
@override @override
@ -32,11 +41,13 @@ class CustomChipWidget extends StatelessWidget {
return Container( return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(borderRadius), borderRadius: BorderRadius.circular(borderRadius),
color: isSelected ? chipType.color : chipType.backgroundColor, color: isSelected ? chipType.color : backgroundColor ?? chipType.backgroundColor,
border: Border.all( border: isShowBorder
? Border.all(
color: chipType.color, color: chipType.color,
width: isSelected ? 0 : 1, width: isSelected ? 0 : 1,
), )
: null,
), ),
child: InkWell( child: InkWell(
onTap: hasOnTap ? onTap : null, onTap: hasOnTap ? onTap : null,
@ -51,18 +62,9 @@ class CustomChipWidget extends StatelessWidget {
children: [ children: [
if (iconAsset != null) ...[ if (iconAsset != null) ...[
Utils.buildSvgWithAssets(icon: iconAsset!), Utils.buildSvgWithAssets(icon: iconAsset!),
const SizedBox(width: 6), SizedBox(width: 4.h),
], ],
Text( chipText.toText10(isBold: true, color: isSelected ? Colors.white : textColor ?? chipType.color, maxlines: 1, weight: FontWeight.w500, letterSpacing: -0.5),
chipText.toUpperCase(),
style: context.dynamicTextStyle(
fontWeight: FontWeight.w500,
fontSize: 14,
color: isSelected ? Colors.white : chipType.color,
letterSpacing: 0.1,
isLanguageSwitcher: true,
),
),
], ],
), ),
), ),

@ -0,0 +1,287 @@
import 'package:flutter/material.dart';
import 'package:fl_chart/fl_chart.dart';
import 'package:hmg_patient_app_new/core/common_models/data_points.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
///
/// CustomGraph(dataPoints: sampleData, scrollDirection: Axis.horizontal,height: 200,maxY: 100, maxX:2.5,
/// leftLabelFormatter: (value){
/// Widget buildLabel(String label) {
/// return Padding(
/// padding: const EdgeInsets.only(right: 8),
/// child: Text(
/// label,
/// style: TextStyle(
/// fontSize: 8.fSize, color: AppColors.textColor,
/// fontFamily:
/// FontUtils.getFontFamilyForLanguage(false)
/// ),
/// textAlign: TextAlign.right,
/// ),
/// );
/// }
/// switch (value.toInt()) {
///
/// case 20:
/// return buildLabel("Critical Low");
/// case 40:
/// return buildLabel("Low");
/// case 60:
/// return buildLabel("Normal");
/// case 80:
/// return buildLabel("High");
/// case 100:
/// return buildLabel("Critical High");
/// }
/// return const SizedBox.shrink();
/// },
///
/// ),
class CustomGraph extends StatelessWidget {
final List<DataPoint> dataPoints;
final double? width;
final double height;
final double? maxY;
final double? maxX;
final Color spotColor;
final Color graphColor;
final Color graphShadowColor;
final Color graphGridColor;
final Color bottomLabelColor;
final double? bottomLabelSize;
final FontWeight? bottomLabelFontWeight;
///creates the left label and provide it to the chart as it will be used by other part of the application so the label will be different for every chart
final Widget Function(double value) leftLabelFormatter;
final Axis scrollDirection;
final bool showBottomTitleDates;
final bool isFullScreeGraph;
const CustomGraph({
super.key,
required this.dataPoints,
required this.leftLabelFormatter,
this.width,
required this.scrollDirection,
required this.height,
this.maxY,
this.maxX,
this.showBottomTitleDates = true,
this.isFullScreeGraph = false,
this.spotColor = AppColors.bgGreenColor,
this.graphColor = AppColors.bgGreenColor,
this.graphShadowColor = AppColors.graphGridColor,
this.graphGridColor = AppColors.graphGridColor,
this.bottomLabelColor = AppColors.textColor,
this.bottomLabelFontWeight = FontWeight.w500,
this.bottomLabelSize,
});
@override
Widget build(BuildContext context) {
// var maxY = 0.0;
double interval = 20;
if ((maxY ?? 0) > 10 && (maxY ?? 0) <= 20) {
interval = 2;
} else if ((maxY ?? 0) > 5 && (maxY ?? 0) <= 10) {
interval = 1;
} else if ((maxY ?? 0) >= 0 && (maxY ?? 0) <= 5) {
interval = .4;
}
return Material(
color: Colors.white,
child: SizedBox(
width: width,
height: height,
child: Padding(
padding: const EdgeInsets.only(top: 8.0, bottom: 8),
child: LineChart(
LineChartData(
minY: 0,
maxY:
((maxY?.ceilToDouble() ?? 0.0) + interval).floorToDouble(),
// minX: dataPoints.first.labelValue - 1,
maxX: maxX,
minX: -0.2,
lineTouchData: LineTouchData(
getTouchLineEnd: (_, __) => 0,
getTouchedSpotIndicator: (barData, indicators) {
// Only show custom marker for touched spot
return indicators.map((int index) {
return TouchedSpotIndicatorData(
FlLine(color: Colors.transparent),
FlDotData(
show: true,
getDotPainter: (spot, percent, barData, idx) {
return FlDotCirclePainter(
radius: 8,
color: spotColor,
strokeWidth: 2,
strokeColor: Colors.white,
);
},
),
);
}).toList();
},
enabled: true,
touchTooltipData: LineTouchTooltipData(
getTooltipColor: (_) => Colors.white,
getTooltipItems: (touchedSpots) {
if (touchedSpots.isEmpty) return [];
// Only show tooltip for the first touched spot, hide others
return touchedSpots.map((spot) {
if (spot == touchedSpots.first) {
final dataPoint = dataPoints[spot.x.toInt()];
return LineTooltipItem(
// '${dataPoint.label} ${spot.y.toStringAsFixed(2)}',
'${dataPoint.value} ',
TextStyle(
color: Colors.black,
fontSize: 12.fSize,
fontWeight: FontWeight.w500),
);
}
return null; // hides the rest
}).toList();
},
),
),
titlesData: FlTitlesData(
leftTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
reservedSize: 77,
interval: .1, // Let fl_chart handle it
getTitlesWidget: (value, _) {
return leftLabelFormatter(value);
},
),
),
bottomTitles: AxisTitles(
axisNameSize: 60,
sideTitles: SideTitles(
showTitles: showBottomTitleDates,
reservedSize: 50,
getTitlesWidget: (value, _) {
if ((value.toDouble() >= 0) &&
(value.toDouble() < (maxX ?? dataPoints.length))) {
var label = dataPoints[value.toInt()].label;
return buildBottomLabel(label);
}
return const SizedBox.shrink();
},
interval: 1, // ensures 1:1 mapping with spots
),
),
topTitles: AxisTitles(),
rightTitles: AxisTitles(),
),
borderData: FlBorderData(
show: true,
border: const Border(
bottom: BorderSide.none,
left: BorderSide(color: Colors.grey, width: .5),
right: BorderSide.none,
top: BorderSide.none,
),
),
lineBarsData: _buildColoredLineSegments(dataPoints),
gridData: FlGridData(
show: true,
drawVerticalLine: false,
horizontalInterval: 20,
checkToShowHorizontalLine: (value) =>
value >= 0 && value <= 100,
getDrawingHorizontalLine: (value) {
return FlLine(
color: AppColors.graphGridColor,
strokeWidth: 1,
dashArray: [5, 5],
);
},
),
),
),
),
));
}
List<LineChartBarData> _buildColoredLineSegments(List<DataPoint> dataPoints) {
final List<FlSpot> allSpots = dataPoints.asMap().entries.map((entry) {
return FlSpot(entry.key.toDouble(), entry.value.value);
}).toList();
var data = [
LineChartBarData(
spots: allSpots,
isCurved: true,
isStrokeCapRound: true,
isStrokeJoinRound: true,
barWidth: 4,
gradient: LinearGradient(
colors: [graphColor, graphColor],
begin: Alignment.centerLeft,
end: Alignment.centerRight,
),
dotData: FlDotData(
show: false,
),
belowBarData: BarAreaData(
show: true,
gradient: LinearGradient(
colors: [
graphShadowColor,
Colors.transparent,
],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
),
)
];
return data;
}
// Widget buildLabel(String label) {
// return Padding(
// padding: const EdgeInsets.only(right: 8),
// child: Text(
// label,
// style: TextStyle(
// fontSize: leftLabelSize ?? 8.fSize, color: leftLabelColor),
// textAlign: TextAlign.right,
// ),
// );
// }
Widget buildBottomLabel(String label) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
label,
style: TextStyle(
fontSize: bottomLabelSize ?? 8.fSize, color: bottomLabelColor),
),
);
}
}
final List<DataPoint> sampleData = [
DataPoint(
value: 20,
label: 'Jan 2024',
),
DataPoint(
value: 36,
label: 'Feb 2024',
),
DataPoint(
value: 80,
label: 'This result',
),
];

@ -1,44 +0,0 @@
import 'package:flutter/material.dart';
class ProfileSelector extends StatelessWidget {
final List<Map<String, dynamic>> profiles;
final Function(Map<String, dynamic>) onSelect;
const ProfileSelector({
Key? key,
required this.profiles,
required this.onSelect,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: profiles.map((profile) {
return ListTile(
leading: CircleAvatar(
radius: 22,
backgroundImage: profile["GenderImage"] != null &&
profile["GenderImage"].toString().isNotEmpty
? NetworkImage(profile["GenderImage"])
: AssetImage(
profile["Gender"] == 1
? "assets/images/male.png"
: "assets/images/female.png")
as ImageProvider,
),
title: Text(
profile["PatientName"] ?? "Unknown",
style: const TextStyle(fontWeight: FontWeight.w600),
),
subtitle: Text(
profile["Relationship"] ?? "Self",
style: const TextStyle(color: Colors.grey),
),
trailing: const Icon(Icons.arrow_forward_ios, size: 16),
onTap: () => onSelect(profile),
);
}).toList(),
);
}
}

@ -1,15 +0,0 @@
import 'package:flutter/material.dart';
import '../common_bottom_sheet.dart';
import 'my_Family.dart';
class MyFamilySheet {
static void show(BuildContext context, List<Map<String, dynamic>> profiles, Function(Map<String, dynamic>) onSelect) {
showCommonBottomSheetWithoutHeight(
context,
title: 'Select Profile',
child: ProfileSelector(profiles: profiles, onSelect: (profile) {
Navigator.of(context).pop(); // Close the bottom sheet
onSelect(profile); // Call the onSelect callback
}), callBackFunc: () {},
);
}
}

@ -55,7 +55,7 @@ dependencies:
uuid: ^4.5.1 uuid: ^4.5.1
health: ^13.1.3 health: ^13.1.3
# health: 12.0.1 # health: 12.0.1
fl_chart: ^1.0.0 fl_chart: ^1.1.1
geolocator: ^14.0.2 geolocator: ^14.0.2
dropdown_search: ^6.0.2 dropdown_search: ^6.0.2
google_maps_flutter: ^2.12.3 google_maps_flutter: ^2.12.3

Loading…
Cancel
Save